Passed
Push — master ( 77eb7f...a4e2d8 )
by Quentin
07:54
created

ModuleController::duplicate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 13
nc 2
nop 2
dl 0
loc 21
ccs 0
cts 13
cp 0
crap 6
rs 9.8333
c 1
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 40
    public function __construct(Application $app, Request $request)
239
    {
240 40
        parent::__construct();
241 40
        $this->app = $app;
242 40
        $this->request = $request;
243
244 40
        $this->setMiddlewarePermission();
245
246 40
        $this->modelName = $this->getModelName();
247 40
        $this->routePrefix = $this->getRoutePrefix();
248 40
        $this->namespace = $this->getNamespace();
249 40
        $this->repository = $this->getRepository();
250 40
        $this->viewPrefix = $this->getViewPrefix();
251 40
        $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 40
        if (!isset($this->defaultFilters)) {
258 26
            $this->defaultFilters = [
259 26
                'search' => ($this->moduleHas('translations') ? '' : '%') . $this->titleColumnKey,
260
            ];
261
        }
262
263
        /*
264
         * Apply any filters that are selected by default
265
         */
266 40
        $this->applyFiltersDefaultOptions();
267
268
        /*
269
         * Available columns of the index view
270
         */
271 40
        if (!isset($this->indexColumns)) {
272 13
            $this->indexColumns = [
273 13
                $this->titleColumnKey => [
274 13
                    'title' => ucfirst($this->titleColumnKey),
275 13
                    'field' => $this->titleColumnKey,
276
                    'sort' => true,
277
                ],
278
            ];
279
        }
280
281
        /*
282
         * Available columns of the browser view
283
         */
284 40
        if (!isset($this->browserColumns)) {
285 40
            $this->browserColumns = [
286 40
                $this->titleColumnKey => [
287 40
                    'title' => ucfirst($this->titleColumnKey),
288 40
                    'field' => $this->titleColumnKey,
289
                ],
290
            ];
291
        }
292 40
    }
293
294
    /**
295
     * @return void
296
     */
297 40
    protected function setMiddlewarePermission()
298
    {
299 40
        $this->middleware('can:list', ['only' => ['index', 'show']]);
300 40
        $this->middleware('can:edit', ['only' => ['store', 'edit', 'update']]);
301 40
        $this->middleware('can:duplicate', ['only' => ['duplicate']]);
302 40
        $this->middleware('can:publish', ['only' => ['publish', 'feature', 'bulkPublish', 'bulkFeature']]);
303 40
        $this->middleware('can:reorder', ['only' => ['reorder']]);
304 40
        $this->middleware('can:delete', ['only' => ['destroy', 'bulkDelete', 'restore', 'bulkRestore', 'forceDelete', 'bulkForceDelete', 'restoreRevision']]);
305 40
    }
306
307
    /**
308
     * @param int|null $parentModuleId
309
     * @return array|\Illuminate\View\View
310
     */
311 6
    public function index($parentModuleId = null)
312
    {
313 6
        $this->submodule = isset($parentModuleId);
314 6
        $this->submoduleParentId = $parentModuleId;
315
316 6
        $indexData = $this->getIndexData($this->submodule ? [
317
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
318 6
        ] : []);
319
320 6
        if ($this->request->ajax()) {
321 3
            return $indexData + ['replaceUrl' => true];
322
        }
323
324 3
        if ($this->request->has('openCreate') && $this->request->get('openCreate')) {
325
            $indexData += ['openCreate' => true];
326
        }
327
328 3
        $view = Collection::make([
329 3
            "$this->viewPrefix.index",
330 3
            "twill::$this->moduleName.index",
331 3
            "twill::layouts.listing",
332
        ])->first(function ($view) {
333 3
            return View::exists($view);
334 3
        });
335
336 3
        return View::make($view, $indexData);
337
    }
338
339
    /**
340
     * @return \Illuminate\Http\JsonResponse
341
     */
342 2
    public function browser()
343
    {
344 2
        return Response::json($this->getBrowserData());
345
    }
346
347
    /**
348
     * @param int|null $parentModuleId
349
     * @return \Illuminate\Http\JsonResponse
350
     */
351 21
    public function store($parentModuleId = null)
