diff --git a/Modules/Content/Http/Controllers/ContentDocumentController.php b/Modules/Content/Http/Controllers/ContentDocumentController.php index 587c03f..af35ce9 100644 --- a/Modules/Content/Http/Controllers/ContentDocumentController.php +++ b/Modules/Content/Http/Controllers/ContentDocumentController.php @@ -1,20 +1,146 @@ ContentDocument::query()->with('type')->withCount('placements')->orderBy('title')->paginate(30)]); } - public function edit(ContentDocument $document): View { $document->load('type','placements'); return view('content::admin.edit',['document'=>$document]); } + public function index(Request $request): View + { + $documents = ContentDocument::query() + ->with('type') + ->withCount('placements') + ->when($request->string('type')->isNotEmpty(), fn ($query) => $query->whereHas('type', fn ($type) => $type->where('key', $request->string('type')->toString()))) + ->when($request->string('status')->isNotEmpty(), fn ($query) => $query->where('status', $request->string('status')->toString())) + ->when($request->string('search')->isNotEmpty(), fn ($query) => $query->where('title', 'like', '%'.$request->string('search')->toString().'%')) + ->orderBy('title') + ->paginate(30) + ->withQueryString(); + + return view('content::admin.index', [ + 'documents' => $documents, + 'types' => ContentType::query()->where('is_active', true)->orderBy('name')->get(), + ]); + } + + public function create(Request $request): View + { + $types = ContentType::query()->where('is_active', true)->orderBy('name')->get(); + $selectedType = $types->firstWhere('id', (int) $request->integer('type')) ?? $types->first(); + + return view('content::admin.form', [ + 'document' => new ContentDocument(['status' => 'draft', 'data' => []]), + 'types' => $types, + 'selectedType' => $selectedType, + ]); + } + + public function store(Request $request): RedirectResponse + { + $validated = $this->validateDocument($request); + $type = ContentType::query()->where('is_active', true)->findOrFail($validated['content_type_id']); + + $document = ContentDocument::query()->create([ + 'content_type_id' => $type->id, + 'title' => $validated['title'], + 'slug' => $this->uniqueSlug($validated['slug'] ?: $validated['title']), + 'status' => $validated['status'], + 'published_at' => $validated['status'] === 'published' ? ($validated['published_at'] ?? now()) : null, + 'expires_at' => $validated['expires_at'] ?? null, + 'data' => json_decode($validated['data_json'], true, 512, JSON_THROW_ON_ERROR), + 'created_by' => $request->user()?->id, + 'updated_by' => $request->user()?->id, + ]); + + return redirect()->route('content.documents.edit', $document)->with('status', 'Content document created.'); + } + + public function edit(ContentDocument $document): View + { + $document->load('type', 'placements'); + + return view('content::admin.form', [ + 'document' => $document, + 'types' => ContentType::query()->where('is_active', true)->orderBy('name')->get(), + 'selectedType' => $document->type, + ]); + } + public function update(Request $request, ContentDocument $document): RedirectResponse { - $validated=$request->validate(['title'=>['required','string','max:255'],'status'=>['required','in:draft,published'],'data_json'=>['required','json']]); - $document->update(['title'=>$validated['title'],'status'=>$validated['status'],'published_at'=>$validated['status']==='published'?($document->published_at??now()):null,'data'=>json_decode($validated['data_json'],true,512,JSON_THROW_ON_ERROR),'updated_by'=>$request->user()?->id]); - return back()->with('status','Content document updated.'); + $validated = $this->validateDocument($request, $document); + + $document->update([ + 'content_type_id' => $validated['content_type_id'], + 'title' => $validated['title'], + 'slug' => $this->uniqueSlug($validated['slug'] ?: $validated['title'], $document), + 'status' => $validated['status'], + 'published_at' => $validated['status'] === 'published' ? ($validated['published_at'] ?? $document->published_at ?? now()) : null, + 'expires_at' => $validated['expires_at'] ?? null, + 'data' => json_decode($validated['data_json'], true, 512, JSON_THROW_ON_ERROR), + 'updated_by' => $request->user()?->id, + ]); + + return back()->with('status', 'Content document updated.'); + } + + public function duplicate(Request $request, ContentDocument $document): RedirectResponse + { + $copy = $document->replicate(['slug', 'status', 'published_at', 'created_by', 'updated_by']); + $copy->title = $document->title.' copy'; + $copy->slug = $this->uniqueSlug($copy->title); + $copy->status = 'draft'; + $copy->published_at = null; + $copy->created_by = $request->user()?->id; + $copy->updated_by = $request->user()?->id; + $copy->save(); + + return redirect()->route('content.documents.edit', $copy)->with('status', 'Independent draft copy created.'); + } + + public function destroy(ContentDocument $document): RedirectResponse + { + if ($document->placements()->exists()) { + return back()->withErrors(['document' => 'Remove this document from every page before deleting it.']); + } + + $document->delete(); + + return redirect()->route('content.index')->with('status', 'Content document deleted.'); + } + + private function validateDocument(Request $request, ?ContentDocument $document = null): array + { + return $request->validate([ + 'content_type_id' => ['required', 'integer', Rule::exists('content_types', 'id')->where('is_active', true)], + 'title' => ['required', 'string', 'max:255'], + 'slug' => ['nullable', 'string', 'max:255'], + 'status' => ['required', Rule::in(['draft', 'published'])], + 'published_at' => ['nullable', 'date'], + 'expires_at' => ['nullable', 'date', 'after:published_at'], + 'data_json' => ['required', 'json'], + ]); + } + + private function uniqueSlug(string $value, ?ContentDocument $except = null): string + { + $base = Str::slug($value) ?: 'content-document'; + $slug = $base; + $suffix = 2; + + while (ContentDocument::withTrashed()->where('slug', $slug)->when($except, fn ($query) => $query->whereKeyNot($except->getKey()))->exists()) { + $slug = $base.'-'.$suffix++; + } + + return $slug; } } diff --git a/Modules/Content/Http/Controllers/PageCompositionController.php b/Modules/Content/Http/Controllers/PageCompositionController.php index 2b6a0de..af0ce60 100644 --- a/Modules/Content/Http/Controllers/PageCompositionController.php +++ b/Modules/Content/Http/Controllers/PageCompositionController.php @@ -1,19 +1,157 @@ $pageKey,'placements'=>PageContent::query()->with('document.type')->where('page_key',$pageKey)->orderByRaw("CASE region WHEN 'header' THEN 1 WHEN 'body' THEN 2 ELSE 3 END")->orderBy('sort_order')->get()->groupBy('region')]); } + private const REGIONS = ['header', 'body', 'footer']; + + public function edit(string $pageKey): View + { + $placements = PageContent::query() + ->with('document.type') + ->where('page_key', $pageKey) + ->orderByRaw("CASE region WHEN 'header' THEN 1 WHEN 'body' THEN 2 ELSE 3 END") + ->orderBy('sort_order') + ->get() + ->groupBy('region'); + + $documents = ContentDocument::query() + ->with('type') + ->withCount('placements') + ->orderBy('title') + ->get(); + + return view('content::admin.composition', compact('pageKey', 'placements', 'documents')); + } + + public function preview(string $pageKey): View + { + abort_unless($pageKey === 'home', 404, 'This phase currently provides the Saasland homepage preview.'); + + return view('stylist-theme-saasland::home', ['pageKey' => $pageKey]); + } + + public function store(Request $request, string $pageKey): RedirectResponse + { + $validated = $request->validate([ + 'content_document_id' => ['required', 'integer', Rule::exists('content_documents', 'id')->whereNull('deleted_at')], + 'region' => ['required', Rule::in(self::REGIONS)], + ]); + + $document = ContentDocument::query()->with('type')->findOrFail($validated['content_document_id']); + abort_unless(in_array($validated['region'], $document->type?->allowed_regions ?? [], true), 422, 'This content type is not allowed in the selected region.'); + + $nextOrder = ((int) PageContent::query()->where('page_key', $pageKey)->where('region', $validated['region'])->max('sort_order')) + 10; + PageContent::query()->create([ + 'page_key' => $pageKey, + 'content_document_id' => $document->id, + 'region' => $validated['region'], + 'sort_order' => $nextOrder, + 'is_enabled' => true, + ]); + + return back()->with('status', 'Document added to the '.$validated['region'].' region.'); + } + public function update(Request $request, PageContent $placement): RedirectResponse { - $validated=$request->validate(['sort_order'=>['required','integer','min:0'],'is_enabled'=>['nullable','boolean']]); - $placement->update(['sort_order'=>$validated['sort_order'],'is_enabled'=>$request->boolean('is_enabled')]); - return back()->with('status','Page placement updated.'); + $validated = $request->validate([ + 'is_enabled' => ['nullable', 'boolean'], + 'starts_at' => ['nullable', 'date'], + 'ends_at' => ['nullable', 'date', 'after:starts_at'], + ]); + + $placement->update([ + 'is_enabled' => $request->boolean('is_enabled'), + 'starts_at' => $validated['starts_at'] ?? null, + 'ends_at' => $validated['ends_at'] ?? null, + ]); + + return back()->with('status', 'Page placement updated.'); + } + + public function destroy(PageContent $placement): RedirectResponse + { + $placement->delete(); + $this->normaliseRegion($placement->page_key, $placement->region); + + return back()->with('status', 'Document removed from this page. The reusable document was not deleted.'); + } + + public function duplicate(Request $request, PageContent $placement): RedirectResponse + { + $source = $placement->document()->firstOrFail(); + $copy = DB::transaction(function () use ($request, $placement, $source): ContentDocument { + $copy = $source->replicate(['slug', 'status', 'published_at', 'created_by', 'updated_by']); + $copy->title = $source->title.' copy'; + $copy->slug = $source->slug.'-copy-'.strtolower(str()->random(6)); + $copy->status = 'draft'; + $copy->published_at = null; + $copy->created_by = $request->user()?->id; + $copy->updated_by = $request->user()?->id; + $copy->save(); + + PageContent::query()->create([ + 'page_id' => $placement->page_id, + 'page_key' => $placement->page_key, + 'content_document_id' => $copy->id, + 'region' => $placement->region, + 'sort_order' => $placement->sort_order + 5, + 'is_enabled' => false, + 'visibility_rules' => $placement->visibility_rules, + 'presentation' => $placement->presentation, + ]); + + return $copy; + }); + + $this->normaliseRegion($placement->page_key, $placement->region); + + return redirect()->route('content.documents.edit', $copy)->with('status', 'Page-specific draft copy created and added after the original.'); + } + + public function reorder(Request $request, string $pageKey): JsonResponse|RedirectResponse + { + $validated = $request->validate([ + 'region' => ['required', Rule::in(self::REGIONS)], + 'placements' => ['required', 'array'], + 'placements.*' => ['required', 'integer'], + ]); + + $ids = array_values(array_unique(array_map('intval', $validated['placements']))); + $owned = PageContent::query()->where('page_key', $pageKey)->where('region', $validated['region'])->whereIn('id', $ids)->pluck('id')->all(); + abort_unless(count($owned) === count($ids), 422, 'One or more placements do not belong to this page region.'); + + DB::transaction(function () use ($ids): void { + PageContent::query()->whereIn('id', $ids)->increment('sort_order', 100000); + foreach ($ids as $index => $id) { + PageContent::query()->whereKey($id)->update(['sort_order' => ($index + 1) * 10]); + } + }); + + return $request->expectsJson() + ? response()->json(['message' => 'Order saved.']) + : back()->with('status', 'Page order updated.'); + } + + private function normaliseRegion(?string $pageKey, string $region): void + { + $ids = PageContent::query()->where('page_key', $pageKey)->where('region', $region)->orderBy('sort_order')->pluck('id'); + DB::transaction(function () use ($ids): void { + foreach ($ids as $index => $id) { + PageContent::query()->whereKey($id)->update(['sort_order' => ($index + 1) * 10]); + } + }); } } diff --git a/Modules/Content/README.md b/Modules/Content/README.md index d2e7942..b1a800d 100644 --- a/Modules/Content/README.md +++ b/Modules/Content/README.md @@ -1,33 +1,37 @@ -# Content composition module +# Content Composer -This module separates public pages into reusable typed content documents placed in named regions. +The Content module provides reusable typed documents and ordered page regions. -## Install the baseline composition +## Phase 2 features + +- Header, body and footer composition editor. +- Drag-and-drop ordering with server-side ownership validation. +- Add existing published or draft documents to a page. +- Remove a placement without deleting the reusable document. +- Create an independent page-specific draft copy. +- Shared-use warnings and deletion protection. +- Placement enable/disable and date scheduling. +- Content document creation, duplication, filtering and publishing controls. +- Authenticated Saasland homepage preview, including draft labels. +- Content Composer entry in the administration sidebar. + +## Setup ```bash php artisan migrate php artisan content:install-saasland ``` -Use `--force` to replace the existing `home` placements while retaining reusable documents. +Reset the seeded homepage composition when required: ```bash php artisan content:install-saasland --force ``` -## Administration +Administration: -After signing in, open: +- `/admin/content` +- `/admin/content/pages/home` +- `/admin/content/pages/home/preview` -- `/admin/content` for reusable content documents. -- `/admin/content/pages/home` for the homepage header/body/footer composition. - -Documents contain structured JSON data and can be shared by several page placements. Placements own ordering, enabled state, scheduling, visibility and presentation metadata. The Saasland frontend falls back to its original hard-coded homepage until the migration and installer command have been run. - -## Theme renderers - -Saasland renderers are stored under: - -`resources/themes/saasland/views/documents` - -The renderer name is defined by each `content_types.renderer` value. Missing renderers fail safely through `content::documents.unsupported`. +The public theme remains backward-compatible: when no composition exists, the legacy Saasland homepage, header and footer are rendered. diff --git a/Modules/Content/Resources/views/admin/composition.blade.php b/Modules/Content/Resources/views/admin/composition.blade.php index 67c433b..de53bf2 100644 --- a/Modules/Content/Resources/views/admin/composition.blade.php +++ b/Modules/Content/Resources/views/admin/composition.blade.php @@ -1 +1,146 @@ -

