Passed
Pull Request — 1.2 (#556)
by
unknown
07:51
created

ModuleController::duplicate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 13
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 21
rs 9.8333
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 $routePrefix;
38
39
    /**
40
     * @var string
41
     */
42
    protected $moduleName;
43
44
    /**
45
     * @var string
46
     */
47
    protected $modelName;
48
49
    /**
50
     * @var \A17\Twill\Repositories\ModuleRepository
51
     */
52
    protected $repository;
53
54
    /**
55
     * Options of the index view.
56
     *
57
     * @var array
58
     */
59
    protected $defaultIndexOptions = [
60
        'create' => true,
61
        'edit' => true,
62
        'publish' => true,
63
        'bulkPublish' => true,
64
        'feature' => false,
65
        'bulkFeature' => false,
66
        'restore' => true,
67
        'bulkRestore' => true,
68
        'delete' => true,
69
        'duplicate' => false,
70
        'bulkDelete' => true,
71
        'reorder' => false,
72
        'permalink' => true,
73
        'bulkEdit' => true,
74
        'editInModal' => false,
75
    ];
76
77
    /**
78
     * Relations to eager load for the index view
79
     *
80
     * @var array
81
     */
82
    protected $indexWith = [];
83
84
    /**
85
     * Relations to eager load for the form view.
86
     *
87
     * @var array
88
     */
89
    protected $formWith = [];
90
91
    /**
92
     * Relation count to eager load for the form view.
93
     *
94
     * @var array
95
     */
96
    protected $formWithCount = [];
97
98
    /**
99
     * Additional filters for the index view.
100
     *
101
     * To automatically have your filter added to the index view use the following convention:
102
     * suffix the key containing the list of items to show in the filter by 'List' and
103
     * name it the same as the filter you defined in this array.
104
     *
105
     * Example: 'fCategory' => 'category_id' here and 'fCategoryList' in indexData()
106
     * By default, this will run a where query on the category_id column with the value
107
     * of fCategory if found in current request parameters. You can intercept this behavior
108
     * from your repository in the filter() function.
109
     *
110
     * @var array
111
     */
112
    protected $filters = [];
113
114
    /**
115
     * Additional links to display in the listing filter
116
     *
117
     * @var array
118
     */
119
    protected $filterLinks = [];
120
121
    /**
122
     * Default orders for the index view.
123
     *
124
     * @var array
125
     */
126
    protected $defaultOrders = [
127
        'created_at' => 'desc',
128
    ];
129
130
    /**
131
     * @var int
132
     */
133
    protected $perPage = 20;
134
135
    /**
136
     * Name of the index column to use as name column.
137
     *
138
     * @var string
139
     */
140
    protected $titleColumnKey = 'title';
141
142
    /**
143
     * Attribute to use as title in forms.
144
     *
145
     * @var string
146
     */
147
    protected $titleFormKey;
148
149
    /**
150
     * Feature field name if the controller is using the feature route (defaults to "featured").
151
     *
152
     * @var string
153
     */
154
    protected $featureField = 'featured';
155
156
    /**
157
     * Indicates if this module is edited through a parent module.
158
     *
159
     * @var bool
160
     */
161
    protected $submodule = false;
162
163
    /**
164
     * @var int|null
165
     */
166
    protected $submoduleParentId = null;
167
168
    /**
169
     * Can be used in child classes to disable the content editor (full screen block editor).
170
     *
171
     * @var bool
172
     */
173
    protected $disableEditor = false;
174
175
    /**
176
     * List of permissions keyed by a request field. Can be used to prevent unauthorized field updates.
177
     *
178
     * @var array
179
     */
180
    protected $fieldsPermissions = [];
181
182
    public function __construct(Application $app, Request $request)
183
    {
184
        parent::__construct();
185
        $this->app = $app;
186
        $this->request = $request;
187
188
        $this->setMiddlewarePermission();
189
190
        $this->modelName = $this->getModelName();
191
        $this->routePrefix = $this->getRoutePrefix();
192
        $this->namespace = $this->getNamespace();
0 ignored issues
show
Bug Best Practice introduced by
The property namespace does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
193
        $this->repository = $this->getRepository();
194
        $this->viewPrefix = $this->getViewPrefix();
0 ignored issues
show
Bug Best Practice introduced by
The property viewPrefix does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
195
        $this->modelTitle = $this->getModelTitle();
0 ignored issues
show
Bug Best Practice introduced by
The property modelTitle does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
196
197
        /*
198
         * Default filters for the index view
199
         * By default, the search field will run a like query on the title field
200
         */
201
        if (!isset($this->defaultFilters)) {
202
            $this->defaultFilters = [
0 ignored issues
show
Bug Best Practice introduced by
The property defaultFilters does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
203
                'search' => ($this->moduleHas('translations') ? '' : '%') . $this->titleColumnKey,
204
            ];
205
        }
206
207
        /*
208
         * Available columns of the index view
209
         */
210
        if (!isset($this->indexColumns)) {
211
            $this->indexColumns = [
0 ignored issues
show
Bug Best Practice introduced by
The property indexColumns does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
212
                $this->titleColumnKey => [
213
                    'title' => ucfirst($this->titleColumnKey),
214
                    'field' => $this->titleColumnKey,
215
                    'sort' => true,
216
                ],
217
            ];
218
        }
219
220
        /*
221
         * Available columns of the browser view
222
         */
223
        if (!isset($this->browserColumns)) {
224
            $this->browserColumns = [
0 ignored issues
show
Bug Best Practice introduced by
The property browserColumns does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
225
                $this->titleColumnKey => [
226
                    'title' => ucfirst($this->titleColumnKey),
227
                    'field' => $this->titleColumnKey,
228
                ],
229
            ];
230
        }
231
    }
232
233
    /**
234
     * @return void
235
     */
236
    protected function setMiddlewarePermission()
237
    {
238
        $this->middleware('can:list', ['only' => ['index', 'show']]);
239
        $this->middleware('can:edit', ['only' => ['store', 'edit', 'update']]);
240
        $this->middleware('can:duplicate', ['only' => ['duplicate']]);
241
        $this->middleware('can:publish', ['only' => ['publish', 'feature', 'bulkPublish', 'bulkFeature']]);
242
        $this->middleware('can:reorder', ['only' => ['reorder']]);
243
        $this->middleware('can:delete', ['only' => ['destroy', 'bulkDelete', 'restore', 'bulkRestore', 'restoreRevision']]);
244
    }
245
246
    /**
247
     * @param int|null $parentModuleId
248
     * @return array|\Illuminate\View\View
249
     */
250
    public function index($parentModuleId = null)
251
    {
252
        $this->submodule = isset($parentModuleId);
253
        $this->submoduleParentId = $parentModuleId;
254
255
        $indexData = $this->getIndexData($this->submodule ? [
256
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
257
        ] : []);
258
259
        if ($this->request->ajax()) {
260
            return $indexData + ['replaceUrl' => true];
261
        }
262
263
        if ($this->request->has('openCreate') && $this->request->get('openCreate')) {
264
            $indexData += ['openCreate' => true];
265
        }
266
267
        $view = Collection::make([
268
            "$this->viewPrefix.index",
269
            "twill::$this->moduleName.index",
270
            "twill::layouts.listing",
271
        ])->first(function ($view) {
272
            return View::exists($view);
273
        });
274
275
        return View::make($view, $indexData);
276
    }
277
278
    /**
279
     * @return \Illuminate\Http\JsonResponse
280
     */
281
    public function browser()
282
    {
283
        return Response::json($this->getBrowserData());
284
    }
285
286
    /**
287
     * @param int|null $parentModuleId
288
     * @return \Illuminate\Http\JsonResponse
289
     */
290
    public function store($parentModuleId = null)
291
    {
292
        $input = $this->validateFormRequest()->all();
293
        $optionalParent = $parentModuleId ? [$this->getParentModuleForeignKey() => $parentModuleId] : [];
294
295
        $item = $this->repository->create($input + $optionalParent);
296
297
        activity()->performedOn($item)->log('created');
298
299
        $this->fireEvent($input);
300
301
        Session::put($this->moduleName . '_retain', true);
302
303
        if ($this->getIndexOption('editInModal')) {
304
            return $this->respondWithSuccess('Content saved. All good!');
305
        }
306
307
        return $this->respondWithRedirect(moduleRoute(
308
            $this->moduleName,
309
            $this->routePrefix,
310
            'edit',
311
            array_filter([$parentModuleId]) + ['id' => $item->id]
312
        ));
313
    }
314
315
    /**
316
     * @param int|$id
317
     * @param int|null $submoduleId
318
     * @return \Illuminate\Http\RedirectResponse
319
     */
320
    public function show($id, $submoduleId = null)
321
    {
322
        if ($this->getIndexOption('editInModal')) {
323
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
324
        }
325
326
        return $this->redirectToForm($submoduleId ?? $id);
327
    }
328
329
    /**
330
     * @param int $id
331
     * @param int|null $submoduleId
332
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
333
     */
334
    public function edit($id, $submoduleId = null)
335
    {
336
        $this->submodule = isset($submoduleId);
337
        $this->submoduleParentId = $id;
338
339
        if ($this->getIndexOption('editInModal')) {
340
            return $this->request->ajax()
341
            ? Response::json($this->modalFormData($submoduleId ?? $id))
342
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
343
        }
344
345
        $this->setBackLink();
346
347
        $view = Collection::make([
348
            "$this->viewPrefix.form",
349
            "twill::$this->moduleName.form",
350
            "twill::layouts.form",
351
        ])->first(function ($view) {
352
            return View::exists($view);
353
        });
354
355
        return View::make($view, $this->form($submoduleId ?? $id));
356
    }
357
358
    /**
359
     * @param int $id
360
     * @param int|null $submoduleId
361
     * @return \Illuminate\Http\JsonResponse
362
     */
363
    public function update($id, $submoduleId = null)
364
    {
365
        $this->submodule = isset($submoduleId);
366
        $this->submoduleParentId = $id;
367
368
        $item = $this->repository->getById($submoduleId ?? $id);
369
        $input = $this->request->all();
370
371
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
372
            return $this->respondWithRedirect(moduleRoute(
373
                $this->moduleName,
374
                $this->routePrefix,
375
                'edit',
376
                ['id' => $id]
377
            ));
378
        } else {
379
            $formRequest = $this->validateFormRequest();
380
381
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
382
383
            activity()->performedOn($item)->log('updated');
384
385
            $this->fireEvent();
386
387
            if (isset($input['cmsSaveType'])) {
388
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
389
                    return $this->respondWithRedirect($this->getBackLink());
390
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
391
                    return $this->respondWithRedirect(moduleRoute(
392
                        $this->moduleName,
393
                        $this->routePrefix,
394
                        'index',
395
                        ['openCreate' => true]
396
                    ));
397
                } elseif ($input['cmsSaveType'] === 'restore') {
398
                    Session::flash('status', "Revision restored.");
399
400
                    return $this->respondWithRedirect(moduleRoute(
401
                        $this->moduleName,
402
                        $this->routePrefix,
403
                        'edit',
404
                        ['id' => $id]
405
                    ));
406
                }
407
            }
408
409
            if ($this->moduleHas('revisions')) {
410
                return Response::json([
411
                    'message' => 'Content saved. All good!',
412
                    'variant' => FlashLevel::SUCCESS,
413
                    'revisions' => $item->revisionsArray(),
414
                ]);
415
            }
416
417
            return $this->respondWithSuccess('Content saved. All good!');
418
        }
