Passed
Push — dependabot/npm_and_yarn/docs/w... ( a770a9...3a5b31 )
by
unknown
07:47
created

ModuleController::modalFormData()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

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

366
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.publisher.save-success'));
Loading history...
367
        }
368
369 22
        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...
370
            $params = [
371
                Str::singular(explode('.', $this->moduleName)[0]) => $parentModuleId,
372
                Str::singular(explode('.', $this->moduleName)[1]) => $item->id,
373
            ];
374
        } else {
375
            $params = [
376 22
                Str::singular($this->moduleName) => $item->id,
377
            ];
378
        }
379
380 22
        return $this->respondWithRedirect(moduleRoute(
381 22
            $this->moduleName,
382 22
            $this->routePrefix,
383 22
            'edit',
384
            $params
385
        ));
386
    }
387
388
    /**
389
     * @param int|$id
390
     * @param int|null $submoduleId
391
     * @return \Illuminate\Http\RedirectResponse
392
     */
393 1
    public function show($id, $submoduleId = null)
394
    {
395 1
        if ($this->getIndexOption('editInModal')) {
396
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
397
        }
398
399 1
        return $this->redirectToForm($submoduleId ?? $id);
400
    }
401
402
    /**
403
     * @param int $id
404
     * @param int|null $submoduleId
405
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
406
     */
407 6
    public function edit($id, $submoduleId = null)
408
    {
409 6
        $this->submodule = isset($submoduleId);
410 6
        $this->submoduleParentId = $id;
411
412 6
        if ($this->getIndexOption('editInModal')) {
413 2
            return $this->request->ajax()
414 1
            ? Response::json($this->modalFormData($submoduleId ?? $id))
415 2
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
416
        }
417
418 4
        $this->setBackLink();
419
420 4
        $view = Collection::make([
421 4
            "$this->viewPrefix.form",
422 4
            "twill::$this->moduleName.form",
423 4
            "twill::layouts.form",
424
        ])->first(function ($view) {
425 4
            return View::exists($view);
426 4
        });
427
428 4
        return View::make($view, $this->form($submoduleId ?? $id));
429
    }
430
431
    /**
432
     * @param int $id
433
     * @param int|null $submoduleId
434
     * @return \Illuminate\Http\JsonResponse
435
     */
436 8
    public function update($id, $submoduleId = null)
437
    {
438 8
        $this->submodule = isset($submoduleId);
439 8
        $this->submoduleParentId = $id;
440
441 8
        $item = $this->repository->getById($submoduleId ?? $id);
442 8
        $input = $this->request->all();
443
444 8
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
445
            return $this->respondWithRedirect(moduleRoute(
446
                $this->moduleName,
447
                $this->routePrefix,
448
                'edit',
449
                [Str::singular($this->moduleName) => $id]
450
            ));
451
        } else {
452 8
            $formRequest = $this->validateFormRequest();
453
454 8
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
455
456 8
            activity()->performedOn($item)->log('updated');
457
458 8
            $this->fireEvent();
459
460 8
            if (isset($input['cmsSaveType'])) {
461 8
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
462
                    return $this->respondWithRedirect($this->getBackLink());
463 8
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
464
                    return $this->respondWithRedirect(moduleRoute(
465
                        $this->moduleName,
466
                        $this->routePrefix,
467
                        'index',
468
                        ['openCreate' => true]
469
                    ));
470 8
                } elseif ($input['cmsSaveType'] === 'restore') {
471
                    Session::flash('status', twillTrans('twill::lang.publisher.restore-success'));
472
473
                    return $this->respondWithRedirect(moduleRoute(
474
                        $this->moduleName,
475
                        $this->routePrefix,
476
                        'edit',
477
                        [Str::singular($this->moduleName) => $id]
478
                    ));
479
                }
480
            }
481
482 8
            if ($this->moduleHas('revisions')) {
483 7
                return Response::json([
484 7
                    'message' => twillTrans('twill::lang.publisher.save-success'),
485 7
                    'variant' => FlashLevel::SUCCESS,
486 7
                    'revisions' => $item->revisionsArray(),
487
                ]);
488
            }