352
    {
353 21
        $input = $this->validateFormRequest()->all();
354 21
        $optionalParent = $parentModuleId ? [$this->getParentModuleForeignKey() => $parentModuleId] : [];
355
356 21
        $item = $this->repository->create($input + $optionalParent);
357
358 21
        activity()->performedOn($item)->log('created');
359
360 21
        $this->fireEvent($input);
361
362 21
        Session::put($this->moduleName . '_retain', true);
363
364 21
        if ($this->getIndexOption('editInModal')) {
365 2
            return $this->respondWithSuccess('Content saved. All good!');
366
        }
367
368 19
        return $this->respondWithRedirect(moduleRoute(
369 19
            $this->moduleName,
370 19
            $this->routePrefix,
371 19
            'edit',
372 19
            array_filter([$parentModuleId]) + [Str::singular($this->moduleName) => $item->id]
373
        ));
374
    }
375
376
    /**
377
     * @param int|$id
378
     * @param int|null $submoduleId
379
     * @return \Illuminate\Http\RedirectResponse
380
     */
381 1
    public function show($id, $submoduleId = null)
382
    {
383 1
        if ($this->getIndexOption('editInModal')) {
384
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
385
        }
386
387 1
        return $this->redirectToForm($submoduleId ?? $id);
388
    }
389
390
    /**
391
     * @param int $id
392
     * @param int|null $submoduleId
393
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
394
     */
395 5
    public function edit($id, $submoduleId = null)
396
    {
397 5
        $this->submodule = isset($submoduleId);
398 5
        $this->submoduleParentId = $id;
399
400 5
        if ($this->getIndexOption('editInModal')) {
401 2
            return $this->request->ajax()
402 1
            ? Response::json($this->modalFormData($submoduleId ?? $id))
403 2
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
404
        }
405
406 3
        $this->setBackLink();
407
408 3
        $view = Collection::make([
409 3
            "$this->viewPrefix.form",
410 3
            "twill::$this->moduleName.form",
411 3
            "twill::layouts.form",
412
        ])->first(function ($view) {
413 3
            return View::exists($view);
414 3
        });
415
416 3
        return View::make($view, $this->form($submoduleId ?? $id));
417
    }
418
419
    /**
420
     * @param int $id
421
     * @param int|null $submoduleId
422
     * @return \Illuminate\Http\JsonResponse
423
     */
424 7
    public function update($id, $submoduleId = null)
425
    {
426 7
        $this->submodule = isset($submoduleId);
427 7
        $this->submoduleParentId = $id;
428
429 7
        $item = $this->repository->getById($submoduleId ?? $id);
430 7
        $input = $this->request->all();
431
432 7
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
433
            return $this->respondWithRedirect(moduleRoute(
434
                $this->moduleName,
435
                $this->routePrefix,
436
                'edit',
437
                [Str::singular($this->moduleName) => $id]
438
            ));
439
        } else {
440 7
            $formRequest = $this->validateFormRequest();
441
442 7
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
443
444 7
            activity()->performedOn($item)->log('updated');
445
446 7
            $this->fireEvent();
447
448 7
            if (isset($input['cmsSaveType'])) {
449 7
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
450
                    return $this->respondWithRedirect($this->getBackLink());
451 7
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
452
                    return $this->respondWithRedirect(moduleRoute(
453
                        $this->moduleName,
454
                        $this->routePrefix,
455
                        'index',
456
                        ['openCreate' => true]
457
                    ));
458 7
                } elseif ($input['cmsSaveType'] === 'restore') {
459
                    Session::flash('status', "Revision restored.");
460
461
                    return $this->respondWithRedirect(moduleRoute(
462
                        $this->moduleName,
463
                        $this->routePrefix,
464
                        'edit',
465
                        [Str::singular($this->moduleName) => $id]
466
                    ));
467
                }
468
            }
469
470 7
            if ($this->moduleHas('revisions')) {
471 6
                return Response::json([
472 6
                    'message' => 'Content saved. All good!',
473
                    'variant' => FlashLevel::SUCCESS,
474 6
                    'revisions' => $item->revisionsArray(),
475
                ]);
476
            }
477
478 1
            return $this->respondWithSuccess('Content saved. All good!');
479
        }
480
    }
481
482
    /**
483
     * @param int $id
484
     * @return \Illuminate\View\View
485
     */
