Passed
Pull Request — 2.x (#1202)
by
unknown
06:20
created

ModuleController::getViewPrefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
347
348
        $indexData = $this->getIndexData($this->submodule ? [
349
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
350
        ] : []);
351 2
352
        if ($this->request->ajax()) {
353 2
            return $indexData + ['replaceUrl' => true];
354
        }
355
356
        if ($this->request->has('openCreate') && $this->request->get('openCreate')) {
357
            $indexData += ['openCreate' => true];
358
        }
359
360 25
        $view = Collection::make([
361
            "$this->viewPrefix.index",
362 25
            "twill::$this->moduleName.index",
363 25
            "twill::layouts.listing",
364
        ])->first(function ($view) {
365 25
            return View::exists($view);
366
        });
367
368
        return View::make($view, $indexData);
369
    }
370
371
    /**
372
     * @return \Illuminate\Http\JsonResponse
373 25
     */
374
    public function browser()
375 25
    {
376
        return Response::json($this->getBrowserData());
377 25
    }
378
379 25
    /**
380
     * @param int|null $parentModuleId
381 25
     * @return \Illuminate\Http\JsonResponse
382 3
     */
383
    public function store($parentModuleId = null)
384
    {
385 22
        $parentModuleId = $this->getParentModuleIdFromRequest($this->request) ?? $parentModuleId;
386
387
        $input = $this->validateFormRequest()->all();
388
        $optionalParent = $parentModuleId ? [$this->getParentModuleForeignKey() => $parentModuleId] : [];
389
390
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
391
            return $this->respondWithRedirect(moduleRoute(
392 22
                $this->moduleName,
393
                $this->routePrefix,
394
                'create'
395
            ));
396 22
        }
397
398
        $item = $this->repository->create($input + $optionalParent);
399
400 22
        activity()->performedOn($item)->log('created');
401
402
        $this->fireEvent($input);
403
404
        Session::put($this->moduleName . '_retain', true);
405
406
        if ($this->getIndexOption('editInModal')) {
407
            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

407
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.publisher.save-success'));
Loading history...
408 22
        }
409 22
410 22
        if (isset($input['cmsSaveType']) && Str::endsWith($input['cmsSaveType'], '-close')) {
411 22
            return $this->respondWithRedirect($this->getBackLink());
412
        }
413
414
        if (isset($input['cmsSaveType']) && Str::endsWith($input['cmsSaveType'], '-new')) {
415
            return $this->respondWithRedirect(moduleRoute(
416
                $this->moduleName,
417
                $this->routePrefix,
418
                'create'
419
            ));
420
        }
421 1
422
        return $this->respondWithRedirect(moduleRoute(
423 1
            $this->moduleName,
424
            $this->routePrefix,
425
            'edit',
426
            [Str::singular(last(explode('.', $this->moduleName))) => $item[$this->identifierColumnKey]]
427 1
        ));
428
    }
429
430
    /**
431
     * @param Request $request
432
     * @param int|$id
433
     * @param int|null $submoduleId
434
     * @return \Illuminate\Http\RedirectResponse
435 6
     */
436
    public function show($id, $submoduleId = null)
437 6
    {
438 6
        if ($this->getIndexOption('editInModal')) {
439
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
440 6
        }
441 2
442 1
        return $this->redirectToForm($this->getParentModuleIdFromRequest($this->request) ?? $submoduleId ?? $id);
0 ignored issues
show
Bug introduced by
It seems like $this->getParentModuleId... ?? $submoduleId ?? $id can also be of type string; however, parameter $id of A17\Twill\Http\Controlle...oller::redirectToForm() does only seem to accept integer, 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
        return $this->redirectToForm(/** @scrutinizer ignore-type */ $this->getParentModuleIdFromRequest($this->request) ?? $submoduleId ?? $id);
Loading history...
443 2
    }
444
445
    /**
446 4
     * @param int $id
447
     * @param int|null $submoduleId
448 4
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
449 4
     */
450 4
    public function edit($id, $submoduleId = null)
451 4
    {
452 4
        $params = $this->request->route()->parameters();
453 4
454 4
        $this->submodule = count($params) > 1;
455
        $this->submoduleParentId = $this->submodule
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->submodule ? $this... ?? $id : head($params) can also be of type string. However, the property $submoduleParentId is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
456 4
        ? $this->getParentModuleIdFromRequest($this->request) ?? $id
457
        : head($params);
458
459
        $id = last($params);
460
461
        if ($this->getIndexOption('editInModal')) {
462
            return $this->request->ajax()
463
            ? Response::json($this->modalFormData($id))
464
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
465
        }
466
467
        $this->setBackLink();
468
469
        $view = Collection::make([
470
            "$this->viewPrefix.form",
471
            "twill::$this->moduleName.form",
472
            "twill::layouts.form",
473
        ])->first(function ($view) {
474
            return View::exists($view);
475
        });
476
477
        return View::make($view, $this->form($id));
478
    }
479
480
    /**
481
     * @param int $parentModuleId
482
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
483
     */
484
    public function create($parentModuleId = null)
485
    {
486
        if (!$this->getIndexOption('skipCreateModal')) {
487
            return Redirect::to(moduleRoute(
488
                $this->moduleName,
489
                $this->routePrefix,
490
                'index',
491
                ['openCreate' => true]
492
            ));
493 8
        }
494
495 8
        $parentModuleId = $this->getParentModuleIdFromRequest($this->request) ?? $parentModuleId;
496 8
497
        $this->submodule = isset($parentModuleId);
498 8
        $this->submoduleParentId = $parentModuleId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $parentModuleId can also be of type string. However, the property $submoduleParentId is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
499 8
500
        $view = Collection::make([
501 8
            "$this->viewPrefix.form",
502
            "twill::$this->moduleName.form",
503
            "twill::layouts.form",
504
        ])->first(function ($view) {
505
            return View::exists($view);
506
        });
507
508
        return View::make($view, $this->form(null));
509 8
    }
510
511 8
    /**
512
     * @param int $id
513 8
     * @param int|null $submoduleId
514
     * @return \Illuminate\Http\JsonResponse
515 8
     */
516
    public function update($id, $submoduleId = null)
