Passed
Pull Request — 2.x (#586)
by Bekzat
04:25
created

ModuleController::getRoutePrefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
ccs 3
cts 5
cp 0.6
crap 2.2559
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace A17\Twill\Http\Controllers\Admin;
4
5
use A17\Twill\Helpers\FlashLevel;
6
use Illuminate\Contracts\Foundation\Application;
7
use Illuminate\Http\Request;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Collection;
10
use Illuminate\Support\Facades\App;
11
use Illuminate\Support\Facades\Auth;
12
use Illuminate\Support\Facades\Config;
13
use Illuminate\Support\Facades\Redirect;
14
use Illuminate\Support\Facades\Response;
15
use Illuminate\Support\Facades\Route;
16
use Illuminate\Support\Facades\Session;
17
use Illuminate\Support\Facades\URL;
18
use Illuminate\Support\Facades\View;
19
use Illuminate\Support\Str;
20
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21
22
abstract class ModuleController extends Controller
23
{
24
    /**
25
     * @var Application
26
     */
27
    protected $app;
28
29
    /**
30
     * @var Request
31
     */
32
    protected $request;
33
34
    /**
35
     * @var string
36
     */
37
    protected $namespace;
38
39
    /**
40
     * @var string
41
     */
42
    protected $routePrefix;
43
44
    /**
45
     * @var string
46
     */
47
    protected $moduleName;
48
49
    /**
50
     * @var string
51
     */
52
    protected $modelName;
53
54
    /**
55
     * @var string
56
     */
57
    protected $modelTitle;
58
59
    /**
60
     * @var \A17\Twill\Repositories\ModuleRepository
61
     */
62
    protected $repository;
63
64
    /**
65
     * Options of the index view.
66
     *
67
     * @var array
68
     */
69
    protected $defaultIndexOptions = [
70
        'create' => true,
71
        'edit' => true,
72
        'publish' => true,
73
        'bulkPublish' => true,
74
        'feature' => false,
75
        'bulkFeature' => false,
76
        'restore' => true,
77
        'bulkRestore' => true,
78
        'forceDelete' => true,
79
        'bulkForceDelete' => true,
80
        'delete' => true,
81
        'duplicate' => false,
82
        'bulkDelete' => true,
83
        'reorder' => false,
84
        'permalink' => true,
85
        'bulkEdit' => true,
86
        'editInModal' => false,
87
    ];
88
89
    /**
90
     * Relations to eager load for the index view
91
     *
92
     * @var array
93
     */
94
    protected $indexWith = [];
95
96
    /**
97
     * Relations to eager load for the form view.
98
     *
99
     * @var array
100
     */
101
    protected $formWith = [];
102
103
    /**
104
     * Relation count to eager load for the form view.
105
     *
106
     * @var array
107
     */
108
    protected $formWithCount = [];
109
110
    /**
111
     * Additional filters for the index view.
112
     *
113
     * To automatically have your filter added to the index view use the following convention:
114
     * suffix the key containing the list of items to show in the filter by 'List' and
115
     * name it the same as the filter you defined in this array.
116
     *
117
     * Example: 'fCategory' => 'category_id' here and 'fCategoryList' in indexData()
118
     * By default, this will run a where query on the category_id column with the value
119
     * of fCategory if found in current request parameters. You can intercept this behavior
120
     * from your repository in the filter() function.
121
     *
122
     * @var array
123
     */
124
    protected $filters = [];
125
126
    /**
127
     * Additional links to display in the listing filter
128
     *
129
     * @var array
130
     */
131
    protected $filterLinks = [];
132
133
    /**
134
     * Filters that are selected by default in the index view.
135
     *
136
     * Example: 'filter_key' => 'default_filter_value'
137
     *
138
     * @var array
139
     */
140
    protected $filtersDefaultOptions = [];
141
142
    /**
143
     * Default orders for the index view.
144
     *
145
     * @var array
146
     */
147
    protected $defaultOrders = [
148
        'created_at' => 'desc',
149
    ];
150
151
    /**
152
     * @var int
153
     */
154
    protected $perPage = 20;
155
156
    /**
157
     * Name of the index column to use as name column.
158
     *
159
     * @var string
160
     */
161
    protected $titleColumnKey = 'title';
162
163
    /**
164
     * Attribute to use as title in forms.
165
     *
166
     * @var string
167
     */
168
    protected $titleFormKey;
169
170
    /**
171
     * Feature field name if the controller is using the feature route (defaults to "featured").
172
     *
173
     * @var string
174
     */
175
    protected $featureField = 'featured';
176
177
    /**
178
     * Indicates if this module is edited through a parent module.
179
     *
180
     * @var bool
181
     */
182
    protected $submodule = false;
183
184
    /**
185
     * @var int|null
186
     */
187
    protected $submoduleParentId = null;
188
189
    /**
190
     * Can be used in child classes to disable the content editor (full screen block editor).
191
     *
192
     * @var bool
193
     */
194
    protected $disableEditor = false;
195
196
    /**
197
     * @var array
198
     */
199
    protected $indexOptions;
200
201
    /**
202
     * @var array
203
     */
204
    protected $indexColumns;
205
206
    /**
207
     * @var array
208
     */
209
    protected $browserColumns;
210
211
    /**
212
     * @var string
213
     */
214
    protected $permalinkBase;
215
216
    /**
217
     * @var array
218
     */
219
    protected $defaultFilters;
220
221
    /**
222
     * @var string
223
     */
224
    protected $viewPrefix;
225
226
    /**
227
     * @var string
228
     */
229
    protected $previewView;
230
231
    /**
232
     * List of permissions keyed by a request field. Can be used to prevent unauthorized field updates.
233
     *
234
     * @var array
235
     */
236
    protected $fieldsPermissions = [];
237
238 1
    public function __construct(Application $app, Request $request)
239
    {
240 1
        parent::__construct();
241 1
        $this->app = $app;
242 1
        $this->request = $request;
243
244 1
        $this->setMiddlewarePermission();
245
246 1
        $this->modelName = $this->getModelName();
247 1
        $this->routePrefix = $this->getRoutePrefix();
248 1
        $this->namespace = $this->getNamespace();
249 1
        $this->repository = $this->getRepository();
250 1
        $this->viewPrefix = $this->getViewPrefix();
251 1
        $this->modelTitle = $this->getModelTitle();
252
253
        /*
254
         * Default filters for the index view
255
         * By default, the search field will run a like query on the title field
256
         */
257 1
        if (!isset($this->defaultFilters)) {
258 1
            $this->defaultFilters = [
259 1
                'search' => ($this->moduleHas('translations') ? '' : '%') . $this->titleColumnKey,
260
            ];
261
        }
262
263
        /*
264
         * Apply any filters that are selected by default
265
         */
266 1
        $this->applyFiltersDefaultOptions();
267
268
        /*
269
         * Available columns of the index view
270
         */
271 1
        if (!isset($this->indexColumns)) {
272 1
            $this->indexColumns = [
273 1
                $this->titleColumnKey => [
274 1
                    'title' => ucfirst($this->titleColumnKey),
275 1
                    'field' => $this->titleColumnKey,
276
                    'sort' => true,
277
                ],
278
            ];
279
        }
280
281
        /*
282
         * Available columns of the browser view
283
         */
284 1
        if (!isset($this->browserColumns)) {
285 1
            $this->browserColumns = [
286 1
                $this->titleColumnKey => [
287 1
                    'title' => ucfirst($this->titleColumnKey),
288 1
                    'field' => $this->titleColumnKey,
289
                ],
290
            ];
291
        }
292 1
    }
293
294
    /**
295
     * @return void
296
     */
297 1
    protected function setMiddlewarePermission()
298
    {
299 1
        $this->middleware('can:list', ['only' => ['index', 'show']]);
300 1
        $this->middleware('can:edit', ['only' => ['store', 'edit', 'update']]);
301 1
        $this->middleware('can:duplicate', ['only' => ['duplicate']]);
302 1
        $this->middleware('can:publish', ['only' => ['publish', 'feature', 'bulkPublish', 'bulkFeature']]);
303 1
        $this->middleware('can:reorder', ['only' => ['reorder']]);
304 1
        $this->middleware('can:delete', ['only' => ['destroy', 'bulkDelete', 'restore', 'bulkRestore', 'forceDelete', 'bulkForceDelete', 'restoreRevision']]);
305 1
    }
306
307
    /**
308
     * @param int|null $parentModuleId
309
     * @return array|\Illuminate\View\View
310
     */
311
    public function index($parentModuleId = null)
312
    {
313
        $this->submodule = isset($parentModuleId);
314
        $this->submoduleParentId = $parentModuleId;
315
316
        $indexData = $this->getIndexData($this->submodule ? [
317
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
318
        ] : []);
319
320
        if ($this->request->ajax()) {
321
            return $indexData + ['replaceUrl' => true];
322
        }
323
324
        if ($this->request->has('openCreate') && $this->request->get('openCreate')) {
325
            $indexData += ['openCreate' => true];
326
        }
327
328
        $view = Collection::make([
329
            "$this->viewPrefix.index",
330
            "twill::$this->moduleName.index",
331
            "twill::layouts.listing",
332
        ])->first(function ($view) {
333
            return View::exists($view);
334
        });
335
336
        return View::make($view, $indexData);
337
    }
338
339
    /**
340
     * @return \Illuminate\Http\JsonResponse
341
     */
342
    public function browser()
343
    {
344
        return Response::json($this->getBrowserData());
345
    }
346
347
    /**
348
     * @param int|null $parentModuleId
349
     * @return \Illuminate\Http\JsonResponse
350
     */
351
    public function store($parentModuleId = null)
352
    {
353
        $input = $this->validateFormRequest()->all();
354
        $optionalParent = $parentModuleId ? [$this->getParentModuleForeignKey() => $parentModuleId] : [];
355
356
        $item = $this->repository->create($input + $optionalParent);
357
358
        activity()->performedOn($item)->log('created');
359
360
        $this->fireEvent($input);
361
362
        Session::put($this->moduleName . '_retain', true);
363
364
        if ($this->getIndexOption('editInModal')) {
365
            return $this->respondWithSuccess('Content saved. All good!');
366
        }
367
368
        if ($parentModuleId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parentModuleId of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
369
            $params = [
370
                Str::singular(explode('.', $this->moduleName)[0]) => $parentModuleId,
371
                Str::singular(explode('.', $this->moduleName)[1]) => $item->id,
372
            ];
373
        } else {
374
            $params = [
375
                Str::singular($this->moduleName) => $item->id,
376
            ];
377
        }
378
379
        return $this->respondWithRedirect(moduleRoute(
380
            $this->moduleName,
381
            $this->routePrefix,
382
            'edit',
383
            $params
384
        ));
385
    }
386
387
    /**
388
     * @param int|$id
389
     * @param int|null $submoduleId
390
     * @return \Illuminate\Http\RedirectResponse
391
     */
392
    public function show($id, $submoduleId = null)
393
    {
394
        if ($this->getIndexOption('editInModal')) {
395
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
396
        }
397
398
        return $this->redirectToForm($submoduleId ?? $id);
399
    }
400
401
    /**
402
     * @param int $id
403
     * @param int|null $submoduleId
404
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
405
     */
406
    public function edit($id, $submoduleId = null)
407
    {
408
        $this->submodule = isset($submoduleId);
409
        $this->submoduleParentId = $id;
410
411
        if ($this->getIndexOption('editInModal')) {
412
            return $this->request->ajax()
413
            ? Response::json($this->modalFormData($submoduleId ?? $id))
414
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
415
        }
416
417
        $this->setBackLink();
418
419
        $view = Collection::make([
420
            "$this->viewPrefix.form",
421
            "twill::$this->moduleName.form",
422
            "twill::layouts.form",
423
        ])->first(function ($view) {
424
            return View::exists($view);
425
        });
426
427
        return View::make($view, $this->form($submoduleId ?? $id));
428
    }
429
430
    /**
431
     * @param int $id
432
     * @param int|null $submoduleId
433
     * @return \Illuminate\Http\JsonResponse
434
     */
435
    public function update($id, $submoduleId = null)
436
    {
437
        $this->submodule = isset($submoduleId);
438
        $this->submoduleParentId = $id;
439
440
        $item = $this->repository->getById($submoduleId ?? $id);
441
        $input = $this->request->all();
442
443
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
444
            return $this->respondWithRedirect(moduleRoute(
445
                $this->moduleName,
446
                $this->routePrefix,
447
                'edit',
448
                [Str::singular($this->moduleName) => $id]
449
            ));
450
        } else {
451
            $formRequest = $this->validateFormRequest();
452
453
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
454
455
            activity()->performedOn($item)->log('updated');
456
457
            $this->fireEvent();
458
459
            if (isset($input['cmsSaveType'])) {
460
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
461
                    return $this->respondWithRedirect($this->getBackLink());
462
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
463
                    return $this->respondWithRedirect(moduleRoute(
464
                        $this->moduleName,
465
                        $this->routePrefix,
466
                        'index',
467
                        ['openCreate' => true]
468
                    ));
469
                } elseif ($input['cmsSaveType'] === 'restore') {
470
                    Session::flash('status', "Revision restored.");
471
472
                    return $this->respondWithRedirect(moduleRoute(
473
                        $this->moduleName,
474
                        $this->routePrefix,
475
                        'edit',
476
                        [Str::singular($this->moduleName) => $id]
477
                    ));
478
                }
479
            }
480
481
            if ($this->moduleHas('revisions')) {
482
                return Response::json([
483
                    'message' => 'Content saved. All good!',
484
                    'variant' => FlashLevel::SUCCESS,
485
                    'revisions' => $item->revisionsArray(),
486
                ]);
487
            }
488
489
            return $this->respondWithSuccess('Content saved. All good!');
490
        }
491
    }