Page composition: {{ $pageKey }}

@if(session('status'))

{{ session('status') }}

@endif @foreach(['header','body','footer'] as $region)

{{ $region }}

@forelse($placements->get($region,collect()) as $placement)
@csrf @method('PUT')
{{ $placement->document?->title }}
{{ $placement->document?->type?->name }}
Edit
@empty

No documents assigned.

@endforelse
@endforeach
+ +
+
+
+
Content documents/Page composition
+

{{ ucfirst($pageKey) }} page

+

Drag documents to reorder each region. Reusing a document keeps content synchronised across pages.

+
+
+ Create document + Open preview +
+
+ + @if(session('status'))
{{ session('status') }}
@endif + @if($errors->any())
    @foreach($errors->all() as $error)
  • {{ $error }}
  • @endforeach
@endif + +
+
+ @foreach(['header', 'body', 'footer'] as $region) +
+
+

{{ ucfirst($region) }}

{{ $region === 'body' ? 'Ordered content sections' : 'Page-level '.$region.' document' }}

+ {{ $placements->get($region, collect())->count() }} document{{ $placements->get($region, collect())->count() === 1 ? '' : 's' }} +
+
+ @forelse($placements->get($region, collect()) as $placement) +
+
+ +
+
+

{{ $placement->document?->title }}