486 1
    public function preview($id)
487
    {
488 1
        if ($this->request->has('revisionId')) {
489
            $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

489
            /** @scrutinizer ignore-call */ 
490
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
Loading history...
490
        } else {
491 1
            $formRequest = $this->validateFormRequest();
492 1
            $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

492
            /** @scrutinizer ignore-call */ 
493
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
493
        }
494
495 1
        if ($this->request->has('activeLanguage')) {
496
            App::setLocale($this->request->get('activeLanguage'));
497
        }
498
499 1
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
500
501 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
502 1
            'item' => $item,
503 1
        ], $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

503
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
504 1
            'moduleName' => Str::singular($this->moduleName),
505
        ]);
506
    }
507
508
    /**
509
     * @param int $id
510
     * @return \Illuminate\View\View
511
     */
512 2
    public function restoreRevision($id)
513
    {
514 2
        if ($this->request->has('revisionId')) {
515 1
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
516 1
            $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...
517 1
            $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...
518
        } else {
519 1
            throw new NotFoundHttpException();
520
        }
521
522 1
        $this->setBackLink();
523
524 1
        $view = Collection::make([
525 1
            "$this->viewPrefix.form",
526 1
            "twill::$this->moduleName.form",
527 1
            "twill::layouts.form",
528
        ])->first(function ($view) {
529 1
            return View::exists($view);
530 1
        });
531
532 1
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
533 1
        $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

533
        /** @scrutinizer ignore-call */ 
534
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
534
535 1
        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...
536
537 1
        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

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

773
        /** @scrutinizer ignore-call */ 
774
        $tags = $this->repository->getTags($query);
Loading history...
774
775
        return Response::json(['items' => $tags->map(function ($tag) {
776
            return $tag->name;
777 1
        })], 200);
778
    }
779
780
    /**
781
     * @param array $prependScope
782
     * @return array
783
     */
784 6
    protected function getIndexData($prependScope = [])
785
    {
786 6
        $scopes = $this->filterScope($prependScope);
787 6
        $items = $this->getIndexItems($scopes);
788
789
        $data = [
790 6
            'tableData' => $this->getIndexTableData($items),
791 6
            'tableColumns' => $this->getIndexTableColumns($items),
792 6
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
793 6
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
794 6
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
795 6
            'filterLinks' => $this->filterLinks ?? [],
796 6
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
797 6
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
798 6
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
799 6
            'defaultOffset' => $this->perPage,
800 6
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
801
802 6
        $baseUrl = $this->getPermalinkBaseUrl();
803
804
        $options = [
805 6
            'moduleName' => $this->moduleName,
806 6
            'reorder' => $this->getIndexOption('reorder'),
807 6
            'create' => $this->getIndexOption('create'),
808 6
            'duplicate' => $this->getIndexOption('duplicate'),
809 6
            'translate' => $this->moduleHas('translations'),
810 6
            'translateTitle' => $this->titleIsTranslatable(),
811 6
            'permalink' => $this->getIndexOption('permalink'),
812 6
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
813 6
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
814 6
            'baseUrl' => $baseUrl,
815 6
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
816
        ];
817
818 6
        return array_replace_recursive($data + $options, $this->indexData($this->request));
819
    }
820
821
    /**
822
     * @param Request $request
823
     * @return array
824
     */
825 5
    protected function indexData($request)
826
    {
827 5
        return [];
828
    }
829
830
    /**
831
     * @param array $scopes
832
     * @param bool $forcePagination
833
     * @return \Illuminate\Database\Eloquent\Collection
834
     */
835 12
    protected function getIndexItems($scopes = [], $forcePagination = false)
836
    {
837 12
        return $this->transformIndexItems($this->repository->get(
838 12
            $this->indexWith,
839
            $scopes,
840 12
            $this->orderScope(),
841 12
            $this->request->get('offset') ?? $this->perPage ?? 50,
842
            $forcePagination
843
        ));
844
    }
845
846
    /**
847
     * @param \Illuminate\Database\Eloquent\Collection $items
848
     * @return \Illuminate\Database\Eloquent\Collection
849
     */
850 10
    protected function transformIndexItems($items)
851
    {
852 10
        return $items;
853
    }
854
855
    /**
856
     * @param \Illuminate\Database\Eloquent\Collection $items
857
     * @return array
858
     */
859 6
    protected function getIndexTableData($items)
860
    {
861 6
        $translated = $this->moduleHas('translations');
862
        return $items->map(function ($item) use ($translated) {
863
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
864 3
                return $this->getItemColumnData($item, $column);
865 3
            })->toArray();
866
867 3
            $name = $columnsData[$this->titleColumnKey];
868
869 3
            if (empty($name)) {
870
                if ($this->moduleHas('translations')) {
871
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
872
873
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
874
                        $name = $fallBackTranslation->{$this->titleColumnKey};
875
                    }
876
                }
877
878
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
879
            }
880
881 3
            unset($columnsData[$this->titleColumnKey]);
882
883 3
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
884 3
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
885 3
            $canEdit = $this->getIndexOption('edit');
886 3
            $canDuplicate = $this->getIndexOption('duplicate');
887
888 3
            return array_replace([
889 3
                'id' => $item->id,
890 3
                'name' => $name,
891 3
                'publish_start_date' => $item->publish_start_date,
892 3
                'publish_end_date' => $item->publish_end_date,
893 3
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
894 3
                'duplicate' => $canDuplicate ? $this->getModuleRoute($item->id, 'duplicate') : null,
895 3
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
896 3
            ] + ($this->getIndexOption('editInModal') ? [
897 1
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
898 1
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
899 3
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
900 3
                'published' => $item->published,
901 3
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
902
                'featured' => $item->{$this->featureField},
903 3
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
904
                'deleted' => true,
905 3
            ] : []) + ($translated ? [
906 3
                'languages' => $item->getActiveLanguages(),
907 3
            ] : []) + $columnsData, $this->indexItemData($item));
908 6
        })->toArray();