492
493
    /**
494
     * @param int $id
495
     * @return \Illuminate\View\View
496
     */
497
    public function preview($id)
498
    {
499
        if ($this->request->has('revisionId')) {
500
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
0 ignored issues
show
Bug introduced by
The method previewForRevision() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

500
            /** @scrutinizer ignore-call */ 
501
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
Loading history...
501
        } else {
502
            $formRequest = $this->validateFormRequest();
503
            $item = $this->repository->preview($id, $formRequest->all());
0 ignored issues
show
Bug introduced by
The method preview() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

503
            /** @scrutinizer ignore-call */ 
504
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
504
        }
505
506
        if ($this->request->has('activeLanguage')) {
507
            App::setLocale($this->request->get('activeLanguage'));
508
        }
509
510
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
511
512
        return View::exists($previewView) ? View::make($previewView, array_replace([
513
            'item' => $item,
514
        ], $this->previewData($item))) : View::make('twill::errors.preview', [
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $item of A17\Twill\Http\Controlle...ntroller::previewData() does only seem to accept Illuminate\Http\Request, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

514
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
515
            'moduleName' => Str::singular($this->moduleName),
516
        ]);
517
    }
518
519
    /**
520
     * @param int $id
521
     * @return \Illuminate\View\View
522
     */
523
    public function restoreRevision($id)
