diff --git a/app/Http/Controllers/Backend/PageBuilder.php b/app/Http/Controllers/Backend/PageBuilder.php new file mode 100644 index 0000000..7f21659 --- /dev/null +++ b/app/Http/Controllers/Backend/PageBuilder.php @@ -0,0 +1,341 @@ +withCount('revisions')->get(); + + return Inertia::render('Backend/PageBuilder/Index', [ + 'builtPages' => $pages->map(function ($page) { + return [ + 'id' => $page->id, + 'uuid' => $page->uuid, + 'title' => $page->title, + 'slug' => $page->slug, + 'content' => $page->content, + 'revisions_count' => $page->revisions_count, + 'author' => [ + 'id' => $page->author->id, + 'name' => $page->author->name, + ], + 'activeRevision' => $page->activeRevision ? [ + 'uuid' => $page->activeRevision->uuid, + 'version' => $page->activeRevision->version, + ] : null + ]; + }), + ]); + } + + public function createLandingPage(CreateRequest $request) { + $imagesNodes = ImageNode::all(); + return Inertia::render('Backend/PageBuilder/Create', [ + 'images' => $imagesNodes + ]); + } + + public function storeLandingPage(CreateRequest $request) { + $data = $request->validated(); + + // Initiate slug duplication detection + $baseSlug = Str::slug($data['title']); + $slug = $baseSlug; + $suffix = 1; + + while (Page::where('slug', $slug)->exists()) { + $slug = $baseSlug . '-' . $suffix++; + } + + // If this page should be the main page, unset main on others + if ($data['mainpage']) { + Page::where('main', true)->update(['main' => false]); + } + + // Check if user has permission to publish a page + $publish = false; + $user = Auth::user(); + if ($user->hasRole('supervisor') || $user->hasRole('board')) { + $publish = $data['publish']; + } + + $page = Page::create([ + 'uuid' => Str::uuid(), + 'user_id' => Auth::id(), + 'title' => $data['title'], + 'slug' => $slug, + 'content' => $data['content'], + 'is_published' => $publish, + 'main' => $data['mainpage'], + 'linked' => $data['linked'], + 'linkorder' => $data['linkorder'], + 'visibility' => $data['visibility'], + ]); + + $rev = PageRevision::create([ + 'page_id' => $page->id, + 'user_id' => Auth::id(), + 'title' => $page->title, + 'slug' => $page->slug, + 'content' => $page->content, + 'label' => $page->is_published ? 'published' : 'manual-save', + 'active' => true + ]); + + return to_route('page-builder.builder.edit', ['pageVersion' => $rev->uuid]); + } + + public function deleteLandingPage(DeleteRequest $request) { + $page = Page::findOrFail($request->id); + + // Check if the user is the owner of the page + if (!Auth::user()->hasRole('supervisor') || !Auth::user()->hasRole('board')) { + if (Auth::user()->id != $page->user_id) { + return to_route('page-builder.index') + ->with('error', "Du er ikke forfatteren av denne siden og kan derfor ikke slette den."); + } + } + + $page->delete(); + + return to_route('page-builder.index')->with('warning', 'Siden ble slettet.'); + } + + public function editLandingPage(EditRequest $request, PageRevision $pageVersion) { + $revision = $pageVersion->load(['page.revisions.editor', 'editor']); + $images = ImageNode::all(); + + $pageRevisions = $revision->page->revisions->map(function ($rev) { + return [ + 'id' => $rev->id, + 'title' => $rev->title, + 'uuid' => $rev->uuid, + 'version' => $rev->version, + 'active' => $rev->active, + 'created_at'=> $rev->created_at, + 'updated_at'=> $rev->updated_at, + 'author' => [ + 'name' => $rev->editor->name, + 'email' => $rev->editor->email + ] + ]; + }); + + return Inertia::render('Backend/PageBuilder/Edit', [ + 'revision' => [ + 'id' => $revision->id, + 'uuid' => $revision->uuid, + 'version' => $revision->version, + 'title' => $revision->title, + 'slug' => $revision->slug, + 'label' => $revision->label, + 'active' => $revision->active, + 'content' => $revision->content, + 'page' => [ + 'id' => $revision->page->id, + 'uuid' => $revision->page->uuid, + 'is_published'=> $revision->page->is_published, + 'main' => $revision->page->main, + 'linked' => $revision->page->linked, + 'linkorder' => $revision->page->linkorder, + 'visibility' => $revision->page->visibility, + ] + ], + 'images' => $images, + 'revisions' => $pageRevisions + ]); + } + + public function patchLandingPage(EditRequest $request) { + $data = $request->validated(); + + // Load the revision by uuid + $revision = PageRevision::where('uuid', $request->input('revision_uuid')) + ->with('page') + ->firstOrFail(); + + // Check if the user is the owner of the current revision + if (!Auth::user()->hasRole('supervisor') || !Auth::user()->hasRole('board')) { + if (Auth::user()->id != $revision->user_id) { + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('info', "Du er ikke forfatteren av denne revisjonen og kan derfor ikke gjøre endringer til den. Hvis du ønsker å gjøre endringer, lagre denne revisjonen som en ny versjon."); + } + } + + // If this page should be the main page, unset main on others + if ($data['mainpage']) { + Page::where('main', true)->where('id', '!=', $revision->page_id)->update(['main' => false]); + } + + // Check if user can publish + $canPublish = Auth::user()->hasRole('supervisor') || Auth::user()->hasRole('board'); + $publish = $canPublish ? $data['publish'] : false; + + if ($revision->active) { + // Initiate slug duplication detection + $baseSlug = Str::slug($data['title']); + $slug = $baseSlug; + $suffix = 1; + + // ONly update slug if it has changed + if ($baseSlug !== $revision->page->slug) { + while (Page::where('slug', $slug)->where('id', '!=', $revision->page_id)->exists()) { + $slug = $baseSlug . '-' . $suffix++; + } + } else { + $slug = $revision->page->slug; + } + + // Update the base page model if the current revision is set to active + $revision->page->update([ + 'title' => $data['title'], + 'slug' => $slug, + 'content' => $data['content'], + 'is_published' => $publish, + 'main' => $data['mainpage'], + 'linked' => $data['linked'], + 'linkorder' => $data['linkorder'], + 'visibility' => $data['visibility'], + ]); + } + + // Update the current revision directly + $revision->update([ + 'title' => $data['title'], + 'slug' => Str::slug($data['title']), + 'content' => $data['content'], + 'label' => $publish ? 'published' : 'manual-save', + ]); + + if ($request->has('autosave') && $request->input('autosave')) { + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('info', 'Endringer lagret automatisk.'); + } + + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('success', 'Endringer ble lagret i nåværende revisjon.'); + } + + public function saveAsLandingPage(CreateRequest $request) { + $data = $request->validated(); + $page = Page::findOrFail($request->page_id); + + $revision = PageRevision::create([ + 'page_id' => $page->id, + 'user_id' => Auth::id(), + 'title' => $data['title'], + 'slug' => Str::slug($data['title']), + 'content' => $data['content'], + 'label' => 'manual-save', + 'active' => false + ]); + + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('success', "Ny versjon (v. {$revision->version}) opprettet."); + } + + public function setLandingPageRevisionActive(SetActiveRequest $request) { + $revision = PageRevision::where('uuid', $request->input('revision_uuid'))->firstOrFail(); + if (!Auth::user()->hasRole('supervisor')) { + if (!Auth::user()->hasRole('board')) { + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('error', "Du mangler tillatelse til å sette denne revisjonen som aktiv"); + } + } + + $data = $request->validated(); + + $page = $revision->page; + + PageRevision::where('active', true)->where('page_id', '=', $page->id)->update(['active' => false]); + $revision->active = true; + $revision->save(); + + // Initiate slug duplication detection + $baseSlug = Str::slug($data['title']); + $slug = $baseSlug; + $suffix = 1; + + while (Page::where('slug', $slug)->exists()) { + $slug = $baseSlug . '-' . $suffix++; + } + + $page->title = $data['title']; + $page->slug = $slug; + $page->content = $data['content']; + $page->is_published = false; + + $page->save; + + return to_route('page-builder.builder.edit', ['pageVersion' => $revision->uuid]) + ->with('success', "Versjon {$revision->version} satt som innhold til siden."); + } + + public function preview(PreviewRequest $request, PageRevision $pageVersion) { + $revision = $pageVersion->load(['page', 'editor']); + $images = ImageNode::all(); + + $neededFetchers = collect(); + + $blocks = json_decode($revision->content, true); + + foreach ($blocks as $block) { + if (!empty($block['needs'])) { + $neededFetchers = $neededFetchers->merge($block['needs']); + } + } + + $neededFetchers = $neededFetchers->unique(); + + $blockData = []; + + foreach ($neededFetchers as $fetcher) { + if (method_exists(PageBlocksHelper::class, $fetcher)) { + $blockData[$fetcher] = PageBlocksHelper::$fetcher(); + } + } + + return Inertia::render('Backend/PageBuilder/Preview', [ + 'revision' => [ + 'id' => $revision->id, + 'uuid' => $revision->uuid, + 'version' => $revision->version, + 'title' => $revision->title, + 'slug' => $revision->slug, + 'label' => $revision->label, + 'active' => $revision->active, + 'content' => json_decode($revision->content, true), + 'page' => [ + 'id' => $revision->page->id, + 'uuid' => $revision->page->uuid, + 'is_published'=> $revision->page->is_published, + 'main' => $revision->page->main, + 'visibility' => $revision->page->visibility, + ] + ], + 'images' => $images, + 'blockData' => $blockData, + 'menuLinks' => PageBlocksHelper::mainMenuLinks() + ]); + } +} \ No newline at end of file diff --git a/app/Http/Requests/Backend/PageBuilder/CreateRequest.php b/app/Http/Requests/Backend/PageBuilder/CreateRequest.php new file mode 100644 index 0000000..2ff0d60 --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/CreateRequest.php @@ -0,0 +1,39 @@ +|string> + */ + public function rules(): array { + if ($this->isMethod('get')) + return []; + + return [ + 'title' => ['required', 'string', 'max:255'], + 'content' => ['required', 'json'], + 'publish' => ['required', 'boolean'], + 'mainpage' => ['required', 'boolean'], + 'linked' => ['required', 'boolean'], + 'linkorder' => ['required', 'between:0,20'], + 'visibility' => ['required', 'in:public,private'], + ]; + } +} diff --git a/app/Http/Requests/Backend/PageBuilder/DeleteRequest.php b/app/Http/Requests/Backend/PageBuilder/DeleteRequest.php new file mode 100644 index 0000000..ae1ade7 --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/DeleteRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/Backend/PageBuilder/EditRequest.php b/app/Http/Requests/Backend/PageBuilder/EditRequest.php new file mode 100644 index 0000000..de46f42 --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/EditRequest.php @@ -0,0 +1,39 @@ +|string> + */ + public function rules(): array { + if ($this->isMethod('get')) + return []; + + return [ + 'title' => ['required', 'string', 'max:255'], + 'content' => ['required', 'json'], + 'publish' => ['required', 'boolean'], + 'mainpage' => ['required', 'boolean'], + 'linked' => ['required', 'boolean'], + 'linkorder' => ['required', 'between:0,20'], + 'visibility' => ['required', 'in:public,private'], + ]; + } +} diff --git a/app/Http/Requests/Backend/PageBuilder/IndexRequest.php b/app/Http/Requests/Backend/PageBuilder/IndexRequest.php new file mode 100644 index 0000000..47ea78f --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/IndexRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/Backend/PageBuilder/PreviewRequest.php b/app/Http/Requests/Backend/PageBuilder/PreviewRequest.php new file mode 100644 index 0000000..8a62c7f --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/PreviewRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/Backend/PageBuilder/SetActiveRequest.php b/app/Http/Requests/Backend/PageBuilder/SetActiveRequest.php new file mode 100644 index 0000000..23d848f --- /dev/null +++ b/app/Http/Requests/Backend/PageBuilder/SetActiveRequest.php @@ -0,0 +1,31 @@ +|string> + */ + public function rules(): array { + return [ + 'title' => ['required', 'string', 'max:255'], + 'content' => ['required', 'json'] + ]; + } +}