419
    }
420
421
    /**
422
     * @param int $id
423
     * @return \Illuminate\View\View
424
     */
425
    public function preview($id)
426
    {
427
        if ($this->request->has('revisionId')) {
428
            $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

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

431
            /** @scrutinizer ignore-call */ 
432
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
432
        }
433
434
        if ($this->request->has('activeLanguage')) {
435
            App::setLocale($this->request->get('activeLanguage'));
436
        }
437
438
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
0 ignored issues
show
Bug Best Practice introduced by
The property previewView does not exist on A17\Twill\Http\Controllers\Admin\ModuleController. Did you maybe forget to declare it?
Loading history...
439
440
        return View::exists($previewView) ? View::make($previewView, array_replace([
441
            'item' => $item,
442
        ], $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

442
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
443
            'moduleName' => Str::singular($this->moduleName),
444
        ]);
445
    }
446
447
    /**
448
     * @param int $id
449
     * @return \Illuminate\View\View
450
     */
451
    public function restoreRevision($id)
452
    {
453
        if ($this->request->has('revisionId')) {
454
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
455
            $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...
456
            $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...
457
        } else {
458
            throw new NotFoundHttpException();
459
        }
460
461
        $this->setBackLink();
462
463
        $view = Collection::make([
464
            "$this->viewPrefix.form",
465
            "twill::$this->moduleName.form",
466
            "twill::layouts.form",
467
        ])->first(function ($view) {
468
            return View::exists($view);
469
        });
470
471
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
472
        $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

472
        /** @scrutinizer ignore-call */ 
473
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
473
474
        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...
475
476
        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

476
        return View::make($view, $this->form($id, /** @scrutinizer ignore-type */ $item));
Loading history...
477
    }