489
490 1
            return $this->respondWithSuccess(twillTrans('twill::lang.publisher.save-success'));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.publisher.save-success') can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...r::respondWithSuccess() does only seem to accept string, 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

490
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.publisher.save-success'));
Loading history...
491
        }
492
    }
493
494
    /**
495
     * @param int $id
496
     * @return \Illuminate\View\View
497
     */
498 1
    public function preview($id)
499
    {
500 1
        if ($this->request->has('revisionId')) {
501
            $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

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

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

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

545
        /** @scrutinizer ignore-call */ 
546
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
546
547 1
        Session::flash('restoreMessage', twillTrans('twill::lang.publisher.restore-message', ['user' => $revision->byUser, 'date' => $date]));
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...
548
549 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

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

785
        /** @scrutinizer ignore-call */ 
786
        $tags = $this->repository->getTags($query);
Loading history...
786
787
        return Response::json(['items' => $tags->map(function ($tag) {
788
            return $tag->name;
789 1
        })], 200);
790
    }
791
792
    /**
793
     * @param array $prependScope
794
     * @return array
795
     */
796 6
    protected function getIndexData($prependScope = [])
797
    {
798 6
        $scopes = $this->filterScope($prependScope);
799 6
        $items = $this->getIndexItems($scopes);
800
801
        $data = [
802 6
            'tableData' => $this->getIndexTableData($items),
803 6
            'tableColumns' => $this->getIndexTableColumns($items),
804 6
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
805 6
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
806 6
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
807 6
            'filterLinks' => $this->filterLinks ?? [],
808 6
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
809 6
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
810 6
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
811 6
            'defaultOffset' => $this->perPage,
812 6
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
813
814 6
        $baseUrl = $this->getPermalinkBaseUrl();
815
816
        $options = [
817 6
            'moduleName' => $this->moduleName,
818 6
            'reorder' => $this->getIndexOption('reorder'),
819 6
            'create' => $this->getIndexOption('create'),
820 6
            'duplicate' => $this->getIndexOption('duplicate'),
821 6
            'translate' => $this->moduleHas('translations'),
822 6
            'translateTitle' => $this->titleIsTranslatable(),
823 6
            'permalink' => $this->getIndexOption('permalink'),
824 6
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
825 6
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
826 6
            'baseUrl' => $baseUrl,
827 6
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
828
        ];
829
830 6
        return array_replace_recursive($data + $options, $this->indexData($this->request));
831
    }
832
833
    /**
834
     * @param Request $request
835
     * @return array
836
     */
837 5
    protected function indexData($request)
838
    {
839 5
        return [];
840
    }
841
842
    /**
843
     * @param array $scopes
844
     * @param bool $forcePagination
845
     * @return \Illuminate\Database\Eloquent\Collection
846
     */
847 12
    protected function getIndexItems($scopes = [], $forcePagination = false)
848
    {
849 12
        return $this->transformIndexItems($this->repository->get(
850 12
            $this->indexWith,
851
            $scopes,
852 12
            $this->orderScope(),
853 12
            $this->request->get('offset') ?? $this->perPage ?? 50,
854
            $forcePagination
855
        ));
856
    }
857
858
    /**
859
     * @param \Illuminate\Database\Eloquent\Collection $items
860
     * @return \Illuminate\Database\Eloquent\Collection
861
     */
862 10
    protected function transformIndexItems($items)
863
    {
864 10
        return $items;
865
    }
866
867
    /**
868
     * @param \Illuminate\Database\Eloquent\Collection $items
869
     * @return array
870
     */
871 6
    protected function getIndexTableData($items)
872
    {
873 6
        $translated = $this->moduleHas('translations');
874
        return $items->map(function ($item) use ($translated) {
875
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
876 3
                return $this->getItemColumnData($item, $column);
877 3
            })->toArray();
878
879 3
            $name = $columnsData[$this->titleColumnKey];
880
881 3
            if (empty($name)) {
882
                if ($this->moduleHas('translations')) {
883
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
884
885
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
886
                        $name = $fallBackTranslation->{$this->titleColumnKey};
887
                    }
888
                }
889
890
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
891
            }
892
893 3
            unset($columnsData[$this->titleColumnKey]);
894
895 3
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
896 3
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
897 3
            $canEdit = $this->getIndexOption('edit');
898 3
            $canDuplicate = $this->getIndexOption('duplicate');
899
900 3
            return array_replace([
901 3
                'id' => $item->id,
902 3
                'name' => $name,
903 3
                'publish_start_date' => $item->publish_start_date,
904 3
                'publish_end_date' => $item->publish_end_date,
905 3
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
906 3
                'duplicate' => $canDuplicate ? $this->getModuleRoute($item->id, 'duplicate') : null,
907 3
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
908 3
            ] + ($this->getIndexOption('editInModal') ? [
909 1
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
910 1
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
911 3
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
912 3
                'published' => $item->published,
913 3
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
914
                'featured' => $item->{$this->featureField},
915 3
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
916
                'deleted' => true,
917 3
            ] : []) + (($this->getIndexOption('forceDelete') && $itemIsTrashed) ? [
918
                'destroyable' => true,
919 3
            ] : []) + ($translated ? [
920 3
                'languages' => $item->getActiveLanguages(),
921 3
            ] : []) + $columnsData, $this->indexItemData($item));
922 6
        })->toArray();