517 8
    {
518 8
        $params = $this->request->route()->parameters();
519
520 8
        $submoduleParentId = $this->getParentModuleIdFromRequest($this->request) ?? $id;
521
        $this->submodule = isset($submoduleParentId);
522
        $this->submoduleParentId = $submoduleParentId;
0 ignored issues
show
Documentation Bug introduced by
It seems like $submoduleParentId can also be of type string. However, the property $submoduleParentId is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
523
524
        $id = last($params);
525
526
        $item = $this->repository->getById($id);
527
        $input = $this->request->all();
528
529
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
530
            return $this->respondWithRedirect(moduleRoute(
531
                $this->moduleName,
532
                $this->routePrefix,
533
                'edit',
534 8
                [Str::singular($this->moduleName) => $id]
535
            ));
536
        } else {
537
            $formRequest = $this->validateFormRequest();
538
539
            $this->repository->update($id, $formRequest->all());
540
541
            activity()->performedOn($item)->log('updated');
542
543
            $this->fireEvent();
544
545
            if (isset($input['cmsSaveType'])) {
546 8
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
547 7
                    return $this->respondWithRedirect($this->getBackLink());
548 7
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
549 7
                    if ($this->getIndexOption('skipCreateModal')) {
550 7
                        return $this->respondWithRedirect(moduleRoute(
551
                            $this->moduleName,
552
                            $this->routePrefix,
553
                            'create'
554 1
                        ));
555
                    }
556
                    return $this->respondWithRedirect(moduleRoute(
557
                        $this->moduleName,
558
                        $this->routePrefix,
559
                        'index',
560
                        ['openCreate' => true]
561
                    ));
562 1
                } elseif ($input['cmsSaveType'] === 'restore') {
563
                    Session::flash('status', twillTrans('twill::lang.publisher.restore-success'));
564 1
565
                    return $this->respondWithRedirect(moduleRoute(
566
                        $this->moduleName,
567 1
                        $this->routePrefix,
568 1
                        'edit',
569
                        [Str::singular($this->moduleName) => $id]
570
                    ));
571 1
                }
572
            }
573
574
            if ($this->moduleHas('revisions')) {
575 1
                return Response::json([
576
                    'message' => twillTrans('twill::lang.publisher.save-success'),
577 1
                    'variant' => FlashLevel::SUCCESS,
578 1
                    'revisions' => $item->revisionsArray(),
579 1
                ]);
580 1
            }
581
582
            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

582
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.publisher.save-success'));
Loading history...
583
        }
584
    }
585
586
    /**
587
     * @param int $id
588 2
     * @return \Illuminate\View\View
589
     */
590 2
    public function preview($id)
591 1
    {
592 1
        if ($this->request->has('revisionId')) {
593 1
            $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

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

596
            /** @scrutinizer ignore-call */ 
597
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
597
        }
598 1
599
        if ($this->request->has('activeLanguage')) {
600 1
            App::setLocale($this->request->get('activeLanguage'));
601 1
        }
602 1
603 1
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
604 1
605 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
606 1
            'item' => $item,
607
        ], $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

607
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
608 1
            'moduleName' => Str::singular($this->moduleName),
609 1
        ]);
610
    }
611 1
612
    /**
613 1
     * @param int $id
614
     * @return \Illuminate\View\View
615
     */
616
    public function restoreRevision($id)
617
    {
618
        if ($this->request->has('revisionId')) {
619 2
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
620
            $item[$this->identifierColumnKey] = $id;
621
            $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...
622 2
        } else {
623 2
            throw new NotFoundHttpException();
624
        }
625 2
626 2
        $this->setBackLink();
627 1
628 1
        $view = Collection::make([
629
            "$this->viewPrefix.form",
630
            "twill::$this->moduleName.form",
631 1
            "twill::layouts.form",
632
        ])->first(function ($view) {
633 1
            return View::exists($view);
634 1
        });
635
636
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
637 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 Nette\Utils\DateTime or 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

637
        /** @scrutinizer ignore-call */ 
638
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
638 1
639
        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...
640
641 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

641
        return View::make($view, $this->form($id, /** @scrutinizer ignore-type */ $item));
Loading history...
642 1
    }
643
644
    /**
645
     * @return \Illuminate\Http\JsonResponse
646
     */
647
    public function publish()
