From e003ffa38a7fe9a789d3c91db6fc83a42d6c8223 Mon Sep 17 00:00:00 2001 From: ShadowVirtual <61156296+DeathbotGaming@users.noreply.github.com> Date: Mon, 1 Apr 2024 03:51:52 -0800 Subject: [PATCH] Page Plus Changed the text editor --- Config/config.php | 24 +++ ..._03_26_063207_create_page_pluses_table.php | 25 +++ ...00_create_page_plus_translations_table.php | 25 +++ ...26_063441_create_page_plus_metas_table.php | 24 +++ Entities/PageHelper.php | 54 +++++++ Entities/PagePlus.php | 146 ++++++++++++++++++ Entities/PagePlusMeta.php | 29 ++++ Entities/PagePlusTranslation.php | 29 ++++ Http/Controllers/AdminPagePlusController.php | 98 ++++++++++++ Http/Controllers/PagePlusController.php | 16 ++ Providers/PagePlusServiceProvider.php | 77 +++++++++ Providers/RouteServiceProvider.php | 32 ++++ Resources/lang/en/messages.php | 39 +++++ Resources/lang/uk/messages.php | 39 +++++ .../views/admin/default/create.blade.php | 123 +++++++++++++++ .../views/admin/default/editor.blade.php | 17 ++ Resources/views/admin/default/index.blade.php | 43 ++++++ .../admin/default/page_children.blade.php | 48 ++++++ .../views/admin/default/translate.blade.php | 45 ++++++ .../client/tailwind/elements/apps.blade.php | 13 ++ .../components/children-dropdown.blade.php | 43 ++++++ .../nav-children-dropdown.blade.php | 44 ++++++ .../tailwind/elements/footer-help.blade.php | 33 ++++ .../tailwind/elements/footer-legal.blade.php | 33 ++++ .../elements/footer-resources.blade.php | 33 ++++ .../tailwind/elements/main-menu.blade.php | 33 ++++ .../elements/navbar-dropdown-left.blade.php | 35 +++++ .../elements/navbar-dropdown-right.blade.php | 28 ++++ .../tailwind/elements/user-dropdown.blade.php | 39 +++++ .../views/client/tailwind/index.blade.php | 49 ++++++ Routes/admin.php | 17 ++ Routes/web.php | 8 + Rules/UniqueSlug.php | 47 ++++++ composer.json | 23 +++ helper.php | 18 +++ module.json | 11 ++ package.json | 3 + 37 files changed, 1443 insertions(+) create mode 100644 Config/config.php create mode 100644 Database/Migrations/2024_03_26_063207_create_page_pluses_table.php create mode 100644 Database/Migrations/2024_03_26_063400_create_page_plus_translations_table.php create mode 100644 Database/Migrations/2024_03_26_063441_create_page_plus_metas_table.php create mode 100644 Entities/PageHelper.php create mode 100644 Entities/PagePlus.php create mode 100644 Entities/PagePlusMeta.php create mode 100644 Entities/PagePlusTranslation.php create mode 100644 Http/Controllers/AdminPagePlusController.php create mode 100644 Http/Controllers/PagePlusController.php create mode 100644 Providers/PagePlusServiceProvider.php create mode 100644 Providers/RouteServiceProvider.php create mode 100644 Resources/lang/en/messages.php create mode 100644 Resources/lang/uk/messages.php create mode 100644 Resources/views/admin/default/create.blade.php create mode 100644 Resources/views/admin/default/editor.blade.php create mode 100644 Resources/views/admin/default/index.blade.php create mode 100644 Resources/views/admin/default/page_children.blade.php create mode 100644 Resources/views/admin/default/translate.blade.php create mode 100644 Resources/views/client/tailwind/elements/apps.blade.php create mode 100644 Resources/views/client/tailwind/elements/components/children-dropdown.blade.php create mode 100644 Resources/views/client/tailwind/elements/components/nav-children-dropdown.blade.php create mode 100644 Resources/views/client/tailwind/elements/footer-help.blade.php create mode 100644 Resources/views/client/tailwind/elements/footer-legal.blade.php create mode 100644 Resources/views/client/tailwind/elements/footer-resources.blade.php create mode 100644 Resources/views/client/tailwind/elements/main-menu.blade.php create mode 100644 Resources/views/client/tailwind/elements/navbar-dropdown-left.blade.php create mode 100644 Resources/views/client/tailwind/elements/navbar-dropdown-right.blade.php create mode 100644 Resources/views/client/tailwind/elements/user-dropdown.blade.php create mode 100644 Resources/views/client/tailwind/index.blade.php create mode 100644 Routes/admin.php create mode 100644 Routes/web.php create mode 100644 Rules/UniqueSlug.php create mode 100644 composer.json create mode 100644 helper.php create mode 100644 module.json create mode 100644 package.json diff --git a/Config/config.php b/Config/config.php new file mode 100644 index 0000000..3025bc0 --- /dev/null +++ b/Config/config.php @@ -0,0 +1,24 @@ + 'PagePlus', + 'icon' => 'https://imgur.png', + 'author' => 'GIGABAIT', + 'version' => '1.0.0', + 'wemx_version' => '>=2.0.0', + + 'elements' => [ + 'admin_menu' => + [ + [ + 'name' => 'PagePlus', + 'icon' => '', + 'href' => '/admin/pageplus', + 'style' => '', + ], + ], + + ], + +]; diff --git a/Database/Migrations/2024_03_26_063207_create_page_pluses_table.php b/Database/Migrations/2024_03_26_063207_create_page_pluses_table.php new file mode 100644 index 0000000..43aa53b --- /dev/null +++ b/Database/Migrations/2024_03_26_063207_create_page_pluses_table.php @@ -0,0 +1,25 @@ +id(); + $table->unsignedBigInteger('parent_id')->nullable(); + $table->string('slug')->unique(); + $table->integer('order')->default(0); + $table->timestamps(); + $table->foreign('parent_id')->references('id')->on('page_pluses')->onDelete('cascade'); + }); + } + + public function down(): void + { + Schema::dropIfExists('page_pluses'); + } +}; diff --git a/Database/Migrations/2024_03_26_063400_create_page_plus_translations_table.php b/Database/Migrations/2024_03_26_063400_create_page_plus_translations_table.php new file mode 100644 index 0000000..b943d70 --- /dev/null +++ b/Database/Migrations/2024_03_26_063400_create_page_plus_translations_table.php @@ -0,0 +1,25 @@ +id(); + $table->foreignId('page_id')->constrained('page_pluses')->onDelete('cascade'); + $table->string('locale', 10)->default('en'); + $table->string('title'); + $table->longText('content'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('page_plus_translations'); + } +}; diff --git a/Database/Migrations/2024_03_26_063441_create_page_plus_metas_table.php b/Database/Migrations/2024_03_26_063441_create_page_plus_metas_table.php new file mode 100644 index 0000000..5c99978 --- /dev/null +++ b/Database/Migrations/2024_03_26_063441_create_page_plus_metas_table.php @@ -0,0 +1,24 @@ +id(); + $table->foreignId('page_id')->constrained('page_pluses')->onDelete('cascade'); + $table->string('key'); + $table->text('value'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('page_plus_metas'); + } +}; diff --git a/Entities/PageHelper.php b/Entities/PageHelper.php new file mode 100644 index 0000000..47d4888 --- /dev/null +++ b/Entities/PageHelper.php @@ -0,0 +1,54 @@ +get() : $page->children; + foreach ($pages as $page) { + $path = $pathPrefix . '/' . $page->slug; + Route::get($path, [PagePlusController::class, 'show'])->name(str_replace('/', '.', $page->slug))->defaults('slug', $page->slug); + if ($page->children()->count() > 0) { + self::registerRoutes($page, $path); + } + } + } catch (\Exception $e) { + ErrorLog('pageplus', $e->getMessage()); + } + + } + + public static function cacheKey($key, $id): string + { + return sprintf('pagePlus_%s_%s', $key, $id); + } + + public static function clearAllCache(): void + { + // Clear cache for PagePlus + $pagePlusIds = PagePlus::pluck('id'); + foreach ($pagePlusIds as $id) { + Cache::forget(self::cacheKey('fullSlug', $id)); + Cache::forget(self::cacheKey('childrenIds', $id)); + } + + // Clear cache for PagePlusMeta + $pagePlusMeta = PagePlusMeta::all(); + foreach ($pagePlusMeta as $meta) { + Cache::forget(self::cacheKey('meta', $meta->page_id) . '_' . $meta->key); + } + + // Clear cache for PagePlusTranslation + $pagePlusTranslations = PagePlusTranslation::all(); + foreach ($pagePlusTranslations as $translation) { + Cache::forget(self::cacheKey('translation', $translation->page_id) . '_' . $translation->locale); + } + } +} diff --git a/Entities/PagePlus.php b/Entities/PagePlus.php new file mode 100644 index 0000000..92c0c41 --- /dev/null +++ b/Entities/PagePlus.php @@ -0,0 +1,146 @@ +orderBy('order'); + }); + + $clearCache = function ($model) { + Cache::forget(PageHelper::cacheKey('fullSlug', $model->id)); + Cache::forget(PageHelper::cacheKey('childrenIds', $model->id)); + Cache::forget(PageHelper::cacheKey('bySlug', $model->slug)); + Cache::forget(PageHelper::cacheKey('children', $model->parent_id)); + Cache::forget(PageHelper::cacheKey('topmostAncestor', $model->id)); + if ($parent = $model->parent()->first()) { + Cache::forget(PageHelper::cacheKey('fullSlug', $parent->id)); + Cache::forget(PageHelper::cacheKey('childrenIds', $parent->id)); + Cache::forget(PageHelper::cacheKey('children', $parent->id)); + Cache::forget(PageHelper::cacheKey('topmostAncestor', $parent->id)); + } + $allLocations = PagePlusMeta::where('key', 'location')->pluck('value')->unique(); + foreach ($allLocations as $location) { + Cache::forget(PageHelper::cacheKey('byLocation', $location)); + } + }; + + static::saved($clearCache); + static::deleted($clearCache); + } + + public function metas(): HasMany + { + return $this->hasMany(PagePlusMeta::class, 'page_id'); + } + + public function translations(): HasMany + { + return $this->hasMany(PagePlusTranslation::class, 'page_id'); + } + + public function parent(): BelongsTo + { + return $this->belongsTo(PagePlus::class, 'parent_id'); + } + + public function children(): HasMany + { + return $this->hasMany(PagePlus::class, 'parent_id'); + } + + public function childrenCached() + { + return Cache::rememberForever(PageHelper::cacheKey('children', $this->id), function () { + return $this->children()->orderBy('order')->get(); + }); + } + + public function getFullSlug(): string + { + return Cache::rememberForever(PageHelper::cacheKey('fullSlug', $this->id), function () { + $slugs = []; + $page = $this; + while ($page) { + array_unshift($slugs, $page->slug); + $page = $page->parent; + } + return implode('/', $slugs); + }); + } + + public function getMeta($key, $default = null): ?string + { + $cacheKey = PageHelper::cacheKey('meta', $this->id) . '_' . $key; + return Cache::rememberForever($cacheKey, function () use ($key, $default) { + $meta = $this->metas()->where('key', $key)->first(); + return $meta ? $meta->value : $default; + }); + } + + public function getTranslation($locale = null) + { + $locale = $locale ?? (auth()->check() ? auth()->user()->language : app()->getLocale()); + $cacheKey = PageHelper::cacheKey('translation', $this->id) . '_' . $locale; + return Cache::rememberForever($cacheKey, function () use ($locale) { + $translation = $this->translations()->where('locale', $locale)->first(); + return $translation ?: $this->translations()->orderBy('created_at', 'asc')->first(); + }); + } + + public function getAllChildrenIds(): array + { + return Cache::rememberForever(PageHelper::cacheKey('childrenIds', $this->id), function () { + $ids = []; + foreach ($this->children as $child) { + $ids[] = $child->id; + $ids = array_merge($ids, $child->getAllChildrenIds()); + } + return $ids; + }); + } + + public function availableParents() + { + $excludedIds = $this->getAllChildrenIds(); + $excludedIds[] = $this->id; + return PagePlus::whereNotIn('id', $excludedIds)->get(); + } + + public function getTopmostAncestor() + { + return Cache::rememberForever(PageHelper::cacheKey('topmostAncestor', $this->id), function () { + $ancestor = $this; + while ($ancestor->parent()->exists()) { + $ancestor = $ancestor->parent()->first(); + } + return $ancestor; + }); + } + + + public static function getBySlug($slug) + { + return Cache::rememberForever(PageHelper::cacheKey('bySlug', $slug), function () use ($slug) { + $slugs = explode('/', trim($slug, '/')); + $page = self::where('slug', array_shift($slugs))->firstOrFail(); + foreach ($slugs as $slug) { + $page = $page->children()->where('slug', $slug)->firstOrFail(); + } + return $page; + }); + } + +} + diff --git a/Entities/PagePlusMeta.php b/Entities/PagePlusMeta.php new file mode 100644 index 0000000..d55e40b --- /dev/null +++ b/Entities/PagePlusMeta.php @@ -0,0 +1,29 @@ +belongsTo(PagePlus::class, 'page_id'); + } + + protected static function booted(): void + { + static::saved(function ($meta) { + Cache::forget(PageHelper::cacheKey('meta', $meta->page_id) . '_' . $meta->key); + }); + + static::deleted(function ($meta) { + Cache::forget(PageHelper::cacheKey('meta', $meta->page_id) . '_' . $meta->key); + }); + } +} + diff --git a/Entities/PagePlusTranslation.php b/Entities/PagePlusTranslation.php new file mode 100644 index 0000000..4dc9286 --- /dev/null +++ b/Entities/PagePlusTranslation.php @@ -0,0 +1,29 @@ +belongsTo(PagePlus::class, 'page_id'); + } + + protected static function booted(): void + { + static::saved(function ($translation) { + Cache::forget(PageHelper::cacheKey('translation', $translation->page_id) . '_' . $translation->locale); + }); + + static::deleted(function ($translation) { + Cache::forget(PageHelper::cacheKey('translation', $translation->page_id) . '_' . $translation->locale); + }); + } +} + diff --git a/Http/Controllers/AdminPagePlusController.php b/Http/Controllers/AdminPagePlusController.php new file mode 100644 index 0000000..9853de2 --- /dev/null +++ b/Http/Controllers/AdminPagePlusController.php @@ -0,0 +1,98 @@ +paginate(20); + return view(AdminTheme::moduleView('pageplus', 'index'), compact('pages')); + } + + public function create(PagePlus $page = null) + { + $pages = PagePlus::get(); + return view(AdminTheme::moduleView('pageplus', 'create'), compact('pages', 'page')); + } + + public function store(Request $request) + { + $data = $request->validate([ + 'id' => 'nullable|exists:page_pluses,id', + 'parent_id' => 'nullable|exists:page_pluses,id', + 'slug' => ['required', 'string', 'regex:/^[A-Za-z0-9\-_]+$/', new UniqueSlug($request->input('id'))], + 'order' => 'required|integer', + 'locale' => 'nullable|string', + 'title' => 'nullable|string', + 'content' => 'nullable|string', + ]); + + $page = PagePlus::updateOrCreate( + ['id' => $data['id'] ?? null], + ['parent_id' => $data['parent_id'], 'slug' => $data['slug'], 'order' => $data['order']] + ); + + $page->translations()->updateOrCreate( + ['locale' => $data['locale'] ?? app()->getLocale()], + ['title' => $data['title'] ?? '', 'content' => $data['content'] ?? ''] + ); + + if ($request->has('meta')) { + foreach ($request->input('meta') as $key => $value) { + $page->metas()->updateOrCreate( + ['key' => $key], + ['value' => $value ?? ''] + ); + } + } + + return redirect()->route('admin.pageplus.index')->with('success', __('pageplus::messages.page_saved_success')); + } + + public function destroy(PagePlus $page) + { + $page->delete(); + return redirect()->route('admin.pageplus.index'); + } + + public function translate(PagePlus $page, $locale = null) + { + $locale = $locale ?: app()->getLocale(); + return view(AdminTheme::moduleView('pageplus', 'translate'), compact('page', 'locale')); + } + + public function translateStore(Request $request, PagePlus $page) + { + $data = $request->validate([ + 'locale' => 'required|string', + 'title' => 'required|string', + 'content' => 'required|string', + ]); + $page->translations()->updateOrCreate( + ['locale' => $data['locale']], + ['title' => $data['title'], 'content' => $data['content']] + ); + + return redirect()->route('admin.pageplus.index')->with('success', __('pageplus::messages.translate_saved_success')); + } + + public function changeOrder(PagePlus $page, $action = 'down') + { + if ($action == 'up') { + $page->increment('order'); + } else { + $page->decrement('order'); + } + return redirect()->back()->with('success', __('pageplus::messages.order_change_success')); + } + +} diff --git a/Http/Controllers/PagePlusController.php b/Http/Controllers/PagePlusController.php new file mode 100644 index 0000000..fc04741 --- /dev/null +++ b/Http/Controllers/PagePlusController.php @@ -0,0 +1,16 @@ +registerTranslations(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); + $this->registerConfig(); + } + + public function register(): void + { + $this->app->register(RouteServiceProvider::class); + } + + protected function registerConfig(): void + { + $this->publishes([ + module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'), + ], 'config'); + $this->mergeConfigFrom( + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower + ); + } + + public function registerViews(): void + { + $viewPath = resource_path('views/modules/' . $this->moduleNameLower); + + $sourcePath = module_path($this->moduleName, 'Resources/views'); + + $this->publishes([ + $sourcePath => $viewPath + ], ['views', $this->moduleNameLower . '-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + } + + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/' . $this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath, $this->moduleNameLower); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + } + } + + public function provides(): array + { + return []; + } + + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (\Config::get('view.paths') as $path) { + if (is_dir($path . '/modules/' . $this->moduleNameLower)) { + $paths[] = $path . '/modules/' . $this->moduleNameLower; + } + } + return $paths; + } +} diff --git a/Providers/RouteServiceProvider.php b/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..655aa64 --- /dev/null +++ b/Providers/RouteServiceProvider.php @@ -0,0 +1,32 @@ +mapAdminRoutes(); + $this->mapWebRoutes(); + } + + protected function mapWebRoutes(): void + { + Route::middleware('web') + ->namespace($this->moduleNamespace) + ->group(module_path('PagePlus', '/Routes/web.php')); + } + + protected function mapAdminRoutes(): void + { + Route::prefix('admin') + ->middleware(['web']) + ->namespace($this->moduleNamespace) + ->group(module_path('PagePlus', '/Routes/admin.php')); + } +} diff --git a/Resources/lang/en/messages.php b/Resources/lang/en/messages.php new file mode 100644 index 0000000..fbba0d3 --- /dev/null +++ b/Resources/lang/en/messages.php @@ -0,0 +1,39 @@ + 'Title', + 'edit_page' => 'Edit Page', + 'create_page' => 'Create Page', + 'back' => 'Back', + 'slug' => 'Slug', + 'icon' => 'Icon', + 'order' => 'Order', + 'parent_page' => 'Parent Page', + 'redirect_url' => 'Redirect URL', + 'page_location' => 'Page Location', + 'update_page' => 'Update Page', + 'no_location' => 'No Location', + 'navbar' => 'Navbar', + 'header_right' => 'Header Right', + 'header_left' => 'Header Left', + 'user_dropdown' => 'User Dropdown', + 'app_dropdown' => 'App Dropdown', + 'help_footer' => 'Help Center (footer)', + 'legal_footer' => 'Legal (footer)', + 'resources_footer' => 'Resources (footer)', + 'pages' => 'Pages', + 'actions' => 'Actions', + 'translation_page' => 'Translation Page', + 'language' => 'Language', + 'save_translation' => 'Save Translation', + 'content' => 'Content', + 'move_up' => 'Move Up', + 'move_down' => 'Move Down', + 'go_to_page' => 'Go to page', + 'translate' => 'Translate', + 'edit' => 'Edit', + 'delete' => 'Delete', + 'page_saved_success' => 'Page saved successfully.', + 'translate_saved_success' => 'Page translated successfully.', + 'order_change_success' => 'Order changed successfully.', +]; diff --git a/Resources/lang/uk/messages.php b/Resources/lang/uk/messages.php new file mode 100644 index 0000000..ae6c926 --- /dev/null +++ b/Resources/lang/uk/messages.php @@ -0,0 +1,39 @@ + 'Назва', + 'edit_page' => 'Редагувати сторінку', + 'create_page' => 'Створити сторінку', + 'back' => 'Назад', + 'slug' => 'Урл (slug)', + 'icon' => 'Значок', + 'order' => 'Порядок', + 'parent_page' => 'Батьківська сторінка', + 'redirect_url' => 'URL-адреса перенаправлення', + 'page_location' => 'Розташування сторінки', + 'update_page' => 'Сторінка оновлення', + 'no_location' => 'Без розташування', + 'navbar' => 'Навігаційна панель', + 'header_right' => 'Заголовок справа', + 'header_left' => 'Заголовок ліворуч', + 'user_dropdown' => 'Випадаюче меню користувача', + 'app_dropdown' => 'Випадаюче меню програми', + 'help_footer' => 'Довідковий центр (footer)', + 'legal_footer' => 'Юридична інформація (footer)', + 'resources_footer' => 'Ресурси (footer)', + 'pages' => 'Сторінки', + 'actions' => 'Дії', + 'translation_page' => 'Сторінка перекладу', + 'language' => 'Мова', + 'save_translation' => 'Зберегти переклад', + 'content' => 'Зміст', + 'move_up' => 'Перемістити вгору', + 'move_down' => 'Перемістити вниз', + 'go_to_page' => 'Перейти на сторінку', + 'translate' => 'Переклад', + 'edit' => 'Редагувати', + 'delete' => 'Видалити', + 'page_saved_success' => 'Сторінку успішно збережено.', + 'translate_saved_success' => 'Сторінку успішно перекладено.', + 'order_change_success' => 'Порядок успішно змінено.', +]; diff --git a/Resources/views/admin/default/create.blade.php b/Resources/views/admin/default/create.blade.php new file mode 100644 index 0000000..5527f33 --- /dev/null +++ b/Resources/views/admin/default/create.blade.php @@ -0,0 +1,123 @@ +@extends(AdminTheme::wrapper(), ['title' => $page ? __('pageplus::messages.edit_page') : __('pageplus::messages.create_page'), 'keywords' => 'WemX Dashboard, WemX Panel']) + +@section('container') + +
{!! __('pageplus::messages.title') !!} | +{!! __('pageplus::messages.slug') !!} | +{!! __('pageplus::messages.order') !!} | +{!! __('pageplus::messages.actions') !!} | +
---|