524
    {
525
        if ($this->request->has('revisionId')) {
526
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
527
            $item->id = $id;
0 ignored issues
show
Bug introduced by
The property id does not seem to exist on Illuminate\Database\Eloquent\Builder.
Loading history...
528
            $item->cmsRestoring = true;
0 ignored issues
show
Bug introduced by
The property cmsRestoring does not seem to exist on Illuminate\Database\Eloquent\Builder.
Loading history...
529
        } else {
530
            throw new NotFoundHttpException();
531
        }
532
533
        $this->setBackLink();
534
535
        $view = Collection::make([
536
            "$this->viewPrefix.form",
537
            "twill::$this->moduleName.form",
538
            "twill::layouts.form",
539
        ])->first(function ($view) {
540
            return View::exists($view);
541
        });
542
543
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
544
        $date = $revision->created_at->toDayDateTimeString();
0 ignored issues
show
Bug introduced by
The method toDayDateTimeString() does not exist on DateTime. It seems like you code against a sub-type of DateTime such as Carbon\Carbon. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

544
        /** @scrutinizer ignore-call */ 
545
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
545
546
        Session::flash('restoreMessage', "You are currently editing an older revision of this content (saved by $revision->byUser on $date). Make changes if needed and click restore to save a new revision.");
0 ignored issues
show
Bug introduced by
The property byUser does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
547
548
        return View::make($view, $this->form($id, $item));
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $item of A17\Twill\Http\Controlle...oduleController::form() does only seem to accept A17\Twill\Models\Model|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

548
        return View::make($view, $this->form($id, /** @scrutinizer ignore-type */ $item));
Loading history...
549
    }
550
551
    /**
552
     * @return \Illuminate\Http\JsonResponse
553
     */
554
    public function publish()
555
    {
556
        try {
557
            if ($this->repository->updateBasic($this->request->get('id'), [
558
                'published' => !$this->request->get('active'),
559
            ])) {
560
                activity()->performedOn(
561
                    $this->repository->getById($this->request->get('id'))
562
                )->log(
563
                    ($this->request->get('active') ? 'un' : '') . 'published'
564
                );
565
566
                $this->fireEvent();
567
568
                return $this->respondWithSuccess(
569
                    $this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'published!'
570
                );
571
            }
572
        } catch (\Exception $e) {
573
            \Log::error($e);
574
        }
575
576
        return $this->respondWithError(
577
            $this->modelTitle . ' was not published. Something wrong happened!'
578
        );
579
    }
580
581
    /**
582
     * @return \Illuminate\Http\JsonResponse
583
     */
584
    public function bulkPublish()
585
    {
586
        try {
587
            if ($this->repository->updateBasic(explode(',', $this->request->get('ids')), [
588
                'published' => $this->request->get('publish'),
589
            ])) {
590
                $this->fireEvent();
591
592
                return $this->respondWithSuccess(
593
                    $this->modelTitle . ' items ' . ($this->request->get('publish') ? '' : 'un') . 'published!'
594
                );
595
            }
596
        } catch (\Exception $e) {
597
            \Log::error($e);
598
        }
599
600
        return $this->respondWithError(
601
            $this->modelTitle . ' items were not published. Something wrong happened!'
602
        );
603
    }
604
605
    /**
606
     * @param int $id
607
     * @param int|null $submoduleId
608
     * @return \Illuminate\Http\JsonResponse
609
     */
610
    public function duplicate($id, $submoduleId = null)
611
    {
612
613
        $item = $this->repository->getById($submoduleId ?? $id);
614
        if ($newItem = $this->repository->duplicate($submoduleId ?? $id)) {
615
            $this->fireEvent();
616
            activity()->performedOn($item)->log('duplicated');
617
618
            return Response::json([
619
                'message' => $this->modelTitle . ' duplicated with Success!',
620
                'variant' => FlashLevel::SUCCESS,
621
                'redirect' => moduleRoute(
622
                    $this->moduleName,
623
                    $this->routePrefix,
624
                    'edit',
625
                    array_filter([Str::singular($this->moduleName) => $newItem->id])
626
                ),
627
            ]);
628
        }
629
630
        return $this->respondWithError($this->modelTitle . ' was not duplicated. Something wrong happened!');
631
    }
632
633
    /**
634
     * @param int $id
635
     * @param int|null $submoduleId
636
     * @return \Illuminate\Http\JsonResponse
637
     */
638
    public function destroy($id, $submoduleId = null)
639
    {
640
        $item = $this->repository->getById($submoduleId ?? $id);
641
        if ($this->repository->delete($submoduleId ?? $id)) {
642
            $this->fireEvent();
643
            activity()->performedOn($item)->log('deleted');
644
            return $this->respondWithSuccess($this->modelTitle . ' moved to trash!');
645
        }
646
647
        return $this->respondWithError($this->modelTitle . ' was not moved to trash. Something wrong happened!');
648
    }
649
650
    /**
651
     * @return \Illuminate\Http\JsonResponse
652
     */
653
    public function bulkDelete()