478
479
    /**
480
     * @return \Illuminate\Http\JsonResponse
481
     */
482
    public function publish()
483
    {
484
        try {
485
            if ($this->repository->updateBasic($this->request->get('id'), [
486
                'published' => !$this->request->get('active'),
487
            ])) {
488
                activity()->performedOn(
489
                    $this->repository->getById($this->request->get('id'))
490
                )->log(
491
                    ($this->request->get('active') ? 'un' : '') . 'published'
492
                );
493
494
                $this->fireEvent();
495
496
                return $this->respondWithSuccess(
497
                    $this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'published!'
498
                );
499
            }
500
        } catch (\Exception $e) {
501
            \Log::error($e);
502
        }
503
504
        return $this->respondWithError(
505
            $this->modelTitle . ' was not published. Something wrong happened!'
506
        );
507
    }
508
509
    /**
510
     * @return \Illuminate\Http\JsonResponse
511
     */
512
    public function bulkPublish()
513
    {
514
        try {
515
            if ($this->repository->updateBasic(explode(',', $this->request->get('ids')), [
516
                'published' => $this->request->get('publish'),
517
            ])) {
518
                $this->fireEvent();
519
520
                return $this->respondWithSuccess(
521
                    $this->modelTitle . ' items ' . ($this->request->get('publish') ? '' : 'un') . 'published!'
522
                );
523
            }
524
        } catch (\Exception $e) {
525
            \Log::error($e);
526
        }
527
528
        return $this->respondWithError(
529
            $this->modelTitle . ' items were not published. Something wrong happened!'
530
        );
531
    }
532
533
    /**
534
     * @param int $id
535
     * @param int|null $submoduleId
536
     * @return \Illuminate\Http\JsonResponse
537
     */
538
    public function duplicate($id, $submoduleId = null)
539
    {
540
541
        $item = $this->repository->getById($submoduleId ?? $id);
542
        if ($newItem = $this->repository->duplicate($submoduleId ?? $id)) {
543
            $this->fireEvent();
544
            activity()->performedOn($item)->log('duplicated');
545
546
            return Response::json([
547
                'message' => $this->modelTitle . ' duplicated with Success!',
548
                'variant' => FlashLevel::SUCCESS,
549
                'redirect' => moduleRoute(
550
                    $this->moduleName,
551
                    $this->routePrefix,
552
                    'edit',
553
                    array_filter(['id' => $newItem->id])
554
                )
555
            ]);
556
        }
557
558
        return $this->respondWithError($this->modelTitle . ' was not duplicated. Something wrong happened!');
559
    }
560
561
    /**
562
     * @param int $id
563
     * @param int|null $submoduleId
564
     * @return \Illuminate\Http\JsonResponse
565
     */
566
    public function destroy($id, $submoduleId = null)
567
    {
568
        $item = $this->repository->getById($submoduleId ?? $id);
569
        if ($this->repository->delete($submoduleId ?? $id)) {
570
            $this->fireEvent();
571
            activity()->performedOn($item)->log('deleted');
572
            return $this->respondWithSuccess($this->modelTitle . ' moved to trash!');
573
        }
574
575
        return $this->respondWithError($this->modelTitle . ' was not moved to trash. Something wrong happened!');
576
    }
577
578
    /**
579
     * @return \Illuminate\Http\JsonResponse
580
     */
581
    public function bulkDelete()
582
    {
583
        if ($this->repository->bulkDelete(explode(',', $this->request->get('ids')))) {
584
            $this->fireEvent();
585
            return $this->respondWithSuccess($this->modelTitle . ' items moved to trash!');
586
        }
587
588
        return $this->respondWithError($this->modelTitle . ' items were not moved to trash. Something wrong happened!');
589
    }
590
591
    /**
592
     * @return \Illuminate\Http\JsonResponse
593
     */
594
    public function restore()
595
    {
596
        if ($this->repository->restore($this->request->get('id'))) {
597
            $this->fireEvent();
598
            activity()->performedOn($this->repository->getById($this->request->get('id')))->log('restored');
599
            return $this->respondWithSuccess($this->modelTitle . ' restored!');
600
        }
601
602
        return $this->respondWithError($this->modelTitle . ' was not restored. Something wrong happened!');
603
    }
