Passed
Push — 2.x ( 1073f1...3da7ff )
by
unknown
06:09
created

ModuleController::getLocalizedPermalinkBase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
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
1094 6
            $relation = $item->{$column['relationship']}();
1095
1096
            $value = collect($relation->get())
1097 6
                ->pluck($column['field'])
1098
                ->join(', ');
1099
        } elseif (isset($column['present']) && $column['present']) {
1100 6
            $value = $item->presentAdmin()->{$column['field']};
1101
        }
1102 6
1103 4
        return [
1104
            "$field" => $value,
1105 4
        ];
1106
    }
1107 4
1108 4
    /**
1109 4
     * @param \Illuminate\Database\Eloquent\Collection $items
1110 4
     * @return array
1111 4
     */
1112 4
    protected function getIndexTableColumns($items)
1113 4
    {
1114
        $tableColumns = [];
1115
        $visibleColumns = $this->request->get('columns') ?? false;
1116
1117 6
        if (isset(Arr::first($this->indexColumns)['thumb'])
1118 5
            && Arr::first($this->indexColumns)['thumb']
1119 5
        ) {
1120 5
            array_push($tableColumns, [
1121 5
                'name' => 'thumbnail',
1122
                'label' => twillTrans('twill::lang.listing.columns.thumbnail'),
1123
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
1124
                'optional' => true,
1125
                'sortable' => false,
1126
            ]);
1127 6
            array_shift($this->indexColumns);
1128
        }
1129
1130
        if ($this->getIndexOption('feature')) {
1131
            array_push($tableColumns, [
1132
                'name' => 'featured',
1133
                'label' => twillTrans('twill::lang.listing.columns.featured'),
1134
                'visible' => true,
1135 5
                'optional' => false,
1136
                'sortable' => false,
1137 5
            ]);
1138
        }
1139 5
1140
        if ($this->getIndexOption('publish')) {
1141 5
            array_push($tableColumns, [
1142
                'name' => 'published',
1143 5
                'label' => twillTrans('twill::lang.listing.columns.published'),
1144 5
                'visible' => true,
1145 5
                'optional' => false,
1146 5
                'sortable' => false,
1147
            ]);
1148
        }
1149 5
1150 5
        array_push($tableColumns, [
1151 5
            'name' => 'name',
1152 5
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? twillTrans('twill::lang.listing.columns.name'),
1153 5
            'visible' => true,
1154
            'optional' => false,
1155
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
1156
        ]);
1157 5
1158 5
        unset($this->indexColumns[$this->titleColumnKey]);
1159 5
1160 5
        foreach ($this->indexColumns as $column) {
1161 5
            $columnName = isset($column['relationship'])
1162
            ? $column['relationship'] . ucfirst($column['field'])
1163 5
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
1164 5
1165 5
            array_push($tableColumns, [
1166
                'name' => $columnName,
1167
                'label' => $column['title'],
1168
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
1169 5
                'optional' => $column['optional'] ?? true,
1170 5
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
1171 5
                'html' => $column['html'] ?? false,
1172 5
            ]);
1173 5
        }
1174
        if ($this->moduleHas('translations') && count(getLocales()) > 1) {
1175
            array_push($tableColumns, [
1176
                'name' => 'languages',
1177 5
                'label' => twillTrans('twill::lang.listing.languages'),
1178
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
1179
                'optional' => true,
1180
                'sortable' => false,
1181
            ]);
1182
        }
1183
1184
        return $tableColumns;
1185 6
    }
1186
1187 6
    /**
1188 6
     * @param \Illuminate\Database\Eloquent\Collection $items
1189
     * @param array $scopes
1190
     * @return array
1191
     */
1192
    protected function getIndexTableMainFilters($items, $scopes = [])
1193
    {
1194
        $statusFilters = [];
1195
1196
        $scope = ($this->submodule ? [
1197
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
1198
        ] : []) + $scopes;
1199
1200 6
        array_push($statusFilters, [
1201
            'name' => twillTrans('twill::lang.listing.filter.all-items'),
1202 6
            'slug' => 'all',
1203 6
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
1204 6
        ]);
1205
1206
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
1207 6
            array_push($statusFilters, [
1208
                'name' => twillTrans('twill::lang.listing.filter.mine'),
1209
                'slug' => 'mine',
1210
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
1211
            ]);
1212
        }
1213
1214 33
        if ($this->getIndexOption('publish')) {
1215
            array_push($statusFilters, [
1216 33
                'name' => twillTrans('twill::lang.listing.filter.published'),
1217
                'slug' => 'published',
1218 33
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
1219
            ], [
1220
                'name' => twillTrans('twill::lang.listing.filter.draft'),
1221 33
                'slug' => 'draft',
1222
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
1223
            ]);
1224 33
        }
1225
1226
        if ($this->getIndexOption('restore')) {
1227
            array_push($statusFilters, [
1228
                'name' => twillTrans('twill::lang.listing.filter.trash'),
1229
                'slug' => 'trash',
1230
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1231
            ]);
1232
        }
1233
1234
        return $statusFilters;
1235
    }
1236
1237
    /**
1238
     * @param string $moduleName
1239
     * @param string $routePrefix
1240
     * @return array
1241
     */
1242
    protected function getIndexUrls($moduleName, $routePrefix)
1243 33
    {
1244 33
        return Collection::make([
1245 33
            'create',
1246
            'store',
1247
            'publish',
1248
            'bulkPublish',
1249
            'restore',
1250
            'bulkRestore',
1251
            'forceDelete',
1252 2
            'bulkForceDelete',
1253
            'reorder',
1254 2
            'feature',
1255
            'bulkFeature',
1256
            'bulkDelete',
1257
        ])->mapWithKeys(function ($endpoint) {
1258 2
            return [
1259 2
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1260 2
                    $this->moduleName,
1261
                    $this->routePrefix,
1262 2
                    $endpoint,
1263
                    $this->submodule ? [$this->submoduleParentId] : []
1264
                ) : null,
1265
            ];
1266
        })->toArray();
1267
    }
1268
1269 2
    /**
1270
     * @param string $option
1271 2
     * @return bool
1272
     */
1273 2
    protected function getIndexOption($option)
1274 2
    {
1275 2
        return once(function () use ($option) {
1276 2
            $customOptionNamesMapping = [
1277
                'store' => 'create',
1278 2
            ];
1279 2
1280
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1281
1282 2
            $authorizableOptions = [
1283 2
                'create' => 'edit',
1284 2
                'edit' => 'edit',
1285 2
                'publish' => 'publish',
1286 2
                'feature' => 'feature',
1287 2
                'reorder' => 'reorder',
1288 2
                'delete' => 'delete',
1289 2
                'duplicate' => 'duplicate',
1290
                'restore' => 'delete',
1291
                'forceDelete' => 'delete',
1292
                'bulkForceDelete' => 'delete',
1293
                'bulkPublish' => 'publish',
1294
                'bulkRestore' => 'delete',
1295
                'bulkFeature' => 'feature',
1296 2
                'bulkDelete' => 'delete',
1297
                'bulkEdit' => 'edit',
1298 2
                'editInModal' => 'edit',
1299
                'skipCreateModal' => 'edit',
1300
            ];
1301
1302
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1303
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1304
        });
1305 12
    }
1306
1307 12
    /**
1308
     * @param array $prependScope
1309 12
     * @return array
1310
     */
1311 12
    protected function getBrowserData($prependScope = [])
1312
    {
1313 12
        if ($this->request->has('except')) {
1314 1
            $prependScope['exceptIds'] = $this->request->get('except');
1315 1
        }
1316 1
1317 1
        $scopes = $this->filterScope($prependScope);
1318
        $items = $this->getBrowserItems($scopes);
1319
        $data = $this->getBrowserTableData($items);
1320
1321
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1322
    }
1323
1324
    /**
1325
     * @param \Illuminate\Database\Eloquent\Collection $items
1326
     * @return array
1327
     */
1328
    protected function getBrowserTableData($items)
1329 1
    {
1330
        $withImage = $this->moduleHas('medias');
1331
1332 12
        return $items->map(function ($item) use ($withImage) {
1333 12
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item) {
1334 2
                return $this->getItemColumnData($item, $column);
1335 2
            })->toArray();
1336
1337 2
            $name = $columnsData[$this->titleColumnKey];
1338 2
            unset($columnsData[$this->titleColumnKey]);
1339
1340
            return [
1341
                'id' => $item[$this->identifierColumnKey],
1342
                'name' => $name,
1343
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item[$this->identifierColumnKey]),
1344 2
                'endpointType' => $this->repository->getMorphClass(),
0 ignored issues
show
Bug introduced by
The method getMorphClass() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

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

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

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