923
    }
924
925
    /**
926
     * @param \A17\Twill\Models\Model $item
927
     * @return array
928
     */
929 2
    protected function indexItemData($item)
930
    {
931 2
        return [];
932
    }
933
934
    /**
935
     * @param \A17\Twill\Models\Model $item
936
     * @param array $column
937
     * @return array
938
     */
939 5
    protected function getItemColumnData($item, $column)
940
    {
941 5
        if (isset($column['thumb']) && $column['thumb']) {
942 2
            if (isset($column['present']) && $column['present']) {
943
                return [
944
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
945
                ];
946
            } else {
947 2
                $variant = isset($column['variant']);
948 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...
949 2
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
950 2
                $params = $variant && isset($column['variant']['params'])
951
                ? $column['variant']['params']
952 2
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
953
954
                return [
955 2
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
956
                ];
957
            }
958
        }
959
960 5
        if (isset($column['nested']) && $column['nested']) {
961
            $field = $column['nested'];
962
            $nestedCount = $item->{$column['nested']}->count();
963
            $value = '<a href="';
964
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
965
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
966
                ? Str::plural($column['title'])
967
                : Str::singular($column['title']))) . '</a>';
968
        } else {
969 5
            $field = $column['field'];
970 5
            $value = $item->$field;
971
        }
972
973 5
        if (isset($column['relationship'])) {
974
            $field = $column['relationship'] . ucfirst($column['field']);
975
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
976 5
        } elseif (isset($column['present']) && $column['present']) {
977
            $value = $item->presentAdmin()->{$column['field']};
978
        }
979
980
        return [
981 5
            "$field" => $value,
982
        ];
983
    }
984
985
    /**
986
     * @param \Illuminate\Database\Eloquent\Collection $items
987
     * @return array
988
     */
989 6
    protected function getIndexTableColumns($items)