909
    }
910
911
    /**
912
     * @param \A17\Twill\Models\Model $item
913
     * @return array
914
     */
915 2
    protected function indexItemData($item)
916
    {
917 2
        return [];
918
    }
919
920
    /**
921
     * @param \A17\Twill\Models\Model $item
922
     * @param array $column
923
     * @return array
924
     */
925 5
    protected function getItemColumnData($item, $column)
926
    {
927 5
        if (isset($column['thumb']) && $column['thumb']) {
928 2
            if (isset($column['present']) && $column['present']) {
929
                return [
930
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
931
                ];
932
            } else {
933 2
                $variant = isset($column['variant']);
934 2
                $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...
935 2
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
936 2
                $params = $variant && isset($column['variant']['params'])
937
                ? $column['variant']['params']
938 2
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
939
940
                return [
941 2
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
942
                ];
943
            }
944
        }
945
946 5
        if (isset($column['nested']) && $column['nested']) {
947
            $field = $column['nested'];
948
            $nestedCount = $item->{$column['nested']}->count();
949
            $value = '<a href="';
950
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
951
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
952
                ? Str::plural($column['title'])
953
                : Str::singular($column['title']))) . '</a>';
954
        } else {
955 5
            $field = $column['field'];
956 5
            $value = $item->$field;
957
        }
958
959 5
        if (isset($column['relationship'])) {
960
            $field = $column['relationship'] . ucfirst($column['field']);
961
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
962 5
        } elseif (isset($column['present']) && $column['present']) {
963
            $value = $item->presentAdmin()->{$column['field']};
964
        }
965
966
        return [
967 5
            "$field" => $value,
968
        ];
969
    }
970
971
    /**
972
     * @param \Illuminate\Database\Eloquent\Collection $items
973
     * @return array
974
     */
975 6
    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

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

1106
    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

1106
    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...
1107
    {
1108 6
        return Collection::make([
1109 6
            'store',
1110
            'publish',
1111
            'bulkPublish',
1112
            'restore',
1113
            'bulkRestore',
1114
            'forceDelete',
1115
            'bulkForceDelete',
1116
            'reorder',
1117
            'feature',
1118
            'bulkFeature',
1119
            'bulkDelete',
1120
        ])->mapWithKeys(function ($endpoint) {
1121
            return [
1122 6
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1123 6
                    $this->moduleName, $this->routePrefix, $endpoint,
1124 6
                    $this->submodule ? [$this->submoduleParentId] : []
1125
                ) : null,
1126
            ];
1127 6
        })->toArray();
