Passed
Push — 2.x ( d49df2...2d1b2e )
by Quentin
07:05
created

ModuleController::getIndexTableData()   D

Complexity

Conditions 19
Paths 1

Size

Total Lines 52
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 22.1146

Importance

Changes 0
Metric Value
cc 19
eloc 40
nc 1
nop 1
dl 0
loc 52
ccs 31
cts 39
cp 0.7949
crap 22.1146
rs 4.5166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
        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 19
                Str::singular($this->moduleName) => $item->id,
376
            ];
377
        }
378
379 19
        return $this->respondWithRedirect(moduleRoute(
380 19
            $this->moduleName,
381 19
            $this->routePrefix,
382 19
            'edit',
383
            $params
384
        ));
385
    }
386
387
    /**
388
     * @param int|$id
389
     * @param int|null $submoduleId
390
     * @return \Illuminate\Http\RedirectResponse
391
     */
392 1
    public function show($id, $submoduleId = null)
393
    {
394 1
        if ($this->getIndexOption('editInModal')) {
395
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
396
        }
397
398 1
        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 5
    public function edit($id, $submoduleId = null)
407
    {
408 5
        $this->submodule = isset($submoduleId);
409 5
        $this->submoduleParentId = $id;
410
411 5
        if ($this->getIndexOption('editInModal')) {
412 2
            return $this->request->ajax()
413 1
            ? Response::json($this->modalFormData($submoduleId ?? $id))
414 2
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
415
        }
416
417 3
        $this->setBackLink();
418
419 3
        $view = Collection::make([
420 3
            "$this->viewPrefix.form",
421 3
            "twill::$this->moduleName.form",
422 3
            "twill::layouts.form",
423
        ])->first(function ($view) {
424 3
            return View::exists($view);
425 3
        });
426
427 3
        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 7
    public function update($id, $submoduleId = null)
436
    {
437 7
        $this->submodule = isset($submoduleId);
438 7
        $this->submoduleParentId = $id;
439
440 7
        $item = $this->repository->getById($submoduleId ?? $id);
441 7
        $input = $this->request->all();
442
443 7
        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 7
            $formRequest = $this->validateFormRequest();
452
453 7
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
454
455 7
            activity()->performedOn($item)->log('updated');
456
457 7
            $this->fireEvent();
458
459 7
            if (isset($input['cmsSaveType'])) {
460 7
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
461
                    return $this->respondWithRedirect($this->getBackLink());
462 7
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
463
                    return $this->respondWithRedirect(moduleRoute(
464
                        $this->moduleName,
465
                        $this->routePrefix,
466
                        'index',
467
                        ['openCreate' => true]
468
                    ));
469 7
                } 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 7
            if ($this->moduleHas('revisions')) {
482 6
                return Response::json([
483 6
                    'message' => 'Content saved. All good!',
484
                    'variant' => FlashLevel::SUCCESS,
485 6
                    'revisions' => $item->revisionsArray(),
486
                ]);
487
            }
488
489 1
            return $this->respondWithSuccess('Content saved. All good!');
490
        }
491
    }
492
493
    /**
494
     * @param int $id
495
     * @return \Illuminate\View\View
496
     */
497 1
    public function preview($id)
498
    {
499 1
        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 1
            $formRequest = $this->validateFormRequest();
503 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

503
            /** @scrutinizer ignore-call */ 
504
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
504
        }
505
506 1
        if ($this->request->has('activeLanguage')) {
507
            App::setLocale($this->request->get('activeLanguage'));
508
        }
509
510 1
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
511
512 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
513 1
            'item' => $item,
514 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

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

544
        /** @scrutinizer ignore-call */ 
545
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
545
546 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...
547
548 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

548
        return View::make($view, $this->form($id, /** @scrutinizer ignore-type */ $item));
Loading history...
549
    }
550
551
    /**
552
     * @return \Illuminate\Http\JsonResponse
553
     */
554 2
    public function publish()
555
    {
556
        try {
557 2
            if ($this->repository->updateBasic($this->request->get('id'), [
558 2
                'published' => !$this->request->get('active'),
559
            ])) {
560 2
                activity()->performedOn(
561 2
                    $this->repository->getById($this->request->get('id'))
562 1
                )->log(
563 1
                    ($this->request->get('active') ? 'un' : '') . 'published'
564
                );
565
566 1
                $this->fireEvent();
567
568 1
                return $this->respondWithSuccess(
569 1
                    $this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'published!'
570
                );
571
            }
572 1
        } catch (\Exception $e) {
573 1
            \Log::error($e);
574
        }
575
576 1
        return $this->respondWithError(
577 1
            $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, $this->titleColumnKey)) {
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 2
    public function destroy($id, $submoduleId = null)
639
    {
640 2
        $item = $this->repository->getById($submoduleId ?? $id);
641 2
        if ($this->repository->delete($submoduleId ?? $id)) {
642 2
            $this->fireEvent();
643 2
            activity()->performedOn($item)->log('deleted');
644 2
            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 2
    public function restore()
693
    {
694 2
        if ($this->repository->restore($this->request->get('id'))) {
695 1
            $this->fireEvent();
696 1
            activity()->performedOn($this->repository->getById($this->request->get('id')))->log('restored');
697 1
            return $this->respondWithSuccess($this->modelTitle . ' restored!');
698
        }
699
700 1
        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 2
    public function feature()
720
    {
721 2
        if (($id = $this->request->get('id'))) {
722 2
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
723 2
            $featured = !$this->request->get('active');
724
725 2
            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 2
                $this->repository->updateBasic($id, [$featuredField => $featured]);
732
            }
733
734 2
            activity()->performedOn(
735 2
                $this->repository->getById($id)
736 1
            )->log(
737 1
                ($this->request->get('active') ? 'un' : '') . 'featured'
738
            );
739
740 1
            $this->fireEvent();
741 1
            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 2
    public function reorder()
768
    {
769 2
        if (($values = $this->request->get('ids')) && !empty($values)) {
770 2
            $this->repository->setNewOrder($values);
771 1
            $this->fireEvent();
772 1
            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 1
    public function tags()
782
    {
783 1
        $query = $this->request->input('q');
784 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

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

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

1119
    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

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

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

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

1428
    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...
1429
    {
1430 1
        return [];
1431
    }
1432
1433
    /**
1434
     * @return \A17\Twill\Http\Requests\Admin\Request
1435
     */
1436 21
    protected function validateFormRequest()
1437
    {
1438
        $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

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