990
    {
991 6
        $tableColumns = [];
992 6
        $visibleColumns = $this->request->get('columns') ?? false;
993
994 6
        if (isset(Arr::first($this->indexColumns)['thumb'])
995 6
            && Arr::first($this->indexColumns)['thumb']
996
        ) {
997 4
            array_push($tableColumns, [
998 4
                'name' => 'thumbnail',
999 4
                'label' => twillTrans('twill::lang.listing.columns.thumbnail'),
1000 4
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
1001
                'optional' => true,
1002
                'sortable' => false,
1003
            ]);
1004 4
            array_shift($this->indexColumns);
1005
        }
1006
1007 6
        if ($this->getIndexOption('feature')) {
1008
            array_push($tableColumns, [
1009
                'name' => 'featured',
1010
                'label' => twillTrans('twill::lang.listing.columns.featured'),
1011
                'visible' => true,
1012
                'optional' => false,
1013
                'sortable' => false,
1014
            ]);
1015
        }
1016
1017 6
        if ($this->getIndexOption('publish')) {
1018 6
            array_push($tableColumns, [
1019 6
                'name' => 'published',
1020 6
                'label' => twillTrans('twill::lang.listing.columns.published'),
1021
                'visible' => true,
1022
                'optional' => false,
1023
                'sortable' => false,
1024
            ]);
1025
        }
1026
1027 6
        array_push($tableColumns, [
1028 6
            'name' => 'name',
1029 6
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? twillTrans('twill::lang.listing.columns.name'),
1030
            'visible' => true,
1031
            'optional' => false,
1032 6
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
1033
        ]);
1034
1035 6
        unset($this->indexColumns[$this->titleColumnKey]);
1036
1037 6
        foreach ($this->indexColumns as $column) {
1038 4
            $columnName = isset($column['relationship'])
1039
            ? $column['relationship'] . ucfirst($column['field'])
1040 4
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
1041
1042 4
            array_push($tableColumns, [
1043 4
                'name' => $columnName,
1044 4
                'label' => $column['title'],
1045 4
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
1046 4
                'optional' => $column['optional'] ?? true,
1047 4
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
1048 4
                'html' => $column['html'] ?? false,
1049
            ]);
1050
        }
1051
1052 6
        if ($this->moduleHas('translations')) {
1053 5
            array_push($tableColumns, [
1054 5
                'name' => 'languages',
1055 5
                'label' => twillTrans('twill::lang.listing.languages'),
1056 5
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
1057
                'optional' => true,
1058
                'sortable' => false,
1059
            ]);
1060
        }
1061
1062 6
        return $tableColumns;
1063
    }
1064
1065
    /**
1066
     * @param \Illuminate\Database\Eloquent\Collection $items
1067
     * @param array $scopes
1068
     * @return array
1069
     */
1070 5
    protected function getIndexTableMainFilters($items, $scopes = [])
1071
    {
1072 5
        $statusFilters = [];
1073
1074 5
        $scope = ($this->submodule ? [
1075
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
1076 5
        ] : []) + $scopes;
1077
1078 5
        array_push($statusFilters, [
1079 5
            'name' => twillTrans('twill::lang.listing.filter.all-items'),
1080 5
            'slug' => 'all',
1081 5
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
1082
        ]);
1083
1084 5
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
1085 5
            array_push($statusFilters, [
1086 5
                'name' => twillTrans('twill::lang.listing.filter.mine'),
1087 5
                'slug' => 'mine',
1088 5
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
1089
            ]);
1090
        }
1091
1092 5
        if ($this->getIndexOption('publish')) {
1093 5
            array_push($statusFilters, [
1094 5
                'name' => twillTrans('twill::lang.listing.filter.published'),
1095 5
                'slug' => 'published',
1096 5
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
1097
            ], [
1098 5
                'name' => twillTrans('twill::lang.listing.filter.draft'),
1099 5
                'slug' => 'draft',
1100 5
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
1101
            ]);
1102
        }
1103
1104 5
        if ($this->getIndexOption('restore')) {
1105 5
            array_push($statusFilters, [
1106 5
                'name' => twillTrans('twill::lang.listing.filter.trash'),
1107 5
                'slug' => 'trash',
1108 5
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1109
            ]);
1110
        }
1111
1112 5
        return $statusFilters;
1113
    }
1114
1115
    /**
1116
     * @param string $moduleName
1117
     * @param string $routePrefix
1118
     * @return array
1119
     */
1120 6
    protected function getIndexUrls($moduleName, $routePrefix)
1121
    {
1122 6
        return Collection::make([
1123 6
            'store',
1124
            'publish',
1125
            'bulkPublish',
1126
            'restore',
1127
            'bulkRestore',
1128
            'forceDelete',
1129
            'bulkForceDelete',
1130
            'reorder',
1131
            'feature',
1132
            'bulkFeature',
1133
            'bulkDelete',
1134
        ])->mapWithKeys(function ($endpoint) {
1135
            return [
1136 6
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1137 6
                    $this->moduleName, $this->routePrefix, $endpoint,
1138 6
                    $this->submodule ? [$this->submoduleParentId] : []
1139
                ) : null,
1140
            ];
1141 6
        })->toArray();
1142
    }