+ {{ $placement->document?->type?->name }} + @if(($placement->document?->placements()->count() ?? 0) > 1)Shared@endif + @unless($placement->is_enabled)Disabled@endunless +
+

Order {{ $placement->sort_order }} · {{ $placement->document?->status === 'published' ? 'Published' : 'Draft — hidden from public rendering' }}

+
+ Edit +
+ +
+ Placement settings +
+
+ @csrf @method('PUT') + + +
+ + Save +
+ +
+
+
@csrfCreate page-specific copy
+
@csrf @method('DELETE')Remove from page
+
+
+
+ @empty +
No documents assigned to this region.
+ @endforelse +
+
+ @endforeach +
+ + +
+
+ +@push('scripts') + +@endpush +
diff --git a/Modules/Content/Resources/views/admin/edit.blade.php b/Modules/Content/Resources/views/admin/edit.blade.php deleted file mode 100644 index e7ec84d..0000000 --- a/Modules/Content/Resources/views/admin/edit.blade.php +++ /dev/null @@ -1 +0,0 @@ -
@csrf @method('PUT')

{{ $document->title }}

@if(session('status'))

{{ session('status') }}

@endif@error('data_json')

{{ $message }}

@enderror
diff --git a/Modules/Content/Resources/views/admin/form.blade.php b/Modules/Content/Resources/views/admin/form.blade.php new file mode 100644 index 0000000..ebaa180 --- /dev/null +++ b/Modules/Content/Resources/views/admin/form.blade.php @@ -0,0 +1,120 @@ + +
+
+
+
+ Content documents + / + {{ $document->exists ? 'Edit' : 'Create' }} +
+