654
    {
655
        if ($this->repository->bulkDelete(explode(',', $this->request->get('ids')))) {
656
            $this->fireEvent();
657
            return $this->respondWithSuccess($this->modelTitle . ' items moved to trash!');
658
        }
659
660
        return $this->respondWithError($this->modelTitle . ' items were not moved to trash. Something wrong happened!');
661
    }
662
663
    /**
664
     * @return \Illuminate\Http\JsonResponse
665
     */
666
    public function forceDelete()
667
    {
668
        if ($this->repository->forceDelete($this->request->get('id'))) {
669
            $this->fireEvent();
670
            return $this->respondWithSuccess($this->modelTitle . ' destroyed!');
671
        }
672
673
        return $this->respondWithError($this->modelTitle . ' was not destroyed. Something wrong happened!');
674
    }
675
676
    /**
677
     * @return \Illuminate\Http\JsonResponse
678
     */
679
    public function bulkForceDelete()
680
    {
681
        if ($this->repository->bulkForceDelete(explode(',', $this->request->get('ids')))) {
682
            $this->fireEvent();
683
            return $this->respondWithSuccess($this->modelTitle . ' items destroyed!');
684
        }
685
686
        return $this->respondWithError($this->modelTitle . ' items were not destroyed. Something wrong happened!');
687
    }
688
689
    /**
690
     * @return \Illuminate\Http\JsonResponse
691
     */
692
    public function restore()
693
    {
694
        if ($this->repository->restore($this->request->get('id'))) {
695
            $this->fireEvent();
696
            activity()->performedOn($this->repository->getById($this->request->get('id')))->log('restored');
697
            return $this->respondWithSuccess($this->modelTitle . ' restored!');
698
        }
699
700
        return $this->respondWithError($this->modelTitle . ' was not restored. Something wrong happened!');
701
    }
702
703
    /**
704
     * @return \Illuminate\Http\JsonResponse
705
     */
706
    public function bulkRestore()
707
    {
708
        if ($this->repository->bulkRestore(explode(',', $this->request->get('ids')))) {
709
            $this->fireEvent();
710
            return $this->respondWithSuccess($this->modelTitle . ' items restored!');
711
        }
712
713
        return $this->respondWithError($this->modelTitle . ' items were not restored. Something wrong happened!');
714
    }
715
716
    /**
717
     * @return \Illuminate\Http\JsonResponse
718
     */
719
    public function feature()
720
    {
721
        if (($id = $this->request->get('id'))) {
722
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
723
            $featured = !$this->request->get('active');
724
725
            if ($this->repository->isUniqueFeature()) {
726
                if ($featured) {
727
                    $this->repository->updateBasic(null, [$featuredField => false]);
728
                    $this->repository->updateBasic($id, [$featuredField => $featured]);
729
                }
730
            } else {
731
                $this->repository->updateBasic($id, [$featuredField => $featured]);
732
            }
733
734
            activity()->performedOn(
735
                $this->repository->getById($id)
736
            )->log(
737
                ($this->request->get('active') ? 'un' : '') . 'featured'
738
            );
739
740
            $this->fireEvent();
741
            return $this->respondWithSuccess($this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'featured!');
742
        }
743
744
        return $this->respondWithError($this->modelTitle . ' was not featured. Something wrong happened!');
745
    }
746
747
    /**
748
     * @return \Illuminate\Http\JsonResponse
749
     */
750
    public function bulkFeature()
751
    {
752
        if (($ids = explode(',', $this->request->get('ids')))) {
753
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
754
            $featured = $this->request->get('feature') ?? true;
755
            // we don't need to check if unique feature since bulk operation shouldn't be allowed in this case
756
            $this->repository->updateBasic($ids, [$featuredField => $featured]);
757
            $this->fireEvent();
758
            return $this->respondWithSuccess($this->modelTitle . ' items ' . ($this->request->get('feature') ? '' : 'un') . 'featured!');
759
        }
760
761
        return $this->respondWithError($this->modelTitle . ' items were not featured. Something wrong happened!');
762
    }
763
764
    /**
765
     * @return \Illuminate\Http\JsonResponse
766
     */
767
    public function reorder()
768
    {
769
        if (($values = $this->request->get('ids')) && !empty($values)) {
770
            $this->repository->setNewOrder($values);
771
            $this->fireEvent();
772
            return $this->respondWithSuccess($this->modelTitle . ' order changed!');
773
        }
774
775
        return $this->respondWithError($this->modelTitle . ' order was not changed. Something wrong happened!');
776
    }
777
778
    /**
779
     * @return \Illuminate\Http\JsonResponse
780
     */
781
    public function tags()
782
    {
783
        $query = $this->request->input('q');
784
        $tags = $this->repository->getTags($query);
0 ignored issues
show
Bug introduced by
The method getTags() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

784
        /** @scrutinizer ignore-call */ 
785
        $tags = $this->repository->getTags($query);
Loading history...
785
786
        return Response::json(['items' => $tags->map(function ($tag) {
787
            return $tag->name;
788
        })], 200);
789
    }
790
791
    /**
792
     * @param array $prependScope
793
     * @return array
794
     */
795
    protected function getIndexData($prependScope = [])