604
605
    /**
606
     * @return \Illuminate\Http\JsonResponse
607
     */
608
    public function bulkRestore()
609
    {
610
        if ($this->repository->bulkRestore(explode(',', $this->request->get('ids')))) {
611
            $this->fireEvent();
612
            return $this->respondWithSuccess($this->modelTitle . ' items restored!');
613
        }
614
615
        return $this->respondWithError($this->modelTitle . ' items were not restored. Something wrong happened!');
616
    }
617
618
    /**
619
     * @return \Illuminate\Http\JsonResponse
620
     */
621
    public function feature()
622
    {
623
        if (($id = $this->request->get('id'))) {
624
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
625
            $featured = !$this->request->get('active');
626
627
            if ($this->repository->isUniqueFeature()) {
628
                if ($featured) {
629
                    $this->repository->updateBasic(null, [$featuredField => false]);
630
                    $this->repository->updateBasic($id, [$featuredField => $featured]);
631
                }
632
            } else {
633
                $this->repository->updateBasic($id, [$featuredField => $featured]);
634
            }
635
636
            activity()->performedOn(
637
                $this->repository->getById($id)
638
            )->log(
639
                ($this->request->get('active') ? 'un' : '') . 'featured'
640
            );
641
642
            $this->fireEvent();
643
            return $this->respondWithSuccess($this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'featured!');
644
        }
645
646
        return $this->respondWithError($this->modelTitle . ' was not featured. Something wrong happened!');
647
    }
648
649
    /**
650
     * @return \Illuminate\Http\JsonResponse
651
     */
652
    public function bulkFeature()
653
    {
654
        if (($ids = explode(',', $this->request->get('ids')))) {
655
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
656
            $featured = $this->request->get('feature') ?? true;
657
            // we don't need to check if unique feature since bulk operation shouldn't be allowed in this case
658
            $this->repository->updateBasic($ids, [$featuredField => $featured]);
659
            $this->fireEvent();
660
            return $this->respondWithSuccess($this->modelTitle . ' items ' . ($this->request->get('feature') ? '' : 'un') . 'featured!');
661
        }
662
663
        return $this->respondWithError($this->modelTitle . ' items were not featured. Something wrong happened!');
664
    }
665
666
    /**
667
     * @return \Illuminate\Http\JsonResponse
668
     */
669
    public function reorder()
670
    {
671
        if (($values = $this->request->get('ids')) && !empty($values)) {
672
            $this->repository->setNewOrder($values);
673
            $this->fireEvent();
674
            return $this->respondWithSuccess($this->modelTitle . ' order changed!');
675
        }
676
677
        return $this->respondWithError($this->modelTitle . ' order was not changed. Something wrong happened!');
678
    }
679
680
    /**
681
     * @return \Illuminate\Http\JsonResponse
682
     */
683
    public function tags()
684
    {
685
        $query = $this->request->input('q');
686
        $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

686
        /** @scrutinizer ignore-call */ 
687
        $tags = $this->repository->getTags($query);
Loading history...
687
688
        return Response::json(['items' => $tags->map(function ($tag) {
689
            return $tag->name;
690
        })], 200);
691
    }
692
693
    /**
694
     * @param array $prependScope
695
     * @return array
696
     */
697
    protected function getIndexData($prependScope = [])
698
    {
699
        $scopes = $this->filterScope($prependScope);
700
        $items = $this->getIndexItems($scopes);
701
702
        $data = [
703
            'tableData' => $this->getIndexTableData($items),
704
            'tableColumns' => $this->getIndexTableColumns($items),
705
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
706
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
707
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
708
            'filterLinks' => $this->filterLinks ?? [],
709
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
710
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
711
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
712
            'defaultOffset' => $this->perPage,
713
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
714
715
        $baseUrl = $this->getPermalinkBaseUrl();
716
717
        $options = [
718
            'moduleName' => $this->moduleName,
719
            'reorder' => $this->getIndexOption('reorder'),
720
            'create' => $this->getIndexOption('create'),
721
            'duplicate' => $this->getIndexOption('duplicate'),
722
            'translate' => $this->moduleHas('translations'),
723
            'permalink' => $this->getIndexOption('permalink'),
724
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
725
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
726
            'baseUrl' => $baseUrl,
727
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
728
        ];
729
730
        return array_replace_recursive($data + $options, $this->indexData($this->request));
731
    }
732
733
    /**
734
     * @param Request $request
735
     * @return array
736
     */
737
    protected function indexData($request)
738
    {
739
        return [];
740
    }
741
742
    /**
743
     * @param array $scopes
744
     * @param bool $forcePagination
745
     * @return \Illuminate\Database\Eloquent\Collection
746
     */
747
    protected function getIndexItems($scopes = [], $forcePagination = false)
748
    {
749
        return $this->transformIndexItems($this->repository->get(
750
            $this->indexWith,
751
            $scopes,
752
            $this->orderScope(),
753
            $this->request->get('offset') ?? $this->perPage ?? 50,
754
            $forcePagination
755
        ));
756
    }
757
758
    /**
759
     * @param \Illuminate\Database\Eloquent\Collection $items
760
     * @return \Illuminate\Database\Eloquent\Collection
761
     */
762
    protected function transformIndexItems($items)
763
    {
764
        return $items;
765
    }
766
767
    /**
768
     * @param \Illuminate\Database\Eloquent\Collection $items
769
     * @return array
770
     */
771
    protected function getIndexTableData($items)
772
    {
773
        $translated = $this->moduleHas('translations');
774
        return $items->map(function ($item) use ($translated) {
775
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
776
                return $this->getItemColumnData($item, $column);
777
            })->toArray();
778
779
            $name = $columnsData[$this->titleColumnKey];
780
781
            if (empty($name)) {
782
                if ($this->moduleHas('translations')) {
783
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
784
785
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
786
                        $name = $fallBackTranslation->{$this->titleColumnKey};
787
                    }
788
                }
789
790
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
791
            }
792
793
            unset($columnsData[$this->titleColumnKey]);
794
795
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
796
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
797
            $canEdit = $this->getIndexOption('edit');
798
            $canDuplicate = $this->getIndexOption('duplicate');
799
800
            return array_replace([
801
                'id' => $item->id,
802
                'name' => $name,
803
                'publish_start_date' => $item->publish_start_date,
804
                'publish_end_date' => $item->publish_end_date,
805
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
806
                'duplicate' => $canDuplicate ? $this->getModuleRoute($item->id, 'duplicate') : null,
807
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
808
            ] + ($this->getIndexOption('editInModal') ? [
809
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
810
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
811
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
812
                'published' => $item->published,
813
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
814
                'featured' => $item->{$this->featureField},
815
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
816
                'deleted' => true,
817
            ] : []) + ($translated ? [
818
                'languages' => $item->getActiveLanguages(),
819
            ] : []) + $columnsData, $this->indexItemData($item));
820
        })->toArray();