{{ $document->exists ? $document->title : 'Create content document' }}

+

Structured, reusable content that can be placed into one or more page regions.

+
+ @if($document->exists) +
+
+ @csrf + Create independent copy +
+
+ @csrf @method('DELETE') + Delete +
+
+ @endif +
+ + @if(session('status')) +
{{ session('status') }}
+ @endif + @if($errors->any()) +
+ The document could not be saved. +
    @foreach($errors->all() as $error)
  • {{ $error }}
  • @endforeach
+
+ @endif + + @if($document->exists && $document->placements->count() > 1) +
+ Shared document. This document is used in {{ $document->placements->count() }} page placements. Changes will appear everywhere it is published. Use “Create independent copy” for page-specific content. +
+ @endif + +
+ @csrf + @if($document->exists) @method('PUT') @endif + +
+ +

Document

+ + + + @foreach($types as $type) + + @endforeach + +
+ + +
+
+

Structured data

+

This phase retains JSON editing while adding validation, copies, placement controls and preview. Schema-generated field controls follow in the next checkpoint.

+
+ +
+ + +
+
+ + +
+
+ +@push('scripts') + +@endpush +
diff --git a/Modules/Content/Resources/views/admin/index.blade.php b/Modules/Content/Resources/views/admin/index.blade.php index e19be1e..c219cb4 100644 --- a/Modules/Content/Resources/views/admin/index.blade.php +++ b/Modules/Content/Resources/views/admin/index.blade.php @@ -1,5 +1,53 @@ -