796
    {
797
        $scopes = $this->filterScope($prependScope);
798
        $items = $this->getIndexItems($scopes);
799
800
        $data = [
801
            'tableData' => $this->getIndexTableData($items),
802
            'tableColumns' => $this->getIndexTableColumns($items),
803
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
804
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
805
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
806
            'filterLinks' => $this->filterLinks ?? [],
807
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
808
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
809
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
810
            'defaultOffset' => $this->perPage,
811
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
812
813
        $baseUrl = $this->getPermalinkBaseUrl();
814
815
        $options = [
816
            'moduleName' => $this->moduleName,
817
            'reorder' => $this->getIndexOption('reorder'),
818
            'create' => $this->getIndexOption('create'),
819
            'duplicate' => $this->getIndexOption('duplicate'),
820
            'translate' => $this->moduleHas('translations'),
821
            'translateTitle' => $this->titleIsTranslatable(),
822
            'permalink' => $this->getIndexOption('permalink'),
823
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
824
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
825
            'baseUrl' => $baseUrl,
826
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
827
        ];
828
829
        return array_replace_recursive($data + $options, $this->indexData($this->request));
830
    }
831
832
    /**
833
     * @param Request $request
834
     * @return array
835
     */
836
    protected function indexData($request)
837
    {
838
        return [];
839
    }
840
841
    /**
842
     * @param array $scopes
843
     * @param bool $forcePagination
844
     * @return \Illuminate\Database\Eloquent\Collection
845
     */
846
    protected function getIndexItems($scopes = [], $forcePagination = false)
847
    {
848
        return $this->transformIndexItems($this->repository->get(
849
            $this->indexWith,
850
            $scopes,
851
            $this->orderScope(),
852
            $this->request->get('offset') ?? $this->perPage ?? 50,
853
            $forcePagination
854
        ));
855
    }
856
857
    /**
858
     * @param \Illuminate\Database\Eloquent\Collection $items
859
     * @return \Illuminate\Database\Eloquent\Collection
860
     */
861
    protected function transformIndexItems($items)
862
    {
863
        return $items;
864
    }
865
866
    /**
867
     * @param \Illuminate\Database\Eloquent\Collection $items
868
     * @return array
869
     */
870
    protected function getIndexTableData($items)
871
    {
872
        $translated = $this->moduleHas('translations');
873
        return $items->map(function ($item) use ($translated) {
874
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
875
                return $this->getItemColumnData($item, $column);
876
            })->toArray();
877
878
            $name = $columnsData[$this->titleColumnKey];
879
880
            if (empty($name)) {
881
                if ($this->moduleHas('translations')) {
882
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
883
884
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
885
                        $name = $fallBackTranslation->{$this->titleColumnKey};
886
                    }
887
                }
888
889
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
890
            }
891
892
            unset($columnsData[$this->titleColumnKey]);
893
894
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
895
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
896
            $canEdit = $this->getIndexOption('edit');
897
            $canDuplicate = $this->getIndexOption('duplicate');
898
899
            return array_replace([
900
                'id' => $item->id,
901
                'name' => $name,
902
                'publish_start_date' => $item->publish_start_date,
903
                'publish_end_date' => $item->publish_end_date,
904
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
905
                'duplicate' => $canDuplicate ? $this->getModuleRoute($item->id, 'duplicate') : null,
906
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
907
            ] + ($this->getIndexOption('editInModal') ? [
908
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
909
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
910
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
911
                'published' => $item->published,
912
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
913
                'featured' => $item->{$this->featureField},
914
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
915
                'deleted' => true,
916
            ] : []) + ($translated ? [
917
                'languages' => $item->getActiveLanguages(),
918
            ] : []) + $columnsData, $this->indexItemData($item));
919
        })->toArray();
920
    }
921
922
    /**
923
     * @param \A17\Twill\Models\Model $item
924
     * @return array
925
     */
926
    protected function indexItemData($item)
927
    {
928
        return [];
929
    }
930
931
    /**
932
     * @param \A17\Twill\Models\Model $item
933
     * @param array $column
934
     * @return array
935
     */
936
    protected function getItemColumnData($item, $column)
937
    {
938
        if (isset($column['thumb']) && $column['thumb']) {
939
            if (isset($column['present']) && $column['present']) {
940
                return [
941
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
942
                ];
943
            } else {
944
                $variant = isset($column['variant']);
945
                $role = $variant ? $column['variant']['role'] : head(array_keys($item->mediasParams));
0 ignored issues
show
Bug introduced by
The property mediasParams does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
946
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
947
                $params = $variant && isset($column['variant']['params'])
948
                ? $column['variant']['params']
949
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
950
951
                return [
952
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
953
                ];
954
            }
955
        }
956
957
        if (isset($column['nested']) && $column['nested']) {
958
            $field = $column['nested'];
959
            $nestedCount = $item->{$column['nested']}->count();
960
            $value = '<a href="';
961
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
962
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
963
                ? Str::plural($column['title'])
964
                : Str::singular($column['title']))) . '</a>';
965
        } else {
966
            $field = $column['field'];
967
            $value = $item->$field;
968
        }
969
970
        if (isset($column['relationship'])) {
971
            $field = $column['relationship'] . ucfirst($column['field']);
972
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
973
        } elseif (isset($column['present']) && $column['present']) {
974
            $value = $item->presentAdmin()->{$column['field']};
975
        }
976
977
        return [
978
            "$field" => $value,
979
        ];
980
    }
981
982
    /**
983
     * @param \Illuminate\Database\Eloquent\Collection $items
984
     * @return array
985
     */
986
    protected function getIndexTableColumns($items)
0 ignored issues
show
Unused Code introduced by
The parameter $items is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

986
    protected function getIndexTableColumns(/** @scrutinizer ignore-unused */ $items)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
987
    {
988
        $tableColumns = [];
989
        $visibleColumns = $this->request->get('columns') ?? false;
990
991
        if (isset(Arr::first($this->indexColumns)['thumb'])
992
            && Arr::first($this->indexColumns)['thumb']
993
        ) {
994
            array_push($tableColumns, [
995
                'name' => 'thumbnail',
996
                'label' => 'Thumbnail',
997
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
998
                'optional' => true,
999
                'sortable' => false,
1000
            ]);
1001
            array_shift($this->indexColumns);
1002
        }
1003
1004
        if ($this->getIndexOption('feature')) {
1005
            array_push($tableColumns, [
1006
                'name' => 'featured',
1007
                'label' => 'Featured',
1008
                'visible' => true,
1009
                'optional' => false,
1010
                'sortable' => false,
1011
            ]);
1012
        }
1013
1014
        if ($this->getIndexOption('publish')) {
1015
            array_push($tableColumns, [
1016
                'name' => 'published',
1017
                'label' => 'Published',
1018
                'visible' => true,
1019
                'optional' => false,
1020
                'sortable' => false,
1021
            ]);
1022
        }
1023
1024
        array_push($tableColumns, [
1025
            'name' => 'name',
1026
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? 'Name',
1027
            'visible' => true,
1028
            'optional' => false,
1029
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
1030
        ]);
1031
1032
        unset($this->indexColumns[$this->titleColumnKey]);
1033
1034
        foreach ($this->indexColumns as $column) {
1035
            $columnName = isset($column['relationship'])
1036
            ? $column['relationship'] . ucfirst($column['field'])
1037
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
1038
1039
            array_push($tableColumns, [
1040
                'name' => $columnName,
1041
                'label' => $column['title'],
1042
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
1043
                'optional' => $column['optional'] ?? true,
1044
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
1045
                'html' => $column['html'] ?? false,
1046
            ]);
1047
        }
1048
1049
        if ($this->moduleHas('translations')) {
1050
            array_push($tableColumns, [
1051
                'name' => 'languages',
1052
                'label' => twillTrans('twill::lang.listing.languages'),
1053
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
1054
                'optional' => true,
1055
                'sortable' => false,
1056
            ]);
1057
        }
1058
1059
        return $tableColumns;
1060
    }
1061
1062
    /**
1063
     * @param \Illuminate\Database\Eloquent\Collection $items
1064
     * @param array $scopes
1065
     * @return array
1066
     */
1067
    protected function getIndexTableMainFilters($items, $scopes = [])