1128
    }
1129
1130
    /**
1131
     * @param string $option
1132
     * @return bool
1133
     */
1134 29
    protected function getIndexOption($option)
1135
    {
1136
        return once(function () use ($option) {
1137
            $customOptionNamesMapping = [
1138 29
                'store' => 'create',
1139
            ];
1140
1141 29
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1142
1143
            $authorizableOptions = [
1144 29
                'create' => 'edit',
1145
                'edit' => 'edit',
1146
                'publish' => 'publish',
1147
                'feature' => 'feature',
1148
                'reorder' => 'reorder',
1149
                'delete' => 'delete',
1150
                'duplicate' => 'duplicate',
1151
                'restore' => 'delete',
1152
                'forceDelete' => 'delete',
1153
                'bulkForceDelete' => 'delete',
1154
                'bulkPublish' => 'publish',
1155
                'bulkRestore' => 'delete',
1156
                'bulkFeature' => 'feature',
1157
                'bulkDelete' => 'delete',
1158
                'bulkEdit' => 'edit',
1159
                'editInModal' => 'edit',
1160
            ];
1161
1162 29
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1163 29
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1164 29
        });
1165
    }
1166
1167
    /**
1168
     * @param array $prependScope
1169
     * @return array
1170
     */
1171 2
    protected function getBrowserData($prependScope = [])
1172
    {
1173 2
        if ($this->request->has('except')) {
1174
            $prependScope['exceptIds'] = $this->request->get('except');
1175
        }
1176
1177 2
        $scopes = $this->filterScope($prependScope);
1178 2
        $items = $this->getBrowserItems($scopes);
1179 2
        $data = $this->getBrowserTableData($items);
1180
1181 2
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1182
    }
1183
1184
    /**
1185
     * @param \Illuminate\Database\Eloquent\Collection $items
1186
     * @return array
1187
     */
1188 2
    protected function getBrowserTableData($items)
1189
    {
1190 2
        $withImage = $this->moduleHas('medias');
1191
1192
        return $items->map(function ($item) use ($withImage) {
1193
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item) {
1194 2
                return $this->getItemColumnData($item, $column);
1195 2
            })->toArray();
1196
1197 2
            $name = $columnsData[$this->titleColumnKey];
1198 2
            unset($columnsData[$this->titleColumnKey]);
1199
1200
            return [
1201 2
                'id' => $item->id,
1202 2
                'name' => $name,
1203 2
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1204 2
                '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

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

1360
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1361 4
        ] : [])
1362 4
             + (Route::has($restoreRouteName) ? [
1363 4
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1364 4
        ] : []);
1365
1366 4
        return array_replace_recursive($data, $this->formData($this->request));
1367
    }
1368
1369
    /**
1370
     * @param int $id
1371
     * @return array
1372
     */
1373 1
    protected function modalFormData($id)
1374
    {
1375 1
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1376 1
        $fields = $this->repository->getFormFields($item);
1377 1
        $data = [];
1378
1379 1
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1380 1
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1381 1
                $data['fields'][] = [
1382 1
                    'name' => $fieldName,
1383 1
                    'value' => $fieldValue,
1384
                ];
1385
            }
1386
1387 1
            $data['languages'] = $item->getActiveLanguages();
1388
1389 1
            unset($fields['translations']);
1390
        }
1391
1392 1
        foreach ($fields as $fieldName => $fieldValue) {
1393 1
            $data['fields'][] = [
1394 1
                'name' => $fieldName,
1395 1
                'value' => $fieldValue,
1396
            ];
1397
        }
1398
1399 1
        return array_replace_recursive($data, $this->formData($this->request));
1400
    }
1401
1402
    /**
1403
     * @param Request $request
1404
     * @return array
1405
     */
1406 4
    protected function formData($request)
1407
    {
1408 4
        return [];
1409
    }
1410
1411
    /**
1412
     * @param Request $item
1413
     * @return array
1414
     */
1415 1
    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

1415
    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...
1416
    {
1417 1
        return [];
1418
    }
1419
1420
    /**
1421
     * @return \A17\Twill\Http\Requests\Admin\Request
1422
     */
1423 21
    protected function validateFormRequest()
1424
    {
1425
        $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

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