Content documents

Reusable typed documents used by page regions.

-
Edit homepage composition
-
@foreach($documents as $document)@endforeach
DocumentTypeStatusUses
{{ $document->title }}{{ $document->type?->name }}{{ ucfirst($document->status) }}{{ $document->placements_count }}Edit
{{ $documents->links() }}
+
+
+
+

Content documents

+

Create and reuse typed content across page regions.

+
+
+ Compose homepage + Create document +
+
+ + @if(session('status')) +
{{ session('status') }}
+ @endif + @error('document')
{{ $message }}
@enderror + +
+ + + + @foreach($types as $type)@endforeach + + + + + + +
FilterReset
+ + +
+ + + + @forelse($documents as $document) + + + + + + + + @empty + + @endforelse + +
DocumentTypeStatusUsesActions
{{ $document->title }}
{{ $document->slug }}
{{ $document->type?->name }}{{ ucfirst($document->status) }}{{ $document->placements_count }}Edit
No content documents match the current filters.
+
+ {{ $documents->links() }} +
diff --git a/Modules/Content/Routes/web.php b/Modules/Content/Routes/web.php index 6f0a304..803e350 100644 --- a/Modules/Content/Routes/web.php +++ b/Modules/Content/Routes/web.php @@ -1,12 +1,23 @@ prefix('admin/content')->name('content.')->group(function (): void { - Route::get('/', [ContentDocumentController::class,'index'])->name('index'); - Route::get('/documents/{document}/edit', [ContentDocumentController::class,'edit'])->name('documents.edit'); - Route::put('/documents/{document}', [ContentDocumentController::class,'update'])->name('documents.update'); - Route::get('/pages/{pageKey}', [PageCompositionController::class,'edit'])->name('pages.edit'); - Route::put('/placements/{placement}', [PageCompositionController::class,'update'])->name('placements.update'); +Route::middleware(['web', 'auth', 'verified'])->prefix('admin/content')->name('content.')->group(function (): void { + Route::get('/', [ContentDocumentController::class, 'index'])->name('index'); + Route::get('/documents/create', [ContentDocumentController::class, 'create'])->name('documents.create'); + Route::post('/documents', [ContentDocumentController::class, 'store'])->name('documents.store'); + Route::get('/documents/{document}/edit', [ContentDocumentController::class, 'edit'])->name('documents.edit'); + Route::put('/documents/{document}', [ContentDocumentController::class, 'update'])->name('documents.update'); + Route::post('/documents/{document}/duplicate', [ContentDocumentController::class, 'duplicate'])->name('documents.duplicate'); + Route::delete('/documents/{document}', [ContentDocumentController::class, 'destroy'])->name('documents.destroy'); + + Route::get('/pages/{pageKey}', [PageCompositionController::class, 'edit'])->name('pages.edit'); + Route::get('/pages/{pageKey}/preview', [PageCompositionController::class, 'preview'])->name('pages.preview'); + Route::post('/pages/{pageKey}/placements', [PageCompositionController::class, 'store'])->name('placements.store'); + Route::put('/placements/{placement}', [PageCompositionController::class, 'update'])->name('placements.update'); + Route::delete('/placements/{placement}', [PageCompositionController::class, 'destroy'])->name('placements.destroy'); + Route::post('/placements/{placement}/duplicate', [PageCompositionController::class, 'duplicate'])->name('placements.duplicate'); + Route::post('/pages/{pageKey}/reorder', [PageCompositionController::class, 'reorder'])->name('placements.reorder'); }); diff --git a/Modules/Content/Services/PageRenderer.php b/Modules/Content/Services/PageRenderer.php index a13c588..d02c898 100644 --- a/Modules/Content/Services/PageRenderer.php +++ b/Modules/Content/Services/PageRenderer.php @@ -1,8 +1,11 @@ where('page_key',$pageKey)->where('is_enabled',true)->exists(); } - catch (Throwable) { return false; } + try { + return Schema::hasTable('page_content') + && PageContent::query()->where('page_key', $pageKey)->where('is_enabled', true)->exists(); + } catch (Throwable) { + return false; + } } public function renderRegion(string $pageKey, string $region): string { - if (! $this->hasComposition($pageKey)) return ''; + if (! $this->hasComposition($pageKey)) { + return ''; + } + $preview = Route::currentRouteNamed('content.pages.preview'); $placements = PageContent::query() ->with('document.type') ->where('page_key', $pageKey) ->where('region', $region) ->where('is_enabled', true) - ->where(fn($q) => $q->whereNull('starts_at')->orWhere('starts_at','<=',now())) - ->where(fn($q) => $q->whereNull('ends_at')->orWhere('ends_at','>',now())) - ->orderBy('sort_order')->get(); + ->where(fn ($query) => $query->whereNull('starts_at')->orWhere('starts_at', '<=', now())) + ->where(fn ($query) => $query->whereNull('ends_at')->orWhere('ends_at', '>', now())) + ->orderBy('sort_order') + ->get(); - return $placements->map(function (PageContent $placement): string { + return $placements->map(function (PageContent $placement) use ($preview): string { $document = $placement->document; - if (! $document || ! $document->type || $document->status !== 'published') return ''; + if (! $document || ! $document->type || ! $this->isRenderable($document, $preview)) { + return ''; + } + $view = 'stylist-theme-saasland::documents.'.$document->type->renderer; - if (! View::exists($view)) $view = 'content::documents.unsupported'; - return View::make($view, ['document'=>$document,'data'=>$document->data ?? [],'placement'=>$placement])->render(); + if (! View::exists($view)) { + $view = 'content::documents.unsupported'; + } + + $rendered = View::make($view, [ + 'document' => $document, + 'data' => $document->data ?? [], + 'placement' => $placement, + 'isCompositionPreview' => $preview, + ])->render(); + + if ($preview && $document->status !== 'published') { + return '
Draft preview
'.$rendered.'
'; + } + + return $rendered; })->implode("\n"); } + + private function isRenderable(ContentDocument $document, bool $preview): bool + { + if ($preview) { + return true; + } + + if ($document->status !== 'published') { + return false; + } + + if ($document->published_at?->isFuture()) { + return false; + } + + return ! $document->expires_at?->isPast(); + } } diff --git a/resources/themes/common/views/components/layouts/app/sidebar.blade.php b/resources/themes/common/views/components/layouts/app/sidebar.blade.php index 364dd92..c848c25 100644 --- a/resources/themes/common/views/components/layouts/app/sidebar.blade.php +++ b/resources/themes/common/views/components/layouts/app/sidebar.blade.php @@ -26,6 +26,7 @@ {{ __('Pages') }} + {{ __('Content Composer') }} {{ __('Menus') }} {{ __('Redirects') }} {{ __('Forms') }} @@ -120,6 +121,7 @@ @endpersist +@stack('scripts') @fluxScripts diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore deleted file mode 100644 index 01e4a6c..0000000 --- a/storage/framework/cache/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -* -!data/ -!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/cache/data/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/sessions/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/framework/views/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore deleted file mode 100644 index e69de29..0000000