diff --git a/publishable/assets/cms/icons/chevron-left.svg b/publishable/assets/cms/icons/chevron-left.svg new file mode 100644 index 0000000..76edb5b --- /dev/null +++ b/publishable/assets/cms/icons/chevron-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/views/articles/_articles.blade.php b/resources/views/articles/_articles.blade.php index 3d60f99..78f47ad 100644 --- a/resources/views/articles/_articles.blade.php +++ b/resources/views/articles/_articles.blade.php @@ -1,84 +1,49 @@ -@forelse($articles as $article) - - -
-
-
- {{ $article->title }} -
- -
- Edit - Preview - Delete -
-
-
-
-
-
-

Delete article: {{ $article->title }}?

-

Are you sure you want to delete this article?

-
-
-
- Cancel -
- - Delete -
-
-
- - - - {{ views($article)->unique()->count() ?? 0 }} - - - - - {{ $article->category->name }} - - - - - {{ $article->publish_date->format('d M, Y') }} - - - - - @if ($article->article_status == 'Published') - {{ $article->article_status }} - @else - {{ $article->article_status }} - @endif - - - -@empty - - No articles found. - -@endforelse +@if ($articles->isNotEmpty()) + + +@else + No articles found. +@endif \ No newline at end of file diff --git a/resources/views/articles/create.blade.php b/resources/views/articles/create.blade.php index 1c9fb33..d1823e6 100644 --- a/resources/views/articles/create.blade.php +++ b/resources/views/articles/create.blade.php @@ -48,11 +48,13 @@ featuredImage - -
- -
- + + + @@ -66,7 +68,7 @@ - + Save Article diff --git a/resources/views/articles/edit.blade.php b/resources/views/articles/edit.blade.php index 49ea901..36c6def 100644 --- a/resources/views/articles/edit.blade.php +++ b/resources/views/articles/edit.blade.php @@ -50,10 +50,13 @@ -
- -
- + + + @@ -74,7 +77,7 @@ + :value="$article->publish_date->format('D M d Y')" /> Update Article diff --git a/resources/views/articles/index.blade.php b/resources/views/articles/index.blade.php index d53f795..9937df2 100644 --- a/resources/views/articles/index.blade.php +++ b/resources/views/articles/index.blade.php @@ -22,47 +22,71 @@ New Article -
-
- + @goto-page="fetchData($event.detail.page)" + @reload.window="fetchData()" + x-cloak> + +
+
- - - - Title - Views - Category - Published Date - Status - - - -
-
-
{{ $articles->links('flowcms::partials.tailwindPagination') }}
+
+ @include('flowcms::articles._articles') +
+
@endsection diff --git a/resources/views/components/base-datatable.blade.php b/resources/views/components/base-datatable.blade.php new file mode 100644 index 0000000..4d7f1b5 --- /dev/null +++ b/resources/views/components/base-datatable.blade.php @@ -0,0 +1,149 @@ +
+ + + + @foreach($headings as $heading) + + @endforeach + + + + @foreach($data as $index => $item) + + @foreach($values as $value) + + @endforeach + + + + @endforeach + +
+ {{ $heading }} +
+ + @php $valueItem = explode('.', $value['key']); @endphp + + @if($value['type'] === 'data') + + @if(count($valueItem) == 1) + @if(isset($value['theme']) && $value['theme']['type'] === 'badge') + + {{ $item->{$valueItem[0]} }} + + @else + {{ $item->{$valueItem[0]} }} + @endif + @endif + + @if(count($valueItem) == 2) + @if(isset($value['theme']) && $value['theme']['type'] === 'badge') + + {{ $item->{$valueItem[0]}->{$valueItem[1]} }} + + @else + {{ $item->{$valueItem[0]}->{$valueItem[1]} }} + @endif + @endif + + @if(count($valueItem) == 3) + {{ $item->{$valueItem[0]}->{$valueItem[1]}->{$valueItem[2]} }} + @endif + + @endif + + @if($value['type'] === 'date') + + @if(count($valueItem) == 1) + @if(isset($value['format'])) + {{ $item->{$valueItem[0]}->format($value['format']) }} + @else + {{ $item->{$valueItem[0]}->format('j M, Y') }} + @endif + @endif + + @if(count($valueItem) == 2) + @if(isset($value['format'])) + {{ $item->{$valueItem[0]}->{$valueItem[1]}->format($value['format']) }} + @else + {{ $item->{$valueItem[0]}->{$valueItem[1]}->format('j M, Y') }} + @endif + @endif + + @if(count($valueItem) == 3) + @if(isset($value['format'])) + {{ $item->{$valueItem[0]}->{$valueItem[1]}->{$valueItem[2]}->format($value['format']) }} + @else + {{ $item->{$valueItem[0]}->{$valueItem[1]}->{$valueItem[2]}->format('j M, Y') }} + @endif + @endif + + @endif + + @php $actions = collect($value['type']); @endphp + + @if($actions->contains('edit') || $actions->contains('delete')) +
+ @if($actions->contains('edit')) + @if (! empty($editRoute) && !empty($editId)) + Edit + @else + Edit route & id not provided + @endif + @endif + @if($actions->contains('delete')) + @if (! empty($deleteRoute) && !empty($deleteId)) + Delete + @else + Delete route & id not provided + @endif + @endif +
+ @endif +
+ +
+
+
+