821
    }
822
823
    /**
824
     * @param \A17\Twill\Models\Model $item
825
     * @return array
826
     */
827
    protected function indexItemData($item)
828
    {
829
        return [];
830
    }
831
832
    /**
833
     * @param \A17\Twill\Models\Model $item
834
     * @param array $column
835
     * @return array
836
     */
837
    protected function getItemColumnData($item, $column)
838
    {
839
        if (isset($column['thumb']) && $column['thumb']) {
840
            if (isset($column['present']) && $column['present']) {
841
                return [
842
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
843
                ];
844
            } else {
845
                $variant = isset($column['variant']);
846
                $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...
847
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
848
                $params = $variant && isset($column['variant']['params'])
849
                ? $column['variant']['params']
850
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
851
852
                return [
853
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
854
                ];
855
            }
856
        }
857
858
        if (isset($column['nested']) && $column['nested']) {
859
            $field = $column['nested'];
860
            $nestedCount = $item->{$column['nested']}->count();
861
            $value = '<a href="';
862
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
863
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
864
                ? Str::plural($column['title'])
865
                : Str::singular($column['title']))) . '</a>';
866
        } else {
867
            $field = $column['field'];
868
            $value = $item->$field;
869
        }
870
871
        if (isset($column['relationship'])) {
872
            $field = $column['relationship'] . ucfirst($column['field']);
873
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
874
        } elseif (isset($column['present']) && $column['present']) {
875
            $value = $item->presentAdmin()->{$column['field']};
876
        }
877
878
        return [
879
            "$field" => $value,
880
        ];
881
    }
882
883
    /**
884
     * @param \Illuminate\Database\Eloquent\Collection $items
885
     * @return array
886
     */
887
    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

887
    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...
888
    {
889
        $tableColumns = [];
890
        $visibleColumns = $this->request->get('columns') ?? false;
891
892
        if (isset(Arr::first($this->indexColumns)['thumb'])
893
            && Arr::first($this->indexColumns)['thumb']
894
        ) {
895
            array_push($tableColumns, [
896
                'name' => 'thumbnail',
897
                'label' => 'Thumbnail',
898
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
899
                'optional' => true,
900
                'sortable' => false,
901
            ]);
902
            array_shift($this->indexColumns);
903
        }
904
905
        if ($this->getIndexOption('feature')) {
906
            array_push($tableColumns, [
907
                'name' => 'featured',
908
                'label' => 'Featured',
909
                'visible' => true,
910
                'optional' => false,
911
                'sortable' => false,
912
            ]);
913
        }
914
915
        if ($this->getIndexOption('publish')) {
916
            array_push($tableColumns, [
917
                'name' => 'published',
918
                'label' => 'Published',
919
                'visible' => true,
920
                'optional' => false,
921
                'sortable' => false,
922
            ]);
923
        }
924
925
        array_push($tableColumns, [
926
            'name' => 'name',
927
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? 'Name',
928
            'visible' => true,
929
            'optional' => false,
930
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
931
        ]);
932
933
        unset($this->indexColumns[$this->titleColumnKey]);
934
935
        foreach ($this->indexColumns as $column) {
936
            $columnName = isset($column['relationship'])
937
            ? $column['relationship'] . ucfirst($column['field'])
938
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
939
940
            array_push($tableColumns, [
941
                'name' => $columnName,
942
                'label' => $column['title'],
943
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
944
                'optional' => $column['optional'] ?? true,
945
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
946
                'html' => $column['html'] ?? false,
947
            ]);
948
        }
949
950
        if ($this->moduleHas('translations')) {
951
            array_push($tableColumns, [
952
                'name' => 'languages',
953
                'label' => 'Languages',
954
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
955
                'optional' => true,
956
                'sortable' => false,
957
            ]);
958
        }
959
960
        return $tableColumns;
961
    }
962
963
    /**
964
     * @param \Illuminate\Database\Eloquent\Collection $items
965
     * @param array $scopes
966
     * @return array
967
     */
968
    protected function getIndexTableMainFilters($items, $scopes = [])
969
    {
970
        $statusFilters = [];
971
972
        $scope = ($this->submodule ? [
973
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
974
        ] : []) + $scopes;
975
976
        array_push($statusFilters, [
977
            'name' => 'All items',
978
            'slug' => 'all',
979
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
980
        ]);
981
982
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
983
            array_push($statusFilters, [
984
                'name' => 'Mine',
985
                'slug' => 'mine',
986
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
987
            ]);
988
        }
989
990
        if ($this->getIndexOption('publish')) {
991
            array_push($statusFilters, [
992
                'name' => 'Published',
993
                'slug' => 'published',
994
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
995
            ], [
996
                'name' => 'Draft',
997
                'slug' => 'draft',
998
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
999
            ]);
1000
        }