1143
1144
    /**
1145
     * @param string $option
1146
     * @return bool
1147
     */
1148 33
    protected function getIndexOption($option)
1149
    {
1150
        return once(function () use ($option) {
1151
            $customOptionNamesMapping = [
1152 33
                'store' => 'create',
1153
            ];
1154
1155 33
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1156
1157
            $authorizableOptions = [
1158 33
                'create' => 'edit',
1159
                'edit' => 'edit',
1160
                'publish' => 'publish',
1161
                'feature' => 'feature',
1162
                'reorder' => 'reorder',
1163
                'delete' => 'delete',
1164
                'duplicate' => 'duplicate',
1165
                'restore' => 'delete',
1166
                'forceDelete' => 'delete',
1167
                'bulkForceDelete' => 'delete',
1168
                'bulkPublish' => 'publish',
1169
                'bulkRestore' => 'delete',
1170
                'bulkFeature' => 'feature',
1171
                'bulkDelete' => 'delete',
1172
                'bulkEdit' => 'edit',
1173
                'editInModal' => 'edit',
1174
            ];
1175
1176 33
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1177 33
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1178 33
        });
1179
    }
1180
1181
    /**
1182
     * @param array $prependScope
1183
     * @return array
1184
     */
1185 2
    protected function getBrowserData($prependScope = [])
1186
    {
1187 2
        if ($this->request->has('except')) {
1188
            $prependScope['exceptIds'] = $this->request->get('except');
1189
        }
1190
1191 2
        $scopes = $this->filterScope($prependScope);
1192 2
        $items = $this->getBrowserItems($scopes);
1193 2
        $data = $this->getBrowserTableData($items);
1194
1195 2
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1196
    }
1197
1198
    /**
1199
     * @param \Illuminate\Database\Eloquent\Collection $items
1200
     * @return array
1201
     */
1202 2
    protected function getBrowserTableData($items)
1203
    {
1204 2
        $withImage = $this->moduleHas('medias');
1205
1206
        return $items->map(function ($item) use ($withImage) {
1207
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item) {
1208 2
                return $this->getItemColumnData($item, $column);
1209 2
            })->toArray();
1210
1211 2
            $name = $columnsData[$this->titleColumnKey];
1212 2
            unset($columnsData[$this->titleColumnKey]);
1213
1214
            return [
1215 2
                'id' => $item->id,
1216 2
                'name' => $name,
1217 2
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1218 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

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

1379
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1380 5
        ] : [])
1381 5
             + (Route::has($restoreRouteName) ? [
1382 5
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1383 5
        ] : []);
1384
1385 5
        return array_replace_recursive($data, $this->formData($this->request));
1386
    }
1387
1388
    /**
1389
     * @param int $id
1390
     * @return array
1391
     */
1392 1
    protected function modalFormData($id)
1393
    {
1394 1
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1395 1
        $fields = $this->repository->getFormFields($item);
1396 1
        $data = [];
1397
1398 1
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1399 1
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1400 1
                $data['fields'][] = [
1401 1
                    'name' => $fieldName,
1402 1
                    'value' => $fieldValue,
1403
                ];
1404
            }
1405
1406 1
            $data['languages'] = $item->getActiveLanguages();
1407
1408 1
            unset($fields['translations']);
1409
        }
1410
1411 1
        foreach ($fields as $fieldName => $fieldValue) {
1412 1
            $data['fields'][] = [
1413 1
                'name' => $fieldName,
1414 1
                'value' => $fieldValue,
1415
            ];
1416
        }
1417
1418 1
        return array_replace_recursive($data, $this->formData($this->request));
1419
    }
1420
1421
    /**
1422
     * @param Request $request
1423
     * @return array
1424
     */
1425 5
    protected function formData($request)
1426
    {
1427 5
        return [];
1428
    }
1429
1430
    /**
1431
     * @param Request $item
1432
     * @return array
1433
     */
1434 1
    protected function previewData($item)
1435
    {
1436 1
        return [];
1437
    }
1438
1439
    /**
1440
     * @return \A17\Twill\Http\Requests\Admin\Request
1441
     */
1442 25
    protected function validateFormRequest()
1443
    {
1444
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, $field) {
1445 4
            return Auth::guard('twill_users')->user()->cannot($permission);
1446 25
        })->keys();
1447
1448
        $unauthorizedFields->each(function ($field) {
1449
            $this->request->offsetUnset($field);
1450 25
        });
1451
1452 25
        return App::make("$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request");
1453
    }
1454
1455
    /**
1456
     * @return string
1457
     */
1458 44
    protected function getNamespace()
1459
    {
1460 44
        return $this->namespace ?? Config::get('twill.namespace');
1461
    }
1462
1463
    /**
1464
     * @return string
1465
     */
1466 44
    protected function getRoutePrefix()
1467
    {
1468 44
        if ($this->request->route() != null) {
1469 43
            $routePrefix = ltrim(str_replace(Config::get('twill.admin_app_path'), '', $this->request->route()->getPrefix()), "/");
1470 43
            return str_replace("/", ".", $routePrefix);
1471
        }
1472
1473 1
        return '';
1474
    }
1475
1476
    /**
1477
     * @return string
1478
     */
1479 44
    protected function getModelName()
1480
    {
1481 44
        return $this->modelName ?? ucfirst(Str::singular($this->moduleName));
1482
    }
1483
1484
    /**
1485
     * @return \A17\Twill\Repositories\ModuleRepository
1486
     */
1487 44
    protected function getRepository()
1488
    {
1489 44
        return App::make("$this->namespace\Repositories\\" . $this->modelName . "Repository");
1490
    }
1491
1492
    /**
1493
     * @return string
1494
     */
1495 44
    protected function getViewPrefix()
1496
    {
1497 44
        return "admin.$this->moduleName";
1498
    }
1499
1500
    /**
1501
     * @return string
1502
     */
1503 44
    protected function getModelTitle()
1504
    {
1505 44
        return camelCaseToWords($this->modelName);
1506
    }
1507
1508
    /**
1509
     * @return string
1510
     */
1511
    protected function getParentModuleForeignKey()
1512
    {
1513
        return Str::singular(explode('.', $this->moduleName)[0]) . '_id';
1514
    }
1515
1516
    /**
1517
     * @return string
1518
     */
1519 11
    protected function getPermalinkBaseUrl()
1520
    {
1521 11
        $appUrl = Config::get('app.url');
1522
1523 11
        if (blank(parse_url($appUrl)['scheme'] ?? null)) {
1524
            $appUrl = $this->request->getScheme() . '://' . $appUrl;
1525
        }
1526
1527 11
        return $appUrl . '/'
1528 11
            . ($this->moduleHas('translations') ? '{language}/' : '')
1529 11
            . ($this->moduleHas('revisions') ? '{preview}/' : '')
1530 11
            . ($this->permalinkBase ?? $this->moduleName)
1531 11
            . (isset($this->permalinkBase) && empty($this->permalinkBase) ? '' : '/');
1532
    }
1533
1534
    /**
1535
     * @param string $baseUrl
1536
     * @return string
1537
     */
1538 11
    protected function getPermalinkPrefix($baseUrl)
1539
    {
1540 11
        return rtrim(str_replace(['http://', 'https://', '{preview}/', '{language}/'], '', $baseUrl), "/") . '/';
1541
    }
1542
1543
    /**
1544
     * @param int $id
1545
     * @param string $action
1546
     * @return string
1547
     */
1548 8
    protected function getModuleRoute($id, $action)
1549
    {
1550 8
        return moduleRoute(
1551 8
            $this->moduleName,
1552 8
            $this->routePrefix,
1553
            $action,
1554 8
            array_merge($this->submodule ? [$this->submoduleParentId] : [], [$id])
1555
        );
1556
    }
1557
1558
    /**
1559
     * @param string $behavior
1560
     * @return bool
1561
     */
1562 33
    protected function moduleHas($behavior)
1563
    {
1564 33
        return $this->repository->hasBehavior($behavior);
1565
    }
1566
1567
    /**
1568
     * @return bool
1569
     */
1570 11
    protected function titleIsTranslatable()
1571
    {
1572 11
        return $this->repository->isTranslatable(
1573 11
            $this->titleColumnKey
1574
        );
1575
    }
1576
1577
    /**
1578
     * @param string|null $back_link
1579
     * @param array $params
1580
     * @return void
1581
     */
1582 5
    protected function setBackLink($back_link = null, $params = [])
1583
    {
1584 5
        if (!isset($back_link)) {
1585 5
            if (($back_link = Session::get($this->getBackLinkSessionKey())) == null) {
1586 5
                $back_link = $this->request->headers->get('referer') ?? moduleRoute(
1587 5
                    $this->moduleName,
1588 5
                    $this->routePrefix,
1589 5
                    'index',
1590
                    $params
1591
                );
1592
            }
1593
        }
1594
1595 5
        if (!Session::get($this->moduleName . '_retain')) {
1596 1
            Session::put($this->getBackLinkSessionKey(), $back_link);
1597
        } else {
1598 4
            Session::put($this->moduleName . '_retain', false);
1599
        }
1600 5
    }
1601
1602
    /**
1603
     * @param string|null $fallback
1604
     * @param array $params
1605
     * @return string
1606
     */
1607
    protected function getBackLink($fallback = null, $params = [])
1608
    {
1609
        $back_link = Session::get($this->getBackLinkSessionKey(), $fallback);
1610
        return $back_link ?? moduleRoute($this->moduleName, $this->routePrefix, 'index', $params);
1611
    }
1612
1613
    /**
1614
     * @return string
1615
     */
1616 5
    protected function getBackLinkSessionKey()
1617
    {
1618 5
        return $this->moduleName . ($this->submodule ? $this->submoduleParentId ?? '' : '') . '_back_link';
1619
    }
1620
1621
    /**
1622
     * @param int $id
1623
     * @param array $params
1624
     * @return \Illuminate\Http\RedirectResponse
1625
     */
1626 1
    protected function redirectToForm($id, $params = [])
1627
    {
1628 1
        Session::put($this->moduleName . '_retain', true);
1629
1630 1
        return Redirect::to(moduleRoute(
1631 1
            $this->moduleName,
1632 1
            $this->routePrefix,
1633 1
            'edit',
1634 1
            array_filter($params) + [Str::singular($this->moduleName) => $id]
1635
        ));
1636
    }
1637
1638
    /**
1639
     * @param string $message
1640
     * @return \Illuminate\Http\JsonResponse
1641
     */
1642 11
    protected function respondWithSuccess($message)
1643
    {
1644 11
        return $this->respondWithJson($message, FlashLevel::SUCCESS);
1645
    }
1646
1647
    /**
1648
     * @param string $redirectUrl
1649
     * @return \Illuminate\Http\JsonResponse
1650
     */
1651 22
    protected function respondWithRedirect($redirectUrl)
1652
    {
1653 22
        return Response::json([
1654 22
            'redirect' => $redirectUrl,
1655
        ]);
1656
    }
1657
1658
    /**
1659
     * @param string $message
1660
     * @return \Illuminate\Http\JsonResponse
1661
     */
1662 2
    protected function respondWithError($message)
1663
    {
1664 2
        return $this->respondWithJson($message, FlashLevel::ERROR);
1665
    }
1666
1667
    /**
1668
     * @param string $message
1669
     * @param mixed $variant
1670
     * @return \Illuminate\Http\JsonResponse
1671
     */
1672 13
    protected function respondWithJson($message, $variant)
1673
    {
1674 13
        return Response::json([
1675 13
            'message' => $message,
1676 13
            'variant' => $variant,
1677
        ]);
1678
    }
1679
1680
    /**
1681
     * @param array $input
1682
     * @return void
1683
     */
1684 25
    protected function fireEvent($input = [])
1685
    {
1686 25
        fireCmsEvent('cms-module.saved', $input);
1687 25
    }
1688
1689
    /**
1690
     * @return Collection
1691
     */
1692 5
    public function getRepeaterList()
1693
    {
1694
        return app(BlockCollection::class)->getRepeaterList()->mapWithKeys(function ($repeater) {
1695 5
            return [$repeater['name'] => $repeater];
1696 5
        });
1697
    }
1698
}
1699