1068
    {
1069
        $statusFilters = [];
1070
1071
        $scope = ($this->submodule ? [
1072
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
1073
        ] : []) + $scopes;
1074
1075
        array_push($statusFilters, [
1076
            'name' => twillTrans('twill::lang.listing.filter.all-items'),
1077
            'slug' => 'all',
1078
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
1079
        ]);
1080
1081
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
1082
            array_push($statusFilters, [
1083
                'name' => twillTrans('twill::lang.listing.filter.mine'),
1084
                'slug' => 'mine',
1085
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
1086
            ]);
1087
        }
1088
1089
        if ($this->getIndexOption('publish')) {
1090
            array_push($statusFilters, [
1091
                'name' => twillTrans('twill::lang.listing.filter.published'),
1092
                'slug' => 'published',
1093
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
1094
            ], [
1095
                'name' => twillTrans('twill::lang.listing.filter.draft'),
1096
                'slug' => 'draft',
1097
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
1098
            ]);
1099
        }
1100
1101
        if ($this->getIndexOption('restore')) {
1102
            array_push($statusFilters, [
1103
                'name' => twillTrans('twill::lang.listing.filter.trash'),
1104
                'slug' => 'trash',
1105
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1106
            ]);
1107
        }
1108
1109
        return $statusFilters;
1110
    }
1111
1112
    /**
1113
     * @param string $moduleName
1114
     * @param string $routePrefix
1115
     * @return array
1116
     */
1117
    protected function getIndexUrls($moduleName, $routePrefix)
0 ignored issues
show
Unused Code introduced by
The parameter $moduleName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1117
    protected function getIndexUrls(/** @scrutinizer ignore-unused */ $moduleName, $routePrefix)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $routePrefix is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1117
    protected function getIndexUrls($moduleName, /** @scrutinizer ignore-unused */ $routePrefix)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1118
    {
1119
        return Collection::make([
1120
            'store',
1121
            'publish',
1122
            'bulkPublish',
1123
            'restore',
1124
            'bulkRestore',
1125
            'forceDelete',
1126
            'bulkForceDelete',
1127
            'reorder',
1128
            'feature',
1129
            'bulkFeature',
1130
            'bulkDelete',
1131
        ])->mapWithKeys(function ($endpoint) {
1132
            return [
1133
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1134
                    $this->moduleName, $this->routePrefix, $endpoint,
1135
                    $this->submodule ? [$this->submoduleParentId] : []
1136
                ) : null,
1137
            ];
1138
        })->toArray();
1139
    }
1140
1141
    /**
1142
     * @param string $option
1143
     * @return bool
1144
     */
1145
    protected function getIndexOption($option)
1146
    {
1147
        return once(function () use ($option) {
1148
            $customOptionNamesMapping = [
1149
                'store' => 'create',
1150
            ];
1151
1152
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1153
1154
            $authorizableOptions = [
1155
                'create' => 'edit',
1156
                'edit' => 'edit',
1157
                'publish' => 'publish',
1158
                'feature' => 'feature',
1159
                'reorder' => 'reorder',
1160
                'delete' => 'delete',
1161
                'duplicate' => 'duplicate',
1162
                'restore' => 'delete',
1163
                'forceDelete' => 'delete',
1164
                'bulkForceDelete' => 'delete',
1165
                'bulkPublish' => 'publish',
1166
                'bulkRestore' => 'delete',
1167
                'bulkFeature' => 'feature',
1168
                'bulkDelete' => 'delete',
1169
                'bulkEdit' => 'edit',
1170
                'editInModal' => 'edit',
1171
            ];
1172
1173
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1174
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1175
        });
1176
    }
1177
1178
    /**
1179
     * @param array $prependScope
1180
     * @return array
1181
     */
1182
    protected function getBrowserData($prependScope = [])
1183
    {
1184
        if ($this->request->has('except')) {
1185
            $prependScope['exceptIds'] = $this->request->get('except');
1186
        }
1187
1188
        $scopes = $this->filterScope($prependScope);
1189
        $items = $this->getBrowserItems($scopes);
1190
        $data = $this->getBrowserTableData($items);
1191
1192
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1193
    }
1194
1195
    /**
1196
     * @param \Illuminate\Database\Eloquent\Collection $items
1197
     * @return array
1198
     */
1199
    protected function getBrowserTableData($items)
1200
    {
1201
        $withImage = $this->moduleHas('medias');
1202
1203
        return $items->map(function ($item) use ($withImage) {
1204
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item) {
1205
                return $this->getItemColumnData($item, $column);
1206
            })->toArray();
1207
1208
            $name = $columnsData[$this->titleColumnKey];
1209
            unset($columnsData[$this->titleColumnKey]);
1210
1211
            return [
1212
                'id' => $item->id,
1213
                'name' => $name,
1214
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1215
                'endpointType' => $this->repository->getMorphClass(),
0 ignored issues
show
Bug introduced by
The method getMorphClass() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1215
                'endpointType' => $this->repository->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
1216
            ] + $columnsData + ($withImage && !array_key_exists('thumbnail', $columnsData) ? [
1217
                'thumbnail' => $item->defaultCmsImage(['w' => 100, 'h' => 100]),
1218
            ] : []);
1219
        })->toArray();
1220
    }
1221
1222
    /**
1223
     * @param array $scopes
1224
     * @return \Illuminate\Database\Eloquent\Collection
1225
     */
1226
    protected function getBrowserItems($scopes = [])
1227
    {
1228
        return $this->getIndexItems($scopes, true);
1229
    }
1230
1231
    /**
1232
     * @param array $prepend
1233
     * @return array
1234
     */
1235
    protected function filterScope($prepend = [])
1236
    {
1237
        $scope = [];
1238
1239
        $requestFilters = $this->getRequestFilters();
1240
1241
        $this->filters = array_merge($this->filters, $this->defaultFilters);
1242
1243
        if (array_key_exists('status', $requestFilters)) {
1244
            switch ($requestFilters['status']) {
1245
                case 'published':
1246
                    $scope['published'] = true;
1247
                    break;
1248
                case 'draft':
1249
                    $scope['draft'] = true;
1250
                    break;
1251
                case 'trash':
1252
                    $scope['onlyTrashed'] = true;
1253
                    break;
1254
                case 'mine':
1255
                    $scope['mine'] = true;
1256
                    break;
1257
            }
1258
1259
            unset($requestFilters['status']);
1260
        }
1261
1262
        foreach ($this->filters as $key => $field) {
1263
            if (array_key_exists($key, $requestFilters)) {
1264
                $value = $requestFilters[$key];
1265
                if ($value == 0 || !empty($value)) {
1266
                    // add some syntaxic sugar to scope the same filter on multiple columns
1267
                    $fieldSplitted = explode('|', $field);
1268
                    if (count($fieldSplitted) > 1) {
1269
                        $requestValue = $requestFilters[$key];
1270
                        Collection::make($fieldSplitted)->each(function ($scopeKey) use (&$scope, $requestValue) {
1271
                            $scope[$scopeKey] = $requestValue;
1272
                        });
1273
                    } else {
1274
                        $scope[$field] = $requestFilters[$key];
1275
                    }
1276
                }
1277
            }
1278
        }
1279
1280
        return $prepend + $scope;
1281
    }