1001
1002
        if ($this->getIndexOption('restore')) {
1003
            array_push($statusFilters, [
1004
                'name' => 'Trash',
1005
                'slug' => 'trash',
1006
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1007
            ]);
1008
        }
1009
1010
        return $statusFilters;
1011
    }
1012
1013
    /**
1014
     * @param string $moduleName
1015
     * @param string $routePrefix
1016
     * @return array
1017
     */
1018
    protected function getIndexUrls($moduleName, $routePrefix)
1019
    {
1020
        return Collection::make([
1021
            'store',
1022
            'publish',
1023
            'bulkPublish',
1024
            'restore',
1025
            'bulkRestore',
1026
            'reorder',
1027
            'feature',
1028
            'bulkFeature',
1029
            'bulkDelete',
1030
        ])->mapWithKeys(function ($endpoint) use ($moduleName, $routePrefix) {
0 ignored issues
show
Unused Code introduced by
The import $moduleName is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
Unused Code introduced by
The import $routePrefix is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
1031
            return [
1032
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1033
                    $this->moduleName, $this->routePrefix, $endpoint,
1034
                    $this->submodule ? [$this->submoduleParentId] : []
1035
                ) : null,
1036
            ];
1037
        })->toArray();
1038
    }
1039
1040
    /**
1041
     * @param string $option
1042
     * @return bool
1043
     */
1044
    protected function getIndexOption($option)
1045
    {
1046
        return once(function () use ($option) {
1047
            $customOptionNamesMapping = [
1048
                'store' => 'create',
1049
            ];
1050
1051
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1052
1053
            $authorizableOptions = [
1054
                'create' => 'edit',
1055
                'edit' => 'edit',
1056
                'publish' => 'publish',
1057
                'feature' => 'feature',
1058
                'reorder' => 'reorder',
1059
                'delete' => 'delete',
1060
                'duplicate' => 'duplicate',
1061
                'restore' => 'delete',
1062
                'bulkPublish' => 'publish',
1063
                'bulkRestore' => 'delete',
1064
                'bulkFeature' => 'feature',
1065
                'bulkDelete' => 'delete',
1066
                'bulkEdit' => 'edit',
1067
                'editInModal' => 'edit',
1068
            ];
1069
1070
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1071
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1072
        });
1073
    }
1074
1075
    /**
1076
     * @param array $prependScope
1077
     * @return array
1078
     */
1079
    protected function getBrowserData($prependScope = [])
1080
    {
1081
        if ($this->request->has('except')) {
1082
            $prependScope['exceptIds'] = $this->request->get('except');
1083
        }
1084
1085
        $scopes = $this->filterScope($prependScope);
1086
        $items = $this->getBrowserItems($scopes);
1087
        $data = $this->getBrowserTableData($items);
1088
1089
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1090
    }
1091
1092
    /**
1093
     * @param \Illuminate\Database\Eloquent\Collection $items
1094
     * @return array
1095
     */
1096
    protected function getBrowserTableData($items)
1097
    {
1098
        $withImage = $this->moduleHas('medias');
1099
1100
        return $items->map(function ($item) use ($withImage) {
1101
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item, $withImage) {
0 ignored issues
show
Unused Code introduced by
The import $withImage is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
1102
                return $this->getItemColumnData($item, $column);
1103
            })->toArray();
1104
1105
            $name = $columnsData[$this->titleColumnKey];
1106
            unset($columnsData[$this->titleColumnKey]);
1107
1108
            return [
1109
                'id' => $item->id,
1110
                'name' => $name,
1111
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1112
                '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

1112
                'endpointType' => $this->repository->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
1113
            ] + $columnsData + ($withImage && !array_key_exists('thumbnail', $columnsData) ? [
1114
                'thumbnail' => $item->defaultCmsImage(['w' => 100, 'h' => 100]),
1115
            ] : []);
1116
        })->toArray();
1117
    }
1118
1119
    /**
1120
     * @param array $scopes
1121
     * @return \Illuminate\Database\Eloquent\Collection
1122
     */
1123
    protected function getBrowserItems($scopes = [])
1124
    {
1125
        return $this->getIndexItems($scopes, true);
1126
    }
1127
1128
    /**
1129
     * @param array $prepend
1130
     * @return array
1131
     */
1132
    protected function filterScope($prepend = [])
1133
    {
1134
        $scope = [];
1135
1136
        $requestFilters = $this->getRequestFilters();
1137
1138
        $this->filters = array_merge($this->filters, $this->defaultFilters);
1139
1140
        if (array_key_exists('status', $requestFilters)) {
1141
            switch ($requestFilters['status']) {
1142
                case 'published':
1143
                    $scope['published'] = true;
1144
                    break;
1145
                case 'draft':
1146
                    $scope['draft'] = true;
1147
                    break;
1148
                case 'trash':
1149
                    $scope['onlyTrashed'] = true;
1150
                    break;
1151
                case 'mine':
1152
                    $scope['mine'] = true;
1153
                    break;
1154
            }
1155
1156
            unset($requestFilters['status']);
1157
        }
1158
1159
        foreach ($this->filters as $key => $field) {
1160
            if (array_key_exists($key, $requestFilters)) {
1161
                $value = $requestFilters[$key];
1162
                if ($value == 0 || !empty($value)) {
1163
                    // add some syntaxic sugar to scope the same filter on multiple columns
1164
                    $fieldSplitted = explode('|', $field);
1165
                    if (count($fieldSplitted) > 1) {
1166
                        $requestValue = $requestFilters[$key];
1167
                        Collection::make($fieldSplitted)->each(function ($scopeKey) use (&$scope, $requestValue) {
1168
                            $scope[$scopeKey] = $requestValue;
1169
                        });
1170
                    } else {
1171
                        $scope[$field] = $requestFilters[$key];
1172
                    }
1173
                }
1174
            }
1175
        }
1176
1177
        return $prepend + $scope;
1178
    }
1179
1180
    /**
1181
     * @return array
1182
     */