Are you sure?

+
+
+ + + + + +
+
+
+ +
+
+ +
+ {{ $data->onEachSide(2)->links('flowcms::partials.tailwindPaginationAlpinejs') }} +
\ No newline at end of file diff --git a/resources/views/components/button.blade.php b/resources/views/components/button.blade.php index af0794f..45b28a5 100644 --- a/resources/views/components/button.blade.php +++ b/resources/views/components/button.blade.php @@ -1,4 +1,5 @@ @if(isset($type) && $type === 'submit') + + @else + + @endif \ No newline at end of file diff --git a/resources/views/components/link.blade.php b/resources/views/components/link.blade.php index 674f5a2..0f1c005 100644 --- a/resources/views/components/link.blade.php +++ b/resources/views/components/link.blade.php @@ -1,6 +1,6 @@ merge([ - 'class' => 'text-indigo-500 border-b border-indigo-200 hover:text-indigo-700 transition duration-300 ease-out' + 'class' => 'text-indigo-500 underline underline-indigo-200 hover:text-indigo-600 transition duration-300 ease-out inline-flex items-center' ])}} > {{ $slot }} diff --git a/resources/views/components/quill-editor.blade.php b/resources/views/components/quill-editor.blade.php index 34f48ba..6ae3a35 100644 --- a/resources/views/components/quill-editor.blade.php +++ b/resources/views/components/quill-editor.blade.php @@ -1,38 +1,103 @@ -
+ }; + + xhr.onload = function () { + if (this.status >= 200 && this.status < 300) { + // this is callback data: url + const data = JSON.parse(this.responseText); + // console.log(data); + + // push image url to rich editor. + const range = quillInstance.getSelection(); + quillInstance.insertEmbed(range.index, 'image', `/${data.url}`); + // puts the cursor at the end of image + quillInstance.setSelection(range.index + 1, Quill.sources.SILENT); + } + }; + xhr.send(fd); + } + }" + x-init=" + document.addEventListener('DOMContentLoaded', () => { + quill = new Quill($refs.quillEditor, { + scrollingContainer: '.ql-scrolling-container', + modules: { + toolbar: { + container: [ + [{'header': 2}, 'bold', 'italic', 'underline', 'strike'], + ['link', 'blockquote', 'code-block', 'image', 'video'], + [{ list: 'ordered' }, { list: 'bullet' }], + ['clean'] + ] + } + }, + theme: 'snow', + placeholder: '{{ $placeholder ?? 'Write something great!' }}' + }); + quill.on('text-change', function () { + let html = quill.root.innerHTML; + if (html === '


') html = '' + content = html; + }); + quill.clipboard.addMatcher(Node.ELEMENT_NODE, function (node, delta) { + var plaintext = node.innerText; + var Delta = Quill.import('delta'); + return new Delta().insert(plaintext); + }); + // quill editor add image handler + quill.getModule('toolbar').addHandler('image', () => { + selectLocalImage(quill); + }); + // console.log(quill.getContents()); + content = (quill.root.innerHTML === '


') + ? '' + : quill.root.innerHTML; + }) + " + x-cloak> @if($label ?? null) @endif -
- -
- {!! old($name, $value) !!} +
+ +
+
+
+ + +
+ {!! old($name, $value ?? '') !!}
@error($name) -
- - - -
{{ $message }}
-
+ + + +
{{ $message }}
@enderror
\ No newline at end of file diff --git a/resources/views/contacts/_contacts.blade.php b/resources/views/contacts/_contacts.blade.php index 97c1d6a..df2cadc 100644 --- a/resources/views/contacts/_contacts.blade.php +++ b/resources/views/contacts/_contacts.blade.php @@ -66,7 +66,35 @@
+@if ($contacts->isNotEmpty()) + + +@else + No contacts. +@endif +{{-- @forelse($contacts as $contact) Delete No messages found. -@endforelse +@endforelse --}} diff --git a/resources/views/contacts/index.blade.php b/resources/views/contacts/index.blade.php index 6d8c772..ea2cb4d 100644 --- a/resources/views/contacts/index.blade.php +++ b/resources/views/contacts/index.blade.php @@ -23,45 +23,72 @@

Contacts

-
-
- + @goto-page="fetchData($event.detail.page)" + @reload.window="fetchData()" + x-cloak> + +
+
- - - - From - Message - Action - - - -
-
-
{{ $contacts->links('flowcms::partials.tailwindPagination') }}
+
+ @include('flowcms::contacts._contacts') +
+
+ @endsection diff --git a/resources/views/front/articles/_articles.blade.php b/resources/views/front/articles/_articles.blade.php index d5c8a5b..7144425 100644 --- a/resources/views/front/articles/_articles.blade.php +++ b/resources/views/front/articles/_articles.blade.php @@ -1,50 +1,56 @@ -@forelse($articles as $article) -
- -
-
-
- {{ $article->category->name }} -
-
{{ $article->publish_date->format('d M Y') }}
-
-
By {{ Str::title($article->user->name) }}
-
+@if($articles->isNotEmpty()) + @foreach($articles as $article) +
+ + -
- Read more → +
+ Read more @svg(url('cms/icons/chevron-right.svg')) +
-
- @if($article->image) -
-
- - - + @if($article->image) +
+
+ + + - Article Image - + Article Image + +
-
- @endif - + @endif + +
+ @endforeach + +
+ {{ $articles->onEachSide(2)->links('flowcms::partials.tailwindPaginationAlpinejs') }}
-@empty +@else
No articles found.
-@endforelse +@endif diff --git a/resources/views/front/articles/index.blade.php b/resources/views/front/articles/index.blade.php index 495fe91..4350f87 100644 --- a/resources/views/front/articles/index.blade.php +++ b/resources/views/front/articles/index.blade.php @@ -9,32 +9,64 @@ -
+ }); + document.addEventListener('keypress',(e) => { + if (event.code == 'Slash') { + event.preventDefault(); + $refs.searchInput.focus(); + } + }); + " + @goto-page="fetchData($event.detail.page)" + x-cloak + @reload.window="fetchData()">
- @include('flowcms::front.articles.articlesLoader') + @include('flowcms::front.articles._articles')
- -
{{ $articles->links('flowcms::partials.tailwindPagination') }}
diff --git a/resources/views/front/articles/show.blade.php b/resources/views/front/articles/show.blade.php index b34ebea..dec32b9 100644 --- a/resources/views/front/articles/show.blade.php +++ b/resources/views/front/articles/show.blade.php @@ -15,116 +15,144 @@ @push('styles') @endpush + @push('scripts') - + @endpush + diff --git a/resources/views/partials/tailwindPaginationAlpinejs.blade.php b/resources/views/partials/tailwindPaginationAlpinejs.blade.php new file mode 100644 index 0000000..05467f9 --- /dev/null +++ b/resources/views/partials/tailwindPaginationAlpinejs.blade.php @@ -0,0 +1,72 @@ +@if ($paginator->hasPages()) +
+
+ +
+ @if ($paginator->onFirstPage()) + + + + Previous + + @else + + @endif +
+ + + +
+ @if ($paginator->hasMorePages()) + + @else + + Next + + + + @endif +
+
+
+
+ Showing {{ $paginator->firstItem() }} to {{ $paginator->lastItem() }} of {{ $paginator->total() }} results +
+
+
+@endif \ No newline at end of file diff --git a/resources/views/seo/index.blade.php b/resources/views/seo/index.blade.php index 59fbc6d..dcce8c4 100644 --- a/resources/views/seo/index.blade.php +++ b/resources/views/seo/index.blade.php @@ -48,7 +48,7 @@ class="bg-gray-800 text-white" @empty - No page found. + No page created yet. @endforelse @endsection diff --git a/routes/web.php b/routes/web.php index 6cdb446..2eaa6c6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -41,7 +41,7 @@ Route::group(['middleware' => 'HtmlMinifier'], function () { Route::get('/blog', 'Front\ArticleController@index')->name('blog'); Route::get('/blog/{article}', 'Front\ArticleController@show')->name('blog.show'); - Route::get('/partials/blog', 'Front\ArticleController@articles')->name('blog.partial'); + // Route::get('/partials/blog', 'Front\ArticleController@articles')->name('blog.partial'); }); // Contact us @@ -65,8 +65,8 @@ // Partial Views - Route::get('/partials/articles', 'ArticleController@articles'); - Route::get('/partials/contacts', 'ContactController@contacts'); + // Route::get('/partials/articles', 'ArticleController@articles'); + // Route::get('/partials/contacts', 'ContactController@contacts'); // Page Route::get('/pages', 'PageController@index')->name('pages.index'); @@ -101,7 +101,7 @@ // Media Route::get('/media', 'MediaController@index')->name('media'); - Route::get('/partials/media', 'MediaController@media'); + // Route::get('/partials/media', 'MediaController@media'); Route::post('/media/upload', 'MediaController@store')->name('media.store'); Route::post('/media/image/delete', 'MediaController@destroy')->name('media.destroy'); }); diff --git a/src/Controllers/ArticleController.php b/src/Controllers/ArticleController.php index b71e778..8708e51 100644 --- a/src/Controllers/ArticleController.php +++ b/src/Controllers/ArticleController.php @@ -13,26 +13,40 @@ class ArticleController extends Controller { - public function index() + public function index(Request $request) { - $articles = Article::with('category')->orderByDesc('publish_date')->paginate(10); + $article = Article::query(); - return view('flowcms::articles.index', compact('articles')); - } + if ($request->ajax()) { + if($search = request('s')) { + $article->where('title', 'like', '%' . $search . '%'); + } - public function articles() - { - $article = Article::with('category')->orderByDesc('publish_date'); + $articles = $article->latest('publish_date')->paginate(10); + $articles->load('category'); - if ($search = request('s')) { - $article->where('title', 'like', '%' . $search . '%'); + return view('flowcms::articles._articles', compact('articles'))->render(); } - $articles = $article->paginate(10); + $articles = $article->latest('publish_date')->paginate(10); + $articles->load('category'); - return view('flowcms::articles._articles', compact('articles')); + return view('flowcms::articles.index', compact('articles')); } + // public function articles() + // { + // $article = Article::with('category')->orderByDesc('publish_date'); + + // if ($search = request('s')) { + // $article->where('title', 'like', '%' . $search . '%'); + // } + + // $articles = $article->paginate(10); + + // return view('flowcms::articles._articles', compact('articles')); + // } + public function create() { $categories = Category::orderBy('name')->get(); diff --git a/src/Controllers/ContactController.php b/src/Controllers/ContactController.php index 3602d24..62a32d1 100644 --- a/src/Controllers/ContactController.php +++ b/src/Controllers/ContactController.php @@ -12,28 +12,32 @@ class ContactController extends Controller { use ValidatesRequests; - public function index() + public function index(Request $request) { - $contacts = Contact::latest()->paginate(10); + $contact = Contact::query(); - return view('flowcms::contacts.index', compact('contacts')); - } + if ($request->ajax()) { + if ($search = request('s')) { + $contact->where('name', 'like', '%' . $search . '%'); + $contact->orWhere('email', 'like', '%' . $search . '%'); + $contact->orWhere('body', 'like', '%' . $search . '%'); + } - public function contacts() - { - $contact = Contact::query(); + $contacts = $contact->latest()->paginate(10); - if ($search = request('s')) { - $contact->where('name', 'like', '%' . $search . '%'); - $contact->orWhere('email', 'like', '%' . $search . '%'); - $contact->orWhere('body', 'like', '%' . $search . '%'); + return view('flowcms::contacts._contacts', compact('contacts')); } $contacts = $contact->latest()->paginate(10); - return view('flowcms::contacts._contacts', compact('contacts')); + return view('flowcms::contacts.index', compact('contacts')); } + // public function contacts() + // { + + // } + public function store(Request $request) { $cmsblocks = collect(config('cms.blocks.contact_us'))->all(); diff --git a/src/Controllers/Front/ArticleController.php b/src/Controllers/Front/ArticleController.php index e34911c..fe18e03 100644 --- a/src/Controllers/Front/ArticleController.php +++ b/src/Controllers/Front/ArticleController.php @@ -21,6 +21,10 @@ public function index() ->paginate(10); }); + if (request()->ajax()) { + return view('flowcms::front.articles._articles', compact('articles'))->render(); + } + return view('flowcms::front.articles.index', compact('articles')); } @@ -37,7 +41,14 @@ public function show($article) return $articleFound; }); - return view('flowcms::front.articles.show', compact('article')); + $previous = Article::where('id', '<', $article->id)->orderBy('id','desc')->isPublished()->first(); + $next = Article::where('id', '>', $article->id)->orderBy('id')->isPublished()->first(); + + return view('flowcms::front.articles.show', [ + 'article' => $article, + 'previous' => $previous, + 'next' => $next + ]); } public function articles() diff --git a/src/Controllers/MediaController.php b/src/Controllers/MediaController.php index 1be3a5d..0856116 100644 --- a/src/Controllers/MediaController.php +++ b/src/Controllers/MediaController.php @@ -9,25 +9,36 @@ class MediaController extends Controller { - public function index() - { - $medias = Media::latest()->paginate(12); - return view('flowcms::media.index', compact('medias')); - } - - public function media(Request $request) + public function index(Request $request) { $media = Media::query(); - if ($search = request('s')) { - $media->where('name', 'like', '%' . $search . '%'); + if ($request->ajax()) { + if ($search = request('s')) { + $media->where('name', 'like', '%' . $search . '%'); + } + + $medias = $media->latest()->paginate(12); + return view('flowcms::media._media', compact('medias'))->render(); } $medias = $media->latest()->paginate(12); - - return view('flowcms::media._media', compact('medias')); + return view('flowcms::media.index', compact('medias')); } + // public function media(Request $request) + // { + // $media = Media::query(); + + // if ($search = request('s')) { + // $media->where('name', 'like', '%' . $search . '%'); + // } + + // $medias = $media->latest()->paginate(12); + + // return view('flowcms::media._media', compact('medias')); + // } + public function store(Request $request) { if ($request->has('file')) { diff --git a/src/Controllers/PageController.php b/src/Controllers/PageController.php index 1d2c992..62e5660 100644 --- a/src/Controllers/PageController.php +++ b/src/Controllers/PageController.php @@ -97,7 +97,8 @@ public function store(Request $request) $validated = $this->validate($request, [ 'title' => [ 'required', - Rule::unique('pages') + Rule::unique('pages'), + Rule::notIn(['home', 'Home', 'page', 'Page', 'blog', 'Blog', 'article', 'Article']) ] ]); diff --git a/src/FlowcmsServiceProvider.php b/src/FlowcmsServiceProvider.php index 76acdbf..3cde121 100755 --- a/src/FlowcmsServiceProvider.php +++ b/src/FlowcmsServiceProvider.php @@ -15,6 +15,7 @@ class FlowcmsServiceProvider extends ServiceProvider private const FLOWCOMPONENTS = [ "alert", "badge", + "base-datatable", "button", "card", "checkbox", diff --git a/src/Helpers/functions.php b/src/Helpers/functions.php index fed64e4..22002a1 100644 --- a/src/Helpers/functions.php +++ b/src/Helpers/functions.php @@ -53,12 +53,16 @@ function responsive_image($imageUrl) } if (collect([$imageSavedUrl, $imageSavedSecureUrl])->contains(request()->root()) && $storageFolderPath != 'cms') { - $imageUrl = explode(config('app.url') . '/storage/', $imageUrl); + if (Str::contains($imageUrl, 'storage')) { + $filename = explode(config('app.url') . '/storage/', $imageUrl)[1]; + } else { + $filename = explode(config('app.url') . '/', $imageUrl)[1]; + } return [ - 'small' => url('/img/' . $imageUrl[1] . '?w=320'), - 'medium' => url('/img/' . $imageUrl[1] . '?w=640'), - 'large' => url('/img/' . $imageUrl[1] . '?w=1024'), + 'small' => url('/img/' . $filename . '?w=320'), + 'medium' => url('/img/' . $filename . '?w=640'), + 'large' => url('/img/' . $filename . '?w=1024'), // 'xlarge' => url('/img/' . $imageUrl[1] .'?w=1200'), ]; } diff --git a/src/Models/Article.php b/src/Models/Article.php index f84c8db..ecf3683 100644 --- a/src/Models/Article.php +++ b/src/Models/Article.php @@ -3,6 +3,7 @@ namespace Flowcms\Flowcms\Models; use Illuminate\Support\Str; +use Flowcms\Flowcms\Traits\MoveUploadedFromTempToDestination; use Illuminate\Support\Facades\Cache; use Illuminate\Database\Eloquent\Model; use CyrildeWit\EloquentViewable\InteractsWithViews; @@ -10,7 +11,7 @@ class Article extends Model implements Viewable { - use InteractsWithViews; + use InteractsWithViews, MoveUploadedFromTempToDestination; /** * The attributes that are mass assignable. @@ -60,7 +61,8 @@ class Article extends Model implements Viewable 'article_summary', 'links', 'images', - 'article_with_responsive_images' + 'article_with_responsive_images', + 'article_views' ]; protected static function boot() @@ -111,6 +113,11 @@ public function getLinksAttribute() ]; } + public function getArticleViewsAttribute() + { + return views($this)->unique()->count() ?? 0; + } + public function getImagesAttribute() { if ($this->image === '') { diff --git a/src/Traits/MoveUploadedFromTempToDestination.php b/src/Traits/MoveUploadedFromTempToDestination.php new file mode 100644 index 0000000..4acad13 --- /dev/null +++ b/src/Traits/MoveUploadedFromTempToDestination.php @@ -0,0 +1,85 @@ +body, 'HTML-ENTITIES', 'UTF-8'); + + // creating new document + $doc = new \DOMDocument('1.0', 'UTF-8'); + + //turning off some errors + libxml_use_internal_errors(true); + + // root node is required + $doc->loadHTML('
' . $html . '
'); + + //turning off some errors + libxml_clear_errors(); + + $tags = $doc->getElementsByTagName('img'); + + foreach ($tags as $tag) { + $oldSrc = $tag->getAttribute('src'); + + if (Storage::disk('public')->exists($oldSrc)) { + $newSrc = '/editor-uploads/' . explode('/', $oldSrc)[2]; + + if (! Storage::disk('public')->exists($newSrc)) { + Storage::disk('public')->move($oldSrc, $newSrc); + } + + $tag->setAttribute('src', $newSrc); + $tag->setAttribute('loading', 'lazy'); + } + } + + $model->body = substr($doc->saveXML($doc->getElementsByTagName('div')->item(0)), 5, -6); + }); + + static::updating(function ($model) { + + // converts all special characters to utf-8 + $html = mb_convert_encoding($model->body, 'HTML-ENTITIES', 'UTF-8'); + + // creating new document + $doc = new \DOMDocument('1.0', 'UTF-8'); + + //turning off some errors + libxml_use_internal_errors(true); + + // root node is required + $doc->loadHTML('
' . $html . '
'); + + //turning off some errors + libxml_clear_errors(); + + $tags = $doc->getElementsByTagName('img'); + + foreach ($tags as $tag) { + $oldSrc = $tag->getAttribute('src'); + + if (Storage::disk('public')->exists($oldSrc)) { + $newSrc = '/editor-uploads/' . explode('/', $oldSrc)[2]; + + if (! Storage::disk('public')->exists($newSrc)) { + Storage::disk('public')->move($oldSrc, $newSrc); + } + + $tag->setAttribute('src', $newSrc); + $tag->setAttribute('loading', 'lazy'); + } + } + + $model->body = substr($doc->saveXML($doc->getElementsByTagName('div')->item(0)), 5, -6); + }); + } + } \ No newline at end of file