1282
1283
    /**
1284
     * @return array
1285
     */
1286
    protected function getRequestFilters()
1287
    {
1288
        if ($this->request->has('search')) {
1289
            return ['search' => $this->request->get('search')];
1290
        }
1291
1292
        return json_decode($this->request->get('filter'), true) ?? [];
1293
    }
1294
1295
    /**
1296
     * @return void
1297
     */
1298 1
    protected function applyFiltersDefaultOptions()
1299
    {
1300 1
        if (!count($this->filtersDefaultOptions) || $this->request->has('search')) {
1301 1
            return;
1302
        }
1303
1304
        $filters = $this->getRequestFilters();
1305
1306
        foreach ($this->filtersDefaultOptions as $filterName => $defaultOption) {
1307
            if (!isset($filters[$filterName])) {
1308
                $filters[$filterName] = $defaultOption;
1309
            }
1310
        }
1311
1312
        $this->request->merge(['filter' => json_encode($filters)]);
1313
    }
1314
1315
    /**
1316
     * @return array
1317
     */
1318
    protected function orderScope()
1319
    {
1320
        $orders = [];
1321
        if ($this->request->has('sortKey') && $this->request->has('sortDir')) {
1322
            if (($key = $this->request->get('sortKey')) == 'name') {
1323
                $sortKey = $this->titleColumnKey;
1324
            } elseif (!empty($key)) {
1325
                $sortKey = $key;
1326
            }
1327
1328
            if (isset($sortKey)) {
1329
                $orders[$this->indexColumns[$sortKey]['sortKey'] ?? $sortKey] = $this->request->get('sortDir');
1330
            }
1331
        }
1332
1333
        // don't apply default orders if reorder is enabled
1334
        $reorder = $this->getIndexOption('reorder');
1335
        $defaultOrders = ($reorder ? [] : ($this->defaultOrders ?? []));
1336
1337
        return $orders + $defaultOrders;
1338
    }
1339
1340
    /**
1341
     * @param int $id
1342
     * @param \A17\Twill\Models\Model|null $item
1343
     * @return array
1344
     */
1345
    protected function form($id, $item = null)
1346
    {
1347
        $item = $item ?? $this->repository->getById($id, $this->formWith, $this->formWithCount);
1348
1349
        $fullRoutePrefix = 'admin.' . ($this->routePrefix ? $this->routePrefix . '.' : '') . $this->moduleName . '.';
1350
        $previewRouteName = $fullRoutePrefix . 'preview';
1351
        $restoreRouteName = $fullRoutePrefix . 'restoreRevision';
1352
1353
        $baseUrl = $item->urlWithoutSlug ?? $this->getPermalinkBaseUrl();
0 ignored issues
show
Bug introduced by
The property urlWithoutSlug does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
1354
1355
        $data = [
1356
            'item' => $item,
1357
            'moduleName' => $this->moduleName,
1358
            'routePrefix' => $this->routePrefix,
1359
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
1360
            'publish' => $item->canPublish ?? true,
0 ignored issues
show
Bug introduced by
The property canPublish does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
1361
            'translate' => $this->moduleHas('translations'),
1362
            'permalink' => $this->getIndexOption('permalink'),
1363
            'form_fields' => $this->repository->getFormFields($item),
1364
            'baseUrl' => $baseUrl,
1365
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
1366
            'saveUrl' => $this->getModuleRoute($item->id, 'update'),
1367
            'editor' => Config::get('twill.enabled.block-editor') && $this->moduleHas('blocks') && !$this->disableEditor,
1368
            'blockPreviewUrl' => Route::has('admin.blocks.preview') ? URL::route('admin.blocks.preview') : '#',
1369
            'revisions' => $this->moduleHas('revisions') ? $item->revisionsArray() : null,
1370
        ] + (Route::has($previewRouteName) ? [
1371
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', $item->id),
0 ignored issues
show
Bug introduced by
$item->id of type integer is incompatible with the type array expected by parameter $parameters of moduleRoute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1371
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1372
        ] : [])
1373
             + (Route::has($restoreRouteName) ? [
1374
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1375
        ] : []);
1376
1377
        return array_replace_recursive($data, $this->formData($this->request));
1378
    }
1379
1380
    /**
1381
     * @param int $id
1382
     * @return array
1383
     */
1384
    protected function modalFormData($id)
1385
    {
1386
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1387
        $fields = $this->repository->getFormFields($item);
1388
        $data = [];
1389
1390
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1391
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1392
                $data['fields'][] = [
1393
                    'name' => $fieldName,
1394
                    'value' => $fieldValue,
1395
                ];
1396
            }
1397
1398
            $data['languages'] = $item->getActiveLanguages();
1399
1400
            unset($fields['translations']);
1401
        }
1402
1403
        foreach ($fields as $fieldName => $fieldValue) {
1404
            $data['fields'][] = [
1405
                'name' => $fieldName,
1406
                'value' => $fieldValue,
1407
            ];
1408
        }
1409
1410
        return array_replace_recursive($data, $this->formData($this->request));
1411
    }
1412
1413
    /**
1414
     * @param Request $request
1415
     * @return array
1416
     */
1417
    protected function formData($request)
1418
    {
1419
        return [];
1420
    }
1421
1422
    /**
1423
     * @param Request $item
1424
     * @return array
1425
     */
1426
    protected function previewData($item)
0 ignored issues
show
Unused Code introduced by
The parameter $item is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1426
    protected function previewData(/** @scrutinizer ignore-unused */ $item)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1427
    {
1428
        return [];
1429
    }
1430
1431
    /**
1432
     * @return \A17\Twill\Http\Requests\Admin\Request
1433
     */
1434
    protected function validateFormRequest()
1435
    {
1436
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, $field) {
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1436
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, /** @scrutinizer ignore-unused */ $field) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1437
            return Auth::guard('twill_users')->user()->cannot($permission);
1438
        })->keys();
1439
1440
        $unauthorizedFields->each(function ($field) {
1441
            $this->request->offsetUnset($field);
1442
        });
1443
1444
        return App::make("$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request");
1445
    }
1446
1447
    /**
1448
     * @return string
1449
     */
1450 1
    protected function getNamespace()
1451
    {
1452 1
        return $this->namespace ?? Config::get('twill.namespace');
1453
    }
1454
1455
    /**
1456
     * @return string
1457
     */
1458 1
    protected function getRoutePrefix()