1183
    protected function getRequestFilters()
1184
    {
1185
        if ($this->request->has('search')) {
1186
            return ['search' => $this->request->get('search')];
1187
        }
1188
1189
        return json_decode($this->request->get('filter'), true) ?? [];
1190
    }
1191
1192
    /**
1193
     * @return array
1194
     */
1195
    protected function orderScope()
1196
    {
1197
        $orders = [];
1198
        if ($this->request->has('sortKey') && $this->request->has('sortDir')) {
1199
            if (($key = $this->request->get('sortKey')) == 'name') {
1200
                $sortKey = $this->titleColumnKey;
1201
            } elseif (!empty($key)) {
1202
                $sortKey = $key;
1203
            }
1204
1205
            if (isset($sortKey)) {
1206
                $orders[$this->indexColumns[$sortKey]['sortKey'] ?? $sortKey] = $this->request->get('sortDir');
1207
            }
1208
        }
1209
1210
        // don't apply default orders if reorder is enabled
1211
        $reorder = $this->getIndexOption('reorder');
1212
        $defaultOrders = ($reorder ? [] : ($this->defaultOrders ?? []));
1213
1214
        return $orders + $defaultOrders;
1215
    }
1216
1217
    /**
1218
     * @param int $id
1219
     * @param \A17\Twill\Models\Model|null $item
1220
     * @return array
1221
     */
1222
    protected function form($id, $item = null)
1223
    {
1224
        $item = $item ?? $this->repository->getById($id, $this->formWith, $this->formWithCount);
1225
1226
        $fullRoutePrefix = 'admin.' . ($this->routePrefix ? $this->routePrefix . '.' : '') . $this->moduleName . '.';
1227
        $previewRouteName = $fullRoutePrefix . 'preview';
1228
        $restoreRouteName = $fullRoutePrefix . 'restoreRevision';
1229
1230
        $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...
1231
1232
        $data = [
1233
            'item' => $item,
1234
            'moduleName' => $this->moduleName,
1235
            'routePrefix' => $this->routePrefix,
1236
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
1237
            '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...
1238
            'translate' => $this->moduleHas('translations'),
1239
            'permalink' => $this->getIndexOption('permalink'),
1240
            'form_fields' => $this->repository->getFormFields($item),
1241
            'baseUrl' => $baseUrl,
1242
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
1243
            'saveUrl' => $this->getModuleRoute($item->id, 'update'),
1244
            'editor' => $this->moduleHas('revisions') && $this->moduleHas('blocks') && !$this->disableEditor,
1245
            'blockPreviewUrl' => Route::has('admin.blocks.preview')? URL::route('admin.blocks.preview') : '#',
1246
            'revisions' => $this->moduleHas('revisions') ? $item->revisionsArray() : null,
1247
        ] + (Route::has($previewRouteName) ? [
1248
            '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

1248
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1249
        ] : [])
1250
             + (Route::has($restoreRouteName) ? [
1251
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1252
        ] : []);
1253
1254
        return array_replace_recursive($data, $this->formData($this->request));
1255
    }
1256
1257
    /**
1258
     * @param int $id
1259
     * @return array
1260
     */
1261
    protected function modalFormData($id)
1262
    {
1263
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1264
        $fields = $this->repository->getFormFields($item);
1265
        $data = [];
1266
1267
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1268
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1269
                $data['fields'][] = [
1270
                    'name' => $fieldName,
1271
                    'value' => $fieldValue,
1272
                ];
1273
            }
1274
1275
            $data['languages'] = $item->getActiveLanguages();
1276
1277
            unset($fields['translations']);
1278
        }
1279
1280
        foreach ($fields as $fieldName => $fieldValue) {
1281
            $data['fields'][] = [
1282
                'name' => $fieldName,
1283
                'value' => $fieldValue,
1284
            ];
1285
        }
1286
1287
        return array_replace_recursive($data, $this->formData($this->request));
1288
    }
1289
1290
    /**
1291
     * @param Request $request
1292
     * @return array
1293
     */
1294
    protected function formData($request)
1295
    {
1296
        return [];
1297
    }
1298
1299
    /**
1300
     * @param Request $item
1301
     * @return array
1302
     */
1303
    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

1303
    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...
1304
    {
1305
        return [];
1306
    }
1307
1308
    /**
1309
     * @return \A17\Twill\Http\Requests\Admin\Request
1310
     */
1311
    protected function validateFormRequest()
1312
    {
1313
        $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

1313
        $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...
1314
            return Auth::guard('twill_users')->user()->cannot($permission);
1315
        })->keys();
1316
1317
        $unauthorizedFields->each(function ($field) {
1318
            $this->request->offsetUnset($field);
1319
        });
1320
1321
        return App::make("$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request");
1322
    }
1323
1324
    /**
1325
     * @return string
1326
     */
1327
    protected function getNamespace()
1328
    {
1329
        return $this->namespace ?? Config::get('twill.namespace');
1330
    }
1331
1332
    /**
1333
     * @return string
1334
     */
1335
    protected function getRoutePrefix()
1336
    {
1337
        if ($this->request->route() != null) {
1338
            $routePrefix = ltrim(str_replace(Config::get('twill.admin_app_path'), '', $this->request->route()->getPrefix()), "/");
1339
            return str_replace("/", ".", $routePrefix);
1340
        }
1341
1342
        return '';
1343
    }
1344
1345
    /**
1346
     * @return string
1347
     */
1348
    protected function getModelName()
1349
    {
1350
        return $this->modelName ?? ucfirst(Str::singular($this->moduleName));
1351
    }
1352
1353
    /**
1354
     * @return \A17\Twill\Repositories\ModuleRepository
1355
     */
1356
    protected function getRepository()
1357
    {
1358
        return App::make("$this->namespace\Repositories\\" . $this->modelName . "Repository");
1359
    }
1360
1361
    /**
1362
     * @return string
1363
     */