648
    {
649
        try {
650
            if ($this->repository->updateBasic($this->request->get('id'), [
651
                'published' => !$this->request->get('active'),
652
            ])) {
653
                activity()->performedOn(
654
                    $this->repository->getById($this->request->get('id'))
655
                )->log(
656
                    ($this->request->get('active') ? 'un' : '') . 'published'
657
                );
658
659
                $this->fireEvent();
660
661
                if ($this->request->get('active')) {
662
                    return $this->respondWithSuccess(twillTrans('twill::lang.listing.publish.unpublished', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

662
                    return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.publish.unpublished', ['modelTitle' => $this->modelTitle]));
Loading history...
663
                } else {
664
                    return $this->respondWithSuccess(twillTrans('twill::lang.listing.publish.published', ['modelTitle' => $this->modelTitle]));
665
                }
666
            }
667
        } catch (\Exception $e) {
668
            \Log::error($e);
669
        }
670
671
        return $this->respondWithError(twillTrans('twill::lang.listing.publish.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

671
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.publish.error', ['modelTitle' => $this->modelTitle]));
Loading history...
672
    }
673
674
    /**
675
     * @return \Illuminate\Http\JsonResponse
676
     */
677
    public function bulkPublish()
678
    {
679
        try {
680
            if ($this->repository->updateBasic(explode(',', $this->request->get('ids')), [
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('ids') can also be of type null; however, parameter $string of explode() 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

680
            if ($this->repository->updateBasic(explode(',', /** @scrutinizer ignore-type */ $this->request->get('ids')), [
Loading history...
681
                'published' => $this->request->get('publish'),
682
            ])) {
683
                $this->fireEvent();
684
                if ($this->request->get('publish')) {
685
                    return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-publish.published', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

685
                    return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-publish.published', ['modelTitle' => $this->modelTitle]));
Loading history...
686
                } else {
687
                    return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-publish.unpublished', ['modelTitle' => $this->modelTitle]));
688
                }
689
            }
690
        } catch (\Exception $e) {
691
            \Log::error($e);
692
        }
693
        return $this->respondWithError(twillTrans('twill::lang.listing.bulk-publish.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

693
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-publish.error', ['modelTitle' => $this->modelTitle]));
Loading history...
694
    }
695
696
    /**
697
     * @param int $id
698
     * @param int|null $submoduleId
699
     * @return \Illuminate\Http\JsonResponse
700
     */
701
    public function duplicate($id, $submoduleId = null)
702
    {
703 2
        $params = $this->request->route()->parameters();
704
705 2
        $id = last($params);
706 2
707 2
        $item = $this->repository->getById($id);
708 2
        if ($newItem = $this->repository->duplicate($id, $this->titleColumnKey)) {
709 2
            $this->fireEvent();
710
            activity()->performedOn($item)->log('duplicated');
711
712
            return Response::json([
713
                'message' => twillTrans('twill::lang.listing.duplicate.success', ['modelTitle' => $this->modelTitle]),
714
                'variant' => FlashLevel::SUCCESS,
715
                'redirect' => moduleRoute(
716
                    $this->moduleName,
717
                    $this->routePrefix,
718
                    'edit',
719
                    array_filter([Str::singular($this->moduleName) => $newItem->id])
720
                ),
721
            ]);
722
        }
723
724
        return $this->respondWithError(twillTrans('twill::lang.listing.duplicate.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

724
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.duplicate.error', ['modelTitle' => $this->modelTitle]));
Loading history...
725
    }
726
727
    /**
728
     * @param int $id
729
     * @param int|null $submoduleId
730
     * @return \Illuminate\Http\JsonResponse
731
     */
732
    public function destroy($id, $submoduleId = null)
733
    {
734
        $params = $this->request->route()->parameters();
735
736
        $id = last($params);
737
738
        $item = $this->repository->getById($id);
739
        if ($this->repository->delete($id)) {
740
            $this->fireEvent();
741
            activity()->performedOn($item)->log('deleted');
742
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.delete.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

742
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.delete.success', ['modelTitle' => $this->modelTitle]));
Loading history...
743
        }
744
745
        return $this->respondWithError(twillTrans('twill::lang.listing.delete.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

745
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.delete.error', ['modelTitle' => $this->modelTitle]));
Loading history...
746
    }
747
748
    /**
749
     * @return \Illuminate\Http\JsonResponse
750
     */
751
    public function bulkDelete()
752
    {
753
        if ($this->repository->bulkDelete(explode(',', $this->request->get('ids')))) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('ids') can also be of type null; however, parameter $string of explode() 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

753
        if ($this->repository->bulkDelete(explode(',', /** @scrutinizer ignore-type */ $this->request->get('ids')))) {
Loading history...
754
            $this->fireEvent();
755
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-delete.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

755
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-delete.success', ['modelTitle' => $this->modelTitle]));
Loading history...
756
        }
757 2
758
        return $this->respondWithError(twillTrans('twill::lang.listing.bulk-delete.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

758
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-delete.error', ['modelTitle' => $this->modelTitle]));
Loading history...
759 2
    }
760 1
761 1
    /**
762 1
     * @return \Illuminate\Http\JsonResponse
763
     */
764
    public function forceDelete()
765 1
    {
766
        if ($this->repository->forceDelete($this->request->get('id'))) {
767
            $this->fireEvent();
768
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.force-delete.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

768
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.force-delete.success', ['modelTitle' => $this->modelTitle]));
Loading history...
769
        }
770
771
        return $this->respondWithError(twillTrans('twill::lang.listing.force-delete.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

771
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.force-delete.error', ['modelTitle' => $this->modelTitle]));
Loading history...
772
    }
773
774
    /**
775
     * @return \Illuminate\Http\JsonResponse
776
     */
777
    public function bulkForceDelete()
778
    {
779
        if ($this->repository->bulkForceDelete(explode(',', $this->request->get('ids')))) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('ids') can also be of type null; however, parameter $string of explode() 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

779
        if ($this->repository->bulkForceDelete(explode(',', /** @scrutinizer ignore-type */ $this->request->get('ids')))) {
Loading history...
780
            $this->fireEvent();
781
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-force-delete.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

781
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-force-delete.success', ['modelTitle' => $this->modelTitle]));
Loading history...
782
        }
783
784 2
        return $this->respondWithError(twillTrans('twill::lang.listing.bulk-force-delete.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

784
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-force-delete.error', ['modelTitle' => $this->modelTitle]));
Loading history...
785
    }
786 2
787 2
    /**
788 2
     * @return \Illuminate\Http\JsonResponse
789
     */
790 2
    public function restore()
791
    {
792
        if ($this->repository->restore($this->request->get('id'))) {
793
            $this->fireEvent();
794
            activity()->performedOn($this->repository->getById($this->request->get('id')))->log('restored');
795
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.restore.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

795
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.restore.success', ['modelTitle' => $this->modelTitle]));
Loading history...
796 2
        }
797
798
        return $this->respondWithError(twillTrans('twill::lang.listing.restore.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

798
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.restore.error', ['modelTitle' => $this->modelTitle]));
Loading history...
799 2
    }
800 2
801 1
    /**
802 1
     * @return \Illuminate\Http\JsonResponse
803
     */
804
    public function bulkRestore()
805 1
    {
806 1
        if ($this->repository->bulkRestore(explode(',', $this->request->get('ids')))) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('ids') can also be of type null; however, parameter $string of explode() 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

806
        if ($this->repository->bulkRestore(explode(',', /** @scrutinizer ignore-type */ $this->request->get('ids')))) {
Loading history...
807
            $this->fireEvent();
808
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-restore.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

808
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-restore.success', ['modelTitle' => $this->modelTitle]));
Loading history...
809
        }
810
811
        return $this->respondWithError(twillTrans('twill::lang.listing.bulk-restore.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

811
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-restore.error', ['modelTitle' => $this->modelTitle]));
Loading history...
812
    }
813
814
    /**
815
     * @return \Illuminate\Http\JsonResponse
816
     */
817
    public function feature()
818
    {
819
        if (($id = $this->request->get('id'))) {
820
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
821
            $featured = !$this->request->get('active');
822
823
            if ($this->repository->isUniqueFeature()) {
824
                if ($featured) {
825
                    $this->repository->updateBasic(null, [$featuredField => false]);
826
                    $this->repository->updateBasic($id, [$featuredField => $featured]);
827
                }
828
            } else {
829
                $this->repository->updateBasic($id, [$featuredField => $featured]);
830
            }
831
832 4
            activity()->performedOn(
833
                $this->repository->getById($id)
834 4
            )->log(
835 4
                ($this->request->get('active') ? 'un' : '') . 'featured'
836 3
            );
837 3
838
            $this->fireEvent();
839
840
            if ($this->request->get('active')) {
841
                return $this->respondWithSuccess(twillTrans('twill::lang.listing.featured.unfeatured', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

841
                return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.featured.unfeatured', ['modelTitle' => $this->modelTitle]));
Loading history...
842
            } else {
843
                return $this->respondWithSuccess(twillTrans('twill::lang.listing.featured.featured', ['modelTitle' => $this->modelTitle]));
844
            }
845
        }
846 1
847
        return $this->respondWithError(twillTrans('twill::lang.listing.featured.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

847
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.featured.error', ['modelTitle' => $this->modelTitle]));
Loading history...
848 1
    }
849 1
850
    /**
851 1
     * @return \Illuminate\Http\JsonResponse
852
     */
853 1
    public function bulkFeature()
854
    {
855
        if (($ids = explode(',', $this->request->get('ids')))) {
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('ids') can also be of type null; however, parameter $string of explode() 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

855
        if (($ids = explode(',', /** @scrutinizer ignore-type */ $this->request->get('ids')))) {
Loading history...
856
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
857
            $featured = $this->request->get('feature') ?? true;
858
            // we don't need to check if unique feature since bulk operation shouldn't be allowed in this case
859
            $this->repository->updateBasic($ids, [$featuredField => $featured]);
860 6
            $this->fireEvent();
861
862 6
            if ($this->request->get('feature')) {
863 6
                return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-featured.featured', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

863
                return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-featured.featured', ['modelTitle' => $this->modelTitle]));
Loading history...
864
            } else {
865
                return $this->respondWithSuccess(twillTrans('twill::lang.listing.bulk-featured.unfeatured', ['modelTitle' => $this->modelTitle]));
866 6
            }
867 6
        }
868 6
869 6
        return $this->respondWithError(twillTrans('twill::lang.listing.bulk-featured.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

869
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.bulk-featured.error', ['modelTitle' => $this->modelTitle]));
Loading history...
870 6
    }
871 6
872 6
    /**
873 6
     * @return \Illuminate\Http\JsonResponse
874 6
     */
875 6
    public function reorder()
876 6
    {
877
        if (($values = $this->request->get('ids')) && !empty($values)) {
878 6
            $this->repository->setNewOrder($values);
879
            $this->fireEvent();
880
            return $this->respondWithSuccess(twillTrans('twill::lang.listing.reorder.success', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) 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

880
            return $this->respondWithSuccess(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.reorder.success', ['modelTitle' => $this->modelTitle]));
Loading history...
881 6
        }
882 6
883 6
        return $this->respondWithError(twillTrans('twill::lang.listing.reorder.error', ['modelTitle' => $this->modelTitle]));
0 ignored issues
show
Bug introduced by
It seems like twillTrans('twill::lang.... => $this->modelTitle)) can also be of type array and array; however, parameter $message of A17\Twill\Http\Controlle...ler::respondWithError() 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

883
        return $this->respondWithError(/** @scrutinizer ignore-type */ twillTrans('twill::lang.listing.reorder.error', ['modelTitle' => $this->modelTitle]));
Loading history...
884 6
    }
885 6
886 6
    /**
887 6
     * @return \Illuminate\Http\JsonResponse
888 6
     */
889 6
    public function tags()
890 6
    {
891 6
        $query = $this->request->input('q');
892 6
        $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

892
        /** @scrutinizer ignore-call */ 
893
        $tags = $this->repository->getTags($query);
Loading history...
893
894
        return Response::json(['items' => $tags->map(function ($tag) {
895 6
            return $tag->name;
896
        })], 200);
897
    }
898
899
    /**
900
     * @return array
901
     */
902 5
    public function additionalTableActions()
903
    {
904 5
        return [];
905
    }
906
907
    /**
908
     * @param array $prependScope
909
     * @return array
910
     */
911
    protected function getIndexData($prependScope = [])
912 12
    {
913
        $scopes = $this->filterScope($prependScope);
914 12
        $items = $this->getIndexItems($scopes);
915 12
916
        $data = [
917 12
            'tableData' => $this->getIndexTableData($items),
918 12
            'tableColumns' => $this->getIndexTableColumns($items),
919
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
920
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('filter') can also be of type null; however, parameter $json of json_decode() 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

920
            'filters' => json_decode(/** @scrutinizer ignore-type */ $this->request->get('filter'), true) ?? [],
Loading history...
921
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
922
            'filterLinks' => $this->filterLinks ?? [],
923
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
924
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
925
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
926
            'defaultOffset' => $this->perPage,
927 10
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
928
929 10
        $baseUrl = $this->getPermalinkBaseUrl();
930
931
        $options = [
932
            'moduleName' => $this->moduleName,
933
            'skipCreateModal' => $this->getIndexOption('skipCreateModal'),
934
            'reorder' => $this->getIndexOption('reorder'),
935
            'create' => $this->getIndexOption('create'),
936 6
            'duplicate' => $this->getIndexOption('duplicate'),
937
            'translate' => $this->moduleHas('translations'),
938 6
            'translateTitle' => $this->titleIsTranslatable(),
939 6
            'permalink' => $this->getIndexOption('permalink'),
940 3
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
941 3
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
942 3
            'baseUrl' => $baseUrl,
943
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
944 3
            'additionalTableActions' => $this->additionalTableActions(),
945
        ];
946 3
947
        return array_replace_recursive($data + $options, $this->indexData($this->request));
948
    }
949
950
    /**
951
     * @param Request $request
952
     * @return array
953
     */
954
    protected function indexData($request)
955
    {
956
        return [];
957
    }
958 3
959
    /**
960 3
     * @param array $scopes
961 3
     * @param bool $forcePagination
962 3
     * @return \Illuminate\Database\Eloquent\Collection
963 3
     */
964
    protected function getIndexItems($scopes = [], $forcePagination = false)
965 3
    {
966 3
        return $this->transformIndexItems($this->repository->get(
967 3
            $this->indexWith,
968 3
            $scopes,
969 3
            $this->orderScope(),
970 3
            $this->request->get('offset') ?? $this->perPage ?? 50,
971 3
            $forcePagination
972 3
        ));
973 3
    }
974 1
975 1
    /**
976 3
     * @param \Illuminate\Database\Eloquent\Collection $items
977 3
     * @return \Illuminate\Database\Eloquent\Collection
978 3
     */
979
    protected function transformIndexItems($items)
980 3
    {
981
        return $items;
982 3
    }
983
984 3
    /**
985 3
     * @param \Illuminate\Database\Eloquent\Collection $items
986 3
     * @return array
987 6
     */
988
    protected function getIndexTableData($items)
989
    {
990
        $translated = $this->moduleHas('translations');
991
        return $items->map(function ($item) use ($translated) {
992
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
993
                return $this->getItemColumnData($item, $column);
994 2
            })->toArray();
995
996 2
            $name = $columnsData[$this->titleColumnKey];
997
998
            if (empty($name)) {
999
                if ($this->moduleHas('translations')) {
1000
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
1001
1002
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
1003
                        $name = $fallBackTranslation->{$this->titleColumnKey};
1004 5
                    }
1005
                }
1006 5
1007 2
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
1008
            }
1009
1010
            unset($columnsData[$this->titleColumnKey]);
1011
1012 2
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
1013 2
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
1014 2
            $canEdit = $this->getIndexOption('edit');
1015 2
            $canDuplicate = $this->getIndexOption('duplicate');
1016
1017 2
            return array_replace([
1018
                'id' => $item[$this->identifierColumnKey],
1019
                'name' => $name,
1020 2
                'publish_start_date' => $item->publish_start_date,
1021
                'publish_end_date' => $item->publish_end_date,
1022
                'edit' => $canEdit ? $this->getModuleRoute($item[$this->identifierColumnKey], 'edit') : null,
1023
                'duplicate' => $canDuplicate ? $this->getModuleRoute($item[$this->identifierColumnKey], 'duplicate') : null,
1024
                'delete' => $itemCanDelete ? $this->getModuleRoute($item[$this->identifierColumnKey], 'destroy') : null,
1025 5
            ] + ($this->getIndexOption('editInModal') ? [
1026
                'editInModal' => $this->getModuleRoute($item[$this->identifierColumnKey], 'edit'),
1027
                'updateUrl' => $this->getModuleRoute($item[$this->identifierColumnKey], 'update'),
1028
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
1029
                'published' => $item->published,
1030
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
1031
                'featured' => $item->{$this->featureField},
1032
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
1033
                'deleted' => true,
1034 5
            ] : []) + (($this->getIndexOption('forceDelete') && $itemIsTrashed) ? [
1035 5
                'destroyable' => true,
1036
            ] : []) + ($translated ? [
1037
                'languages' => $item->getActiveLanguages(),
1038 5
            ] : []) + $columnsData, $this->indexItemData($item));
1039
        })->toArray();
1040
    }
1041 5
1042
    /**
1043
     * @param \A17\Twill\Models\Model $item
1044
     * @return array
1045
     */
1046 5
    protected function indexItemData($item)
1047
    {
1048
        return [];
1049
    }
1050
1051
    /**
1052
     * @param \A17\Twill\Models\Model $item
1053
     * @param array $column
1054 6
     * @return array
1055
     */
1056 6
    protected function getItemColumnData($item, $column)
1057 6
    {
1058
        if (isset($column['thumb']) && $column['thumb']) {
1059 6
            if (isset($column['present']) && $column['present']) {
1060 6
                return [
1061
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
1062 4
                ];
1063 4
            } else {
1064 4
                $variant = isset($column['variant']);
1065 4
                $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...
1066
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
1067
                $params = $variant && isset($column['variant']['params'])
1068
                ? $column['variant']['params']
1069 4
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
1070
1071
                return [
1072 6
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
1073
                ];
1074
            }
1075
        }
1076
1077
        if (isset($column['nested']) && $column['nested']) {
1078
            $field = $column['nested'];
1079
            $nestedCount = $item->{$column['nested']}->count();
1080
            $module = Str::singular(last(explode('.', $this->moduleName)));
1081
            $value = '<a href="';
1082 6
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$module => $item[$this->identifierColumnKey]]);
1083 6
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
1084 6
                ? Str::plural($column['title'])
1085 6
                : Str::singular($column['title']))) . '</a>';
1086
        } else {
1087
            $field = $column['field'];
1088
            $value = $item->$field;
1089
        }
1090
1091
        if (isset($column['relationship'])) {
1092 6
            $field = $column['relationship'] . ucfirst($column['field']);
1093 6
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
1094 6
        } elseif (isset($column['present']) && $column['present']) {
1095
            $value = $item->presentAdmin()->{$column['field']};
1096
        }
1097 6
1098
        return [
1099
            "$field" => $value,
1100 6
        ];
1101
    }
1102 6
1103 4
    /**
1104
     * @param \Illuminate\Database\Eloquent\Collection $items
1105 4
     * @return array
1106
     */
1107 4
    protected function getIndexTableColumns($items)
1108 4
    {
1109 4
        $tableColumns = [];
1110 4
        $visibleColumns = $this->request->get('columns') ?? false;
1111 4
1112 4
        if (isset(Arr::first($this->indexColumns)['thumb'])
1113 4
            && Arr::first($this->indexColumns)['thumb']
1114
        ) {
1115
            array_push($tableColumns, [
1116
                'name' => 'thumbnail',
1117 6
                'label' => twillTrans('twill::lang.listing.columns.thumbnail'),
1118 5
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
1119 5
                'optional' => true,
1120 5
                'sortable' => false,
1121 5
            ]);
1122
            array_shift($this->indexColumns);
1123
        }
1124
1125
        if ($this->getIndexOption('feature')) {
1126
            array_push($tableColumns, [
1127 6
                'name' => 'featured',
1128
                'label' => twillTrans('twill::lang.listing.columns.featured'),
1129
                'visible' => true,
1130
                'optional' => false,
1131
                'sortable' => false,
1132
            ]);
1133
        }
1134
1135 5
        if ($this->getIndexOption('publish')) {
1136
            array_push($tableColumns, [
1137 5
                'name' => 'published',
1138
                'label' => twillTrans('twill::lang.listing.columns.published'),
1139 5
                'visible' => true,
1140
                'optional' => false,
1141 5
                'sortable' => false,
1142
            ]);
1143 5
        }
1144 5
1145 5
        array_push($tableColumns, [
1146 5
            'name' => 'name',
1147
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? twillTrans('twill::lang.listing.columns.name'),
1148
            'visible' => true,
1149 5
            'optional' => false,
1150 5
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
1151 5
        ]);
1152 5
1153 5
        unset($this->indexColumns[$this->titleColumnKey]);
1154
1155
        foreach ($this->indexColumns as $column) {
1156
            $columnName = isset($column['relationship'])
1157 5
            ? $column['relationship'] . ucfirst($column['field'])
1158 5
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
1159 5
1160 5
            array_push($tableColumns, [
1161 5
                'name' => $columnName,
1162
                'label' => $column['title'],
1163 5
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
1164 5
                'optional' => $column['optional'] ?? true,
1165 5
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
1166
                'html' => $column['html'] ?? false,
1167
            ]);
1168
        }
1169 5
        if ($this->moduleHas('translations') && count(getLocales()) > 1) {
1170 5
            array_push($tableColumns, [
1171 5
                'name' => 'languages',
1172 5
                'label' => twillTrans('twill::lang.listing.languages'),
1173 5
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
1174
                'optional' => true,
1175
                'sortable' => false,
1176
            ]);
1177 5
        }
1178
1179
        return $tableColumns;
1180
    }
1181
1182
    /**
1183
     * @param \Illuminate\Database\Eloquent\Collection $items
1184
     * @param array $scopes
1185 6
     * @return array
1186
     */
1187 6
    protected function getIndexTableMainFilters($items, $scopes = [])
1188 6
    {
1189
        $statusFilters = [];
1190
1191
        $scope = ($this->submodule ? [
1192
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
1193
        ] : []) + $scopes;
1194
1195
        array_push($statusFilters, [
1196
            'name' => twillTrans('twill::lang.listing.filter.all-items'),
1197
            'slug' => 'all',
1198
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
1199
        ]);
1200 6
1201
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
1202 6
            array_push($statusFilters, [
1203 6
                'name' => twillTrans('twill::lang.listing.filter.mine'),
1204 6
                'slug' => 'mine',
1205
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
1206
            ]);
1207 6
        }
1208
1209
        if ($this->getIndexOption('publish')) {
1210
            array_push($statusFilters, [
1211
                'name' => twillTrans('twill::lang.listing.filter.published'),
1212
                'slug' => 'published',
1213
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
1214 33
            ], [
1215
                'name' => twillTrans('twill::lang.listing.filter.draft'),
1216 33
                'slug' => 'draft',
1217
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
1218 33
            ]);
1219
        }
1220
1221 33
        if ($this->getIndexOption('restore')) {
1222
            array_push($statusFilters, [
1223
                'name' => twillTrans('twill::lang.listing.filter.trash'),
1224 33
                'slug' => 'trash',
1225
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1226
            ]);
1227
        }
1228
1229
        return $statusFilters;
1230
    }
1231
1232
    /**
1233
     * @param string $moduleName
1234
     * @param string $routePrefix
1235
     * @return array
1236
     */
1237
    protected function getIndexUrls($moduleName, $routePrefix)
1238
    {
1239
        return Collection::make([
1240
            'create',
1241
            'store',
1242
            'publish',
1243 33
            'bulkPublish',
1244 33
            'restore',
1245 33
            'bulkRestore',
1246
            'forceDelete',
1247
            'bulkForceDelete',
1248
            'reorder',
1249
            'feature',
1250
            'bulkFeature',
1251
            'bulkDelete',
1252 2
        ])->mapWithKeys(function ($endpoint) {
1253
            return [
1254 2
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1255
                    $this->moduleName,
1256
                    $this->routePrefix,
1257
                    $endpoint,
1258 2
                    $this->submodule ? [$this->submoduleParentId] : []
1259 2
                ) : null,
1260 2
            ];
1261
        })->toArray();
1262 2
    }
1263
1264
    /**
1265
     * @param string $option
1266
     * @return bool
1267
     */
1268
    protected function getIndexOption($option)
1269 2
    {
1270
        return once(function () use ($option) {
1271 2
            $customOptionNamesMapping = [
1272
                'store' => 'create',
1273 2
            ];
1274 2
1275 2
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1276 2
1277
            $authorizableOptions = [
1278 2
                'create' => 'edit',
1279 2
                'edit' => 'edit',
1280
                'publish' => 'publish',
1281
                'feature' => 'feature',
1282 2
                'reorder' => 'reorder',
1283 2
                'delete' => 'delete',
1284 2
                'duplicate' => 'duplicate',
1285 2
                'restore' => 'delete',
1286 2
                'forceDelete' => 'delete',
1287 2
                'bulkForceDelete' => 'delete',
1288 2
                'bulkPublish' => 'publish',
1289 2
                'bulkRestore' => 'delete',
1290
                'bulkFeature' => 'feature',
1291
                'bulkDelete' => 'delete',
1292
                'bulkEdit' => 'edit',
1293
                'editInModal' => 'edit',
1294
                'skipCreateModal' => 'edit',
1295
            ];
1296 2
1297
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1298 2
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1299
        });
1300
    }
1301
1302
    /**
1303
     * @param array $prependScope
1304
     * @return array
1305 12
     */
1306
    protected function getBrowserData($prependScope = [])
1307 12
    {
1308
        if ($this->request->has('except')) {
1309 12
            $prependScope['exceptIds'] = $this->request->get('except');
1310
        }
1311 12
1312
        $scopes = $this->filterScope($prependScope);
1313 12
        $items = $this->getBrowserItems($scopes);
1314 1
        $data = $this->getBrowserTableData($items);
1315 1
1316 1
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1317 1
    }
1318
1319
    /**
1320
     * @param \Illuminate\Database\Eloquent\Collection $items
1321
     * @return array
1322
     */
1323
    protected function getBrowserTableData($items)
1324
    {
1325
        $withImage = $this->moduleHas('medias');
1326
1327
        return $items->map(function ($item) use ($withImage) {
1328
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item) {
1329 1
                return $this->getItemColumnData($item, $column);
1330
            })->toArray();
1331
1332 12
            $name = $columnsData[$this->titleColumnKey];
1333 12
            unset($columnsData[$this->titleColumnKey]);
1334 2
1335 2
            return [
1336
                'id' => $item[$this->identifierColumnKey],
1337 2
                'name' => $name,
1338 2
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item[$this->identifierColumnKey]),
1339
                '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

1339
                'endpointType' => $this->repository->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
1340
            ] + $columnsData + ($withImage && !array_key_exists('thumbnail', $columnsData) ? [
1341
                'thumbnail' => $item->defaultCmsImage(['w' => 100, 'h' => 100]),
1342
            ] : []);
1343
        })->toArray();
1344 2
    }
1345
1346
    /**
1347
     * @param array $scopes
1348
     * @return \Illuminate\Database\Eloquent\Collection
1349
     */
1350 12
    protected function getBrowserItems($scopes = [])
1351
    {
1352
        return $this->getIndexItems($scopes, true);
1353
    }
1354
1355
    /**
1356 7
     * @param array $prepend
1357
     * @return array
1358 7
     */
1359
    protected function filterScope($prepend = [])
1360
    {
1361
        $scope = [];
1362 7
1363
        $requestFilters = $this->getRequestFilters();
1364
1365
        $this->filters = array_merge($this->filters, $this->defaultFilters);
1366
1367
        if (array_key_exists('status', $requestFilters)) {
1368 44
            switch ($requestFilters['status']) {
1369
                case 'published':
1370 44
                    $scope['published'] = true;
1371 44
                    break;
1372
                case 'draft':
1373
                    $scope['draft'] = true;
1374
                    break;
1375
                case 'trash':
1376
                    $scope['onlyTrashed'] = true;
1377
                    break;
1378
                case 'mine':
1379
                    $scope['mine'] = true;
1380
                    break;
1381
            }
1382
1383
            unset($requestFilters['status']);
1384
        }
1385
1386
        foreach ($this->filters as $key => $field) {
1387
            if (array_key_exists($key, $requestFilters)) {
1388 12
                $value = $requestFilters[$key];
1389
                if ($value == 0 || !empty($value)) {
1390 12
                    // add some syntaxic sugar to scope the same filter on multiple columns
1391 12
                    $fieldSplitted = explode('|', $field);
1392 1
                    if (count($fieldSplitted) > 1) {
1393
                        $requestValue = $requestFilters[$key];
1394 1
                        Collection::make($fieldSplitted)->each(function ($scopeKey) use (&$scope, $requestValue) {
1395 1
                            $scope[$scopeKey] = $requestValue;
1396
                        });
1397
                    } else {
1398 1
                        $scope[$field] = $requestFilters[$key];
1399 1
                    }
1400
                }
1401
            }
1402
        }
1403
1404 12
        return $prepend + $scope;
1405 12
    }
1406
1407 12
    /**
1408
     * @return array
1409
     */
1410
    protected function getRequestFilters()
1411
    {
1412
        if ($this->request->has('search')) {
1413
            return ['search' => $this->request->get('search')];
1414
        }
1415 5
1416
        return json_decode($this->request->get('filter'), true) ?? [];
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('filter') can also be of type null; however, parameter $json of json_decode() 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

1416
        return json_decode(/** @scrutinizer ignore-type */ $this->request->get('filter'), true) ?? [];
Loading history...
1417
    }
1418 5
1419 4
    /**
1420 1
     * @return void
1421
     */
1422
    protected function applyFiltersDefaultOptions()
1423
    {
1424 5
        if (!count($this->filtersDefaultOptions) || $this->request->has('search')) {
1425 5
            return;
1426 5
        }
1427
1428 5
        $filters = $this->getRequestFilters();
1429
1430
        foreach ($this->filtersDefaultOptions as $filterName => $defaultOption) {
1431 5
            if (!isset($filters[$filterName])) {
1432 5
                $filters[$filterName] = $defaultOption;
1433 5
            }
1434 5
        }
1435 5
1436 5
        $this->request->merge(['filter' => json_encode($filters)]);
1437 5
    }
1438 5
1439 5
    /**
1440 5
     * @return array
1441 5
     */
1442 5
    protected function orderScope()
1443 5
    {
1444 5
        $orders = [];
1445 5
        if ($this->request->has('sortKey') && $this->request->has('sortDir')) {
1446 5
            if (($key = $this->request->get('sortKey')) == 'name') {
1447 5
                $sortKey = $this->titleColumnKey;
1448 5
            } elseif (!empty($key)) {
1449 5
                $sortKey = $key;
1450 5
            }
1451 5
1452 5
            if (isset($sortKey)) {
1453 5
                $orders[$this->indexColumns[$sortKey]['sortKey'] ?? $sortKey] = $this->request->get('sortDir');
1454 5
            }
1455 5
        }
1456 5
1457
        // don't apply default orders if reorder is enabled
1458 5
        $reorder = $this->getIndexOption('reorder');
1459
        $defaultOrders = ($reorder ? [] : ($this->defaultOrders ?? []));
1460
1461
        return $orders + $defaultOrders;
1462
    }
1463
1464
    /**
1465 1
     * @param int $id
1466
     * @param \A17\Twill\Models\Model|null $item
1467 1
     * @return array
1468 1
     */
1469 1
    protected function form($id, $item = null)
1470
    {
1471 1
        if (!$item && $id) {
1472 1
            $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1473 1
        } elseif (!$item && !$id) {
1474 1
            $item = $this->repository->newInstance();
0 ignored issues
show
Bug introduced by
The method newInstance() 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

1474
            /** @scrutinizer ignore-call */ 
1475
            $item = $this->repository->newInstance();
Loading history...
1475 1
        }
1476
1477
        $fullRoutePrefix = 'admin.' . ($this->routePrefix ? $this->routePrefix . '.' : '') . $this->moduleName . '.';
1478
        $previewRouteName = $fullRoutePrefix . 'preview';
1479 1
        $restoreRouteName = $fullRoutePrefix . 'restoreRevision';
1480
1481 1
        $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...
1482
1483
        $data = [
1484 1
            'item' => $item,
1485 1
            'moduleName' => $this->moduleName,
1486 1
            'routePrefix' => $this->routePrefix,
1487 1
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
1488
            '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...
1489
            'publishDate24Hr' => Config::get('twill.publish_date_24h') ?? false,
1490
            'publishDateFormat' => Config::get('twill.publish_date_format') ?? null,
1491 1
            'publishDateDisplayFormat' => Config::get('twill.publish_date_display_format') ?? null,
1492
            'translate' => $this->moduleHas('translations'),
1493
            'translateTitle' => $this->titleIsTranslatable(),
1494
            'permalink' => $this->getIndexOption('permalink'),
1495
            'createWithoutModal' => !$item[$this->identifierColumnKey] && $this->getIndexOption('skipCreateModal'),
1496
            'form_fields' => $this->repository->getFormFields($item),
1497
            'baseUrl' => $baseUrl,
1498 5
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
1499
            'saveUrl' => $item[$this->identifierColumnKey] ? $this->getModuleRoute($item[$this->identifierColumnKey], 'update') : moduleRoute($this->moduleName, $this->routePrefix, 'store', [$this->submoduleParentId]),
1500 5
            'editor' => Config::get('twill.enabled.block-editor') && $this->moduleHas('blocks') && !$this->disableEditor,
1501
            'blockPreviewUrl' => Route::has('admin.blocks.preview') ? URL::route('admin.blocks.preview') : '#',
1502
            'availableRepeaters' => $this->getRepeaterList()->toJson(),
1503
            'revisions' => $this->moduleHas('revisions') ? $item->revisionsArray() : null,
1504
        ] + (Route::has($previewRouteName) && $item[$this->identifierColumnKey] ? [
1505
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', [$item[$this->identifierColumnKey]]),
1506
        ] : [])
1507 1
             + (Route::has($restoreRouteName) && $item[$this->identifierColumnKey] ? [
1508
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', [$item[$this->identifierColumnKey]]),
1509 1
        ] : []);
1510
1511
        return array_replace_recursive($data, $this->formData($this->request));
1512
    }
1513
1514
    /**
1515 25
     * @param int $id
1516
     * @return array
1517 25
     */
1518 4
    protected function modalFormData($id)
1519 25
    {
1520
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1521 25
        $fields = $this->repository->getFormFields($item);
1522
        $data = [];
1523 25
1524
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1525 25
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1526
                $data['fields'][] = [
1527
                    'name' => $fieldName,
1528
                    'value' => $fieldValue,
1529
                ];
1530
            }
1531 44
1532
            $data['languages'] = $item->getActiveLanguages();
1533 44
1534
            unset($fields['translations']);
1535
        }
1536
1537
        foreach ($fields as $fieldName => $fieldValue) {
1538
            $data['fields'][] = [
1539 44
                'name' => $fieldName,
1540
                'value' => $fieldValue,
1541 44
            ];
1542 43
        }
1543 43
1544
        return array_replace_recursive($data, $this->formData($this->request));
1545
    }
1546 1
1547
    /**
1548
     * @param Request $request
1549
     * @return array
1550
     */
1551
    protected function formData($request)
1552 44
    {
1553
        return [];
1554 44
    }
1555
1556
    /**
1557
     * @param Request $item
1558
     * @return array
1559
     */
1560 44
    protected function previewData($item)
1561
    {
1562 44
        return [];
1563
    }
1564
1565
    /**
1566
     * @return \A17\Twill\Http\Requests\Admin\Request
1567
     */
1568 44
    protected function validateFormRequest()
1569
    {
1570 44
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, $field) {
1571
            return Auth::guard('twill_users')->user()->cannot($permission);
1572
        })->keys();
1573
1574
        $unauthorizedFields->each(function ($field) {
1575
            $this->request->offsetUnset($field);
1576 44
        });
1577
1578 44
        return App::make($this->getFormRequestClass());
1579
    }
1580
1581
    public function getFormRequestClass()
1582
    {
1583
        $request = "$this->namespace\Http\Requests\Admin\\" . $this->modelName . "Request";
1584
1585
        if (@class_exists($request)) {
1586
            return $request;
1587
        }
1588
1589
        return $this->getCapsuleFormRequestClass($this->modelName);
1590
    }
1591
1592 11
    /**
1593
     * @return string
1594 11
     */
1595
    protected function getNamespace()
1596 11
    {
1597
        return $this->namespace ?? Config::get('twill.namespace');
1598
    }
1599
1600 11
    /**
1601 11
     * @return string
1602 11
     */
1603 11
    protected function getRoutePrefix()
1604 11
    {
1605
        if ($this->request->route() != null) {
1606
            $routePrefix = ltrim(str_replace(Config::get('twill.admin_app_path'), '', $this->request->route()->getPrefix()), "/");
1607
            return str_replace("/", ".", $routePrefix);
1608
        }
1609
1610
        return '';
1611 11
    }
1612
1613 11
    /**
1614
     * @return string
1615
     */
1616
    protected function getModulePermalinkBase()
1617
    {
1618
        $base = '';
1619
        $moduleParts = explode('.', $this->moduleName);
1620
1621 8
        foreach ($moduleParts as $index => $name) {
1622
            if (array_key_last($moduleParts) !== $index) {
1623 8
                $singularName = Str::singular($name);
1624 8
                $modelClass = config('twill.namespace') . '\\Models\\' . Str::studly($singularName);
1625 8
                $model = (new $modelClass)->findOrFail(request()->route()->parameter($singularName));
1626
                $hasSlug = Arr::has(class_uses($modelClass), HasSlug::class);
1627 8
1628
                $base .= $name . '/' . ($hasSlug ? $model->slug : $model->id) . '/';
1629
            } else {
1630
                $base .= $name;
1631
            }
1632
        }
1633
1634
        return $base;
1635 33
    }
1636
1637 33
    /**
1638
     * @return string
1639
     */
1640
    protected function getModelName()
1641
    {
1642
        return $this->modelName ?? ucfirst(Str::singular($this->moduleName));
1643 11
    }
1644
1645 11
    /**
1646 11
     * @return \A17\Twill\Repositories\ModuleRepository
1647
     */
1648
    protected function getRepository()
1649
    {
1650
        return App::make($this->getRepositoryClass($this->modelName));
1651
    }
1652
1653
    public function getRepositoryClass($model)
1654
    {
1655 5
        if (@class_exists($class = "$this->namespace\Repositories\\" . $model . "Repository")) {
1656
            return $class;
1657 5
        }
1658 5
1659 5
        return $this->getCapsuleRepositoryClass($model);
1660 5
    }
1661 5
1662 5
    /**
1663
     * @return string
1664
     */
1665
    protected function getViewPrefix()
1666
    {
1667
        $prefix = "admin.$this->moduleName";
1668 5
1669 1
        if (view()->exists("$prefix.form")) {
1670
            return $prefix;
1671 4
        }
1672
1673 5
        return $this->getCapsuleViewPrefix($this->moduleName);
1674
    }
1675
1676
    /**
1677
     * @return string
1678
     */
1679
    protected function getModelTitle()
1680
    {
1681
        return camelCaseToWords($this->modelName);
1682
    }
1683
1684
    /**
1685
     * @return string
1686
     */
1687
    protected function getParentModuleForeignKey()
1688
    {
1689 5
        $moduleParts = explode('.', $this->moduleName);
1690
1691 5
        return Str::singular($moduleParts[count($moduleParts) - 2]) . '_id';
1692
    }
1693
1694
    /**
1695
     * @return string
1696
     */
1697
    protected function getPermalinkBaseUrl()
1698
    {
1699 1
        $appUrl = Config::get('app.url');
1700
1701 1
        if (blank(parse_url($appUrl)['scheme'] ?? null)) {
1702
            $appUrl = $this->request->getScheme() . '://' . $appUrl;
1703 1
        }
1704 1
1705 1
        return $appUrl . '/'
1706 1
            . ($this->moduleHas('translations') ? '{language}/' : '')
1707 1
            . ($this->moduleHas('revisions') ? '{preview}/' : '')
1708
            . ($this->permalinkBase ?? $this->getModulePermalinkBase())
1709
            . (isset($this->permalinkBase) && empty($this->permalinkBase) ? '' : '/');
1710
    }
1711
1712
    /**
1713
     * @param string $baseUrl
1714
     * @return string
1715 11
     */
1716
    protected function getPermalinkPrefix($baseUrl)
1717 11
    {
1718
        return rtrim(str_replace(['http://', 'https://', '{preview}/', '{language}/'], '', $baseUrl), "/") . '/';
1719
    }
1720
1721
    /**
1722
     * @param int $id
1723
     * @param string $action
1724 22
     * @return string
1725
     */
1726 22
    protected function getModuleRoute($id, $action)
1727 22
    {
1728
        return moduleRoute($this->moduleName, $this->routePrefix, $action, [$id]);
1729
    }
1730
1731
    /**
1732
     * @param string $behavior
1733
     * @return bool
1734
     */
1735 2
    protected function moduleHas($behavior)
1736
    {
1737 2
        return $this->repository->hasBehavior($behavior);
1738
    }
1739
1740
    /**
1741
     * @return bool
1742
     */
1743
    protected function titleIsTranslatable()
1744
    {
1745 13
        return $this->repository->isTranslatable(
1746
            $this->titleColumnKey
1747 13
        );
1748 13
    }
1749 13
1750
    /**
1751
     * @param string|null $back_link
1752
     * @param array $params
1753
     * @return void
1754
     */
1755
    protected function setBackLink($back_link = null, $params = [])
1756
    {
1757 25
        if (!isset($back_link)) {
1758
            if (($back_link = Session::get($this->getBackLinkSessionKey())) == null) {
1759 25
                $back_link = $this->request->headers->get('referer') ?? moduleRoute(
1760 25
                    $this->moduleName,
1761
                    $this->routePrefix,
1762
                    'index',
1763
                    $params
1764
                );
1765 5
            }
1766
        }
1767 5
1768 5
        if (!Session::get($this->moduleName . '_retain')) {
1769 5
            Session::put($this->getBackLinkSessionKey(), $back_link);
1770
        } else {
1771
            Session::put($this->moduleName . '_retain', false);
1772
        }
1773
    }
1774
1775
    /**
1776
     * @param string|null $fallback
1777
     * @param array $params
1778
     * @return string
1779
     */
1780
    protected function getBackLink($fallback = null, $params = [])
1781
    {
1782
        $back_link = Session::get($this->getBackLinkSessionKey(), $fallback);
1783
        return $back_link ?? moduleRoute($this->moduleName, $this->routePrefix, 'index', $params);
1784
    }
1785
1786
    /**
1787
     * @return string
1788
     */
1789
    protected function getBackLinkSessionKey()
1790
    {
1791
        return $this->moduleName . ($this->submodule ? $this->submoduleParentId ?? '' : '') . '_back_link';
1792
    }
1793
1794
    /**
1795
     * @param int $id
1796
     * @param array $params
1797
     * @return \Illuminate\Http\RedirectResponse
1798
     */
1799
    protected function redirectToForm($id, $params = [])
1800
    {
1801
        Session::put($this->moduleName . '_retain', true);
1802
1803
        return Redirect::to(moduleRoute(
1804
            $this->moduleName,
1805
            $this->routePrefix,
1806
            'edit',
1807
            array_filter($params) + [Str::singular($this->moduleName) => $id]
1808
        ));
1809
    }
1810
1811
    /**
1812
     * @param string $message
1813
     * @return \Illuminate\Http\JsonResponse
1814
     */
1815
    protected function respondWithSuccess($message)
1816
    {
1817
        return $this->respondWithJson($message, FlashLevel::SUCCESS);
1818
    }
1819
1820
    /**
1821
     * @param string $redirectUrl
1822
     * @return \Illuminate\Http\JsonResponse
1823
     */
1824
    protected function respondWithRedirect($redirectUrl)
1825
    {
1826
        return Response::json([
1827
            'redirect' => $redirectUrl,
1828
        ]);
1829
    }
1830
1831
    /**
1832
     * @param string $message
1833
     * @return \Illuminate\Http\JsonResponse
1834
     */
1835
    protected function respondWithError($message)
1836
    {
1837
        return $this->respondWithJson($message, FlashLevel::ERROR);
1838
    }
1839
1840
    /**
1841
     * @param string $message
1842
     * @param mixed $variant
1843
     * @return \Illuminate\Http\JsonResponse
1844
     */
1845
    protected function respondWithJson($message, $variant)
1846
    {
1847
        return Response::json([
1848
            'message' => $message,
1849
            'variant' => $variant,
1850
        ]);
1851
    }
1852
1853
    /**
1854
     * @param array $input
1855
     * @return void
1856
     */
1857
    protected function fireEvent($input = [])
1858
    {
1859
        fireCmsEvent('cms-module.saved', $input);
1860
    }
1861
1862
    /**
1863
     * @return Collection
1864
     */
1865
    public function getRepeaterList()
1866
    {
1867
        return app(BlockCollection::class)->getRepeaterList()->mapWithKeys(function ($repeater) {
1868
            return [$repeater['name'] => $repeater];
1869
        });
1870
    }
1871
}
1872