1459
    {
1460 1
        if ($this->request->route() != null) {
1461
            $routePrefix = ltrim(str_replace(Config::get('twill.admin_app_path'), '', $this->request->route()->getPrefix()), "/");
1462
            return str_replace("/", ".", $routePrefix);
1463
        }
1464
1465 1
        return '';
1466
    }
1467
1468
    /**
1469
     * @return string
1470
     */
1471 1
    protected function getModelName()
1472
    {
1473 1
        return $this->modelName ?? ucfirst(Str::singular($this->moduleName));
1474
    }
1475
1476
    /**
1477
     * @return \A17\Twill\Repositories\ModuleRepository
1478
     */
1479 1
    protected function getRepository()
1480
    {
1481 1
        return App::make("$this->namespace\Repositories\\" . $this->modelName . "Repository");
1482
    }
1483
1484
    /**
1485
     * @return string
1486
     */
1487 1
    protected function getViewPrefix()
1488
    {
1489 1
        return "admin.$this->moduleName";
1490
    }
1491
1492
    /**
1493
     * @return string
1494
     */
1495 1
    protected function getModelTitle()
1496
    {
1497 1
        return camelCaseToWords($this->modelName);
1498
    }
1499
1500
    /**
1501
     * @return string
1502
     */
1503
    protected function getParentModuleForeignKey()
1504
    {
1505
        return Str::singular(explode('.', $this->moduleName)[0]) . '_id';
1506
    }
1507
1508
    /**
1509
     * @return string
1510
     */
1511
    protected function getPermalinkBaseUrl()
1512
    {
1513
        return $this->request->getScheme() . '://' . Config::get('app.url') . '/'
1514
            . ($this->moduleHas('translations') ? '{language}/' : '')
1515
            . ($this->moduleHas('revisions') ? '{preview}/' : '')
1516
            . ($this->permalinkBase ?? $this->moduleName)
1517
            . (isset($this->permalinkBase) && empty($this->permalinkBase) ? '' : '/');
1518
    }
1519
1520
    /**
1521
     * @param string $baseUrl
1522
     * @return string
1523
     */
1524
    protected function getPermalinkPrefix($baseUrl)
1525
    {
1526
        return rtrim(str_replace(['http://', 'https://', '{preview}/', '{language}/'], '', $baseUrl), "/") . '/';
1527
    }
1528
1529
    /**
1530
     * @param int $id
1531
     * @param string $action
1532
     * @return string
1533
     */
1534
    protected function getModuleRoute($id, $action)
1535
    {
1536
        return moduleRoute(
1537
            $this->moduleName,
1538
            $this->routePrefix,
1539
            $action,
1540
            array_merge($this->submodule ? [$this->submoduleParentId] : [], [$id])
1541
        );
1542
    }
1543
1544
    /**
1545
     * @param string $behavior
1546
     * @return bool
1547
     */
1548 1
    protected function moduleHas($behavior)
1549
    {
1550 1
        return $this->repository->hasBehavior($behavior);
1551
    }
1552
1553
    /**
1554
     * @return bool
1555
     */
1556
    protected function titleIsTranslatable()
1557
    {
1558
        return $this->repository->isTranslatable(
1559
            $this->titleColumnKey
1560
        );
1561
    }
1562
1563
    /**
1564
     * @param string|null $back_link
1565
     * @param array $params
1566
     * @return void
1567
     */
1568
    protected function setBackLink($back_link = null, $params = [])
1569
    {
1570
        if (!isset($back_link)) {
1571
            if (($back_link = Session::get($this->getBackLinkSessionKey())) == null) {
1572
                $back_link = $this->request->headers->get('referer') ?? moduleRoute(
1573
                    $this->moduleName,
1574
                    $this->routePrefix,
1575
                    'index',
1576
                    $params
1577
                );
1578
            }
1579
        }
1580
1581
        if (!Session::get($this->moduleName . '_retain')) {
1582
            Session::put($this->getBackLinkSessionKey(), $back_link);
1583
        } else {
1584
            Session::put($this->moduleName . '_retain', false);
1585
        }
1586
    }
1587
1588
    /**
1589
     * @param string|null $fallback
1590
     * @param array $params
1591
     * @return string
1592
     */
1593
    protected function getBackLink($fallback = null, $params = [])
1594
    {
1595
        $back_link = Session::get($this->getBackLinkSessionKey(), $fallback);
1596
        return $back_link ?? moduleRoute($this->moduleName, $this->routePrefix, 'index', $params);
1597
    }
1598
1599
    /**
1600
     * @return string
1601
     */
1602
    protected function getBackLinkSessionKey()
1603
    {
1604
        return $this->moduleName . ($this->submodule ? $this->submoduleParentId ?? '' : '') . '_back_link';
1605
    }
1606
1607
    /**
1608
     * @param int $id
1609
     * @param array $params
1610
     * @return \Illuminate\Http\RedirectResponse
1611
     */
1612
    protected function redirectToForm($id, $params = [])
1613
    {
1614
        Session::put($this->moduleName . '_retain', true);
1615
1616
        return Redirect::to(moduleRoute(
1617
            $this->moduleName,
1618
            $this->routePrefix,
1619
            'edit',
1620
            array_filter($params) + [Str::singular($this->moduleName) => $id]
1621
        ));
1622
    }
1623
1624
    /**
1625
     * @param string $message
1626
     * @return \Illuminate\Http\JsonResponse
1627
     */
1628
    protected function respondWithSuccess($message)
1629
    {
1630
        return $this->respondWithJson($message, FlashLevel::SUCCESS);
1631
    }
1632
1633
    /**
1634
     * @param string $redirectUrl
1635
     * @return \Illuminate\Http\JsonResponse
1636
     */
1637
    protected function respondWithRedirect($redirectUrl)
1638
    {
1639
        return Response::json([
1640
            'redirect' => $redirectUrl,
1641
        ]);
1642
    }
1643
1644
    /**
1645
     * @param string $message
1646
     * @return \Illuminate\Http\JsonResponse
1647
     */
1648
    protected function respondWithError($message)
1649
    {
1650
        return $this->respondWithJson($message, FlashLevel::ERROR);
1651
    }
1652
1653
    /**
1654
     * @param string $message
1655
     * @param mixed $variant
1656
     * @return \Illuminate\Http\JsonResponse
1657
     */
1658
    protected function respondWithJson($message, $variant)
1659
    {
1660
        return Response::json([
1661
            'message' => $message,
1662
            'variant' => $variant,
1663
        ]);
1664
    }
1665
1666
    /**
1667
     * @param array $input
1668
     * @return void
1669
     */
1670
    protected function fireEvent($input = [])
1671
    {
1672
        fireCmsEvent('cms-module.saved', $input);
1673
    }
1674
}
1675