1364
    protected function getViewPrefix()
1365
    {
1366
        return "admin.$this->moduleName";
1367
    }
1368
1369
    /**
1370
     * @return string
1371
     */
1372
    protected function getModelTitle()
1373
    {
1374
        return camelCaseToWords($this->modelName);
1375
    }
1376
1377
    /**
1378
     * @return string
1379
     */
1380
    protected function getParentModuleForeignKey()
1381
    {
1382
        return Str::singular(explode('.', $this->moduleName)[0]) . '_id';
1383
    }
1384
1385
    /**
1386
     * @return string
1387
     */
1388
    protected function getPermalinkBaseUrl()
1389
    {
1390
        return $this->request->getScheme() . '://' . Config::get('app.url') . '/'
1391
            . ($this->moduleHas('translations') ? '{language}/' : '')
1392
            . ($this->moduleHas('revisions') ? '{preview}/' : '')
1393
            . ($this->permalinkBase ?? $this->moduleName)
0 ignored issues
show
Bug Best Practice introduced by
The property permalinkBase does not exist on A17\Twill\Http\Controllers\Admin\ModuleController. Did you maybe forget to declare it?
Loading history...
1394
            . (isset($this->permalinkBase) && empty($this->permalinkBase) ? '' : '/');
1395
    }
1396
1397
    /**
1398
     * @param string $baseUrl
1399
     * @return string
1400
     */
1401
    protected function getPermalinkPrefix($baseUrl)
1402
    {
1403
        return rtrim(str_replace(['http://', 'https://', '{preview}/', '{language}/'], '', $baseUrl), "/") . '/';
1404
    }
1405
1406
    /**
1407
     * @param int $id
1408
     * @param string $action
1409
     * @return string
1410
     */
1411
    protected function getModuleRoute($id, $action)
1412
    {
1413
        return moduleRoute(
1414
            $this->moduleName,
1415
            $this->routePrefix,
1416
            $action,
1417
            array_merge($this->submodule ? [$this->submoduleParentId] : [], [$id])
1418
        );
1419
    }
1420
1421
    /**
1422
     * @param string $behavior
1423
     * @return bool
1424
     */
1425
    protected function moduleHas($behavior)
1426
    {
1427
        return classHasTrait($this->repository, 'A17\Twill\Repositories\Behaviors\Handle' . ucfirst($behavior));
1428
    }
1429
1430
    /**
1431
     * @param string|null $back_link
1432
     * @param array $params
1433
     * @return void
1434
     */
1435
    protected function setBackLink($back_link = null, $params = [])
1436
    {
1437
        if (!isset($back_link)) {
1438
            if (($back_link = Session::get($this->getBackLinkSessionKey())) == null) {
1439
                $back_link = $this->request->headers->get('referer') ?? moduleRoute(
1440
                    $this->moduleName,
1441
                    $this->routePrefix,
1442
                    'index',
1443
                    $params
1444
                );
1445
            }
1446
        }
1447
1448
        if (!Session::get($this->moduleName . '_retain')) {
1449
            Session::put($this->getBackLinkSessionKey(), $back_link);
1450
        } else {
1451
            Session::put($this->moduleName . '_retain', false);
1452
        }
1453
    }
1454
1455
    /**
1456
     * @param string|null $fallback
1457
     * @param array $params
1458
     * @return string
1459
     */
1460
    protected function getBackLink($fallback = null, $params = [])
1461
    {
1462
        $back_link = Session::get($this->getBackLinkSessionKey(), $fallback);
1463
        return $back_link ?? moduleRoute($this->moduleName, $this->routePrefix, 'index', $params);
1464
    }
1465
1466
    /**
1467
     * @return string
1468
     */
1469
    protected function getBackLinkSessionKey()
1470
    {
1471
        return $this->moduleName . ($this->submodule ? $this->submoduleParentId ?? '' : '') . '_back_link';
1472
    }
1473
1474
    /**
1475
     * @param int $id
1476
     * @param array $params
1477
     * @return \Illuminate\Http\RedirectResponse
1478
     */
1479
    protected function redirectToForm($id, $params = [])
1480
    {
1481
        Session::put($this->moduleName . '_retain', true);
1482
1483
        return Redirect::to(moduleRoute(
1484
            $this->moduleName,
1485
            $this->routePrefix,
1486
            'edit',
1487
            array_filter($params) + ['id' => $id]
1488
        ));
1489
    }
1490
1491
    /**
1492
     * @param string $message
1493
     * @return \Illuminate\Http\JsonResponse
1494
     */
1495
    protected function respondWithSuccess($message)
1496
    {
1497
        return $this->respondWithJson($message, FlashLevel::SUCCESS);
1498
    }
1499
1500
    /**
1501
     * @param string $redirectUrl
1502
     * @return \Illuminate\Http\JsonResponse
1503
     */
1504
    protected function respondWithRedirect($redirectUrl)
1505
    {
1506
        return Response::json([
1507
            'redirect' => $redirectUrl,
1508
        ]);
1509
    }
1510
1511
    /**
1512
     * @param string $message
1513
     * @return \Illuminate\Http\JsonResponse
1514
     */
1515
    protected function respondWithError($message)
1516
    {
1517
        return $this->respondWithJson($message, FlashLevel::ERROR);
1518
    }
1519
1520
    /**
1521
     * @param string $message
1522
     * @param mixed $variant
1523
     * @return \Illuminate\Http\JsonResponse
1524
     */
1525
    protected function respondWithJson($message, $variant)
1526
    {
1527
        return Response::json([
1528
            'message' => $message,
1529
            'variant' => $variant,
1530
        ]);
1531
    }
1532
1533
    /**
1534
     * @param array $input
1535
     * @return void
1536
     */
1537
    protected function fireEvent($input = [])
1538
    {
1539
        fireCmsEvent('cms-module.saved', $input);
1540
    }
1541
}
1542