Passed
Pull Request — master (#532)
by Antonio Carlos
10:20
created

ModuleController::titleIsTranslatable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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

426
            /** @scrutinizer ignore-call */ 
427
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
Loading history...
427
        } else {
428 1
            $formRequest = $this->validateFormRequest();
429 1
            $item = $this->repository->preview($id, $formRequest->all());
0 ignored issues
show
Bug introduced by
The method preview() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

429
            /** @scrutinizer ignore-call */ 
430
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
430
        }
431
432 1
        if ($this->request->has('activeLanguage')) {
433
            App::setLocale($this->request->get('activeLanguage'));
434
        }
435
436 1
        $previewView = $this->previewView ?? (Config::get('twill.frontend.views_path', 'site') . '.' . Str::singular($this->moduleName));
0 ignored issues
show
Bug Best Practice introduced by
The property previewView does not exist on A17\Twill\Http\Controllers\Admin\ModuleController. Did you maybe forget to declare it?
Loading history...
437
438 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
439 1
            'item' => $item,
440 1
        ], $this->previewData($item))) : View::make('twill::errors.preview', [
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $item of A17\Twill\Http\Controlle...ntroller::previewData() does only seem to accept Illuminate\Http\Request, maybe add an additional type check? ( Ignorable by Annotation )

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

440
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
441 1
            'moduleName' => Str::singular($this->moduleName),
442
        ]);
443
    }
444
445
    /**
446
     * @param int $id
447
     * @return \Illuminate\View\View
448
     */
449 2
    public function restoreRevision($id)
450
    {
451 2
        if ($this->request->has('revisionId')) {
452 1
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
453 1
            $item->id = $id;
0 ignored issues
show
Bug introduced by
The property id does not seem to exist on Illuminate\Database\Eloquent\Builder.
Loading history...
454 1
            $item->cmsRestoring = true;
0 ignored issues
show
Bug introduced by
The property cmsRestoring does not seem to exist on Illuminate\Database\Eloquent\Builder.
Loading history...
455
        } else {
456 1
            throw new NotFoundHttpException();
457
        }
458
459 1
        $this->setBackLink();
460
461 1
        $view = Collection::make([
462 1
            "$this->viewPrefix.form",
463 1
            "twill::$this->moduleName.form",
464 1
            "twill::layouts.form",
465
        ])->first(function ($view) {
466 1
            return View::exists($view);
467 1
        });
468
469 1
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
470 1
        $date = $revision->created_at->toDayDateTimeString();
0 ignored issues
show
Bug introduced by
The method toDayDateTimeString() does not exist on DateTime. It seems like you code against a sub-type of DateTime such as Carbon\Carbon. ( Ignorable by Annotation )

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

470
        /** @scrutinizer ignore-call */ 
471
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
471
472 1
        Session::flash('restoreMessage', "You are currently editing an older revision of this content (saved by $revision->byUser on $date). Make changes if needed and click restore to save a new revision.");
0 ignored issues
show
Bug introduced by
The property byUser does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
473
474 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

474
        return View::make($view, $this->form($id, /** @scrutinizer ignore-type */ $item));
Loading history...
475
    }
476
477
    /**
478
     * @return \Illuminate\Http\JsonResponse
479
     */
480 2
    public function publish()
481
    {
482
        try {
483 2
            if ($this->repository->updateBasic($this->request->get('id'), [
484 2
                'published' => !$this->request->get('active'),
485
            ])) {
486 2
                activity()->performedOn(
487 2
                    $this->repository->getById($this->request->get('id'))
488 1
                )->log(
489 1
                    ($this->request->get('active') ? 'un' : '') . 'published'
490
                );
491
492 1
                $this->fireEvent();
493
494 1
                return $this->respondWithSuccess(
495 1
                    $this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'published!'
496
                );
497
            }
498 1
        } catch (\Exception $e) {
499 1
            \Log::error($e);
500
        }
501
502 1
        return $this->respondWithError(
503 1
            $this->modelTitle . ' was not published. Something wrong happened!'
504
        );
505
    }
506
507
    /**
508
     * @return \Illuminate\Http\JsonResponse
509
     */
510
    public function bulkPublish()
511
    {
512
        try {
513
            if ($this->repository->updateBasic(explode(',', $this->request->get('ids')), [
514
                'published' => $this->request->get('publish'),
515
            ])) {
516
                $this->fireEvent();
517
518
                return $this->respondWithSuccess(
519
                    $this->modelTitle . ' items ' . ($this->request->get('publish') ? '' : 'un') . 'published!'
520
                );
521
            }
522
        } catch (\Exception $e) {
523
            \Log::error($e);
524
        }
525
526
        return $this->respondWithError(
527
            $this->modelTitle . ' items were not published. Something wrong happened!'
528
        );
529
    }
530
531
    /**
532
     * @param int $id
533
     * @param int|null $submoduleId
534
     * @return \Illuminate\Http\JsonResponse
535
     */
536 2
    public function destroy($id, $submoduleId = null)
537
    {
538 2
        $item = $this->repository->getById($submoduleId ?? $id);
539 2
        if ($this->repository->delete($submoduleId ?? $id)) {
540 2
            $this->fireEvent();
541 2
            activity()->performedOn($item)->log('deleted');
542 2
            return $this->respondWithSuccess($this->modelTitle . ' moved to trash!');
543
        }
544
545
        return $this->respondWithError($this->modelTitle . ' was not moved to trash. Something wrong happened!');
546
    }
547
548
    /**
549
     * @return \Illuminate\Http\JsonResponse
550
     */
551
    public function bulkDelete()
552
    {
553
        if ($this->repository->bulkDelete(explode(',', $this->request->get('ids')))) {
554
            $this->fireEvent();
555
            return $this->respondWithSuccess($this->modelTitle . ' items moved to trash!');
556
        }
557
558
        return $this->respondWithError($this->modelTitle . ' items were not moved to trash. Something wrong happened!');
559
    }
560
561
    /**
562
     * @return \Illuminate\Http\JsonResponse
563
     */
564 2
    public function restore()
565
    {
566 2
        if ($this->repository->restore($this->request->get('id'))) {
567 1
            $this->fireEvent();
568 1
            activity()->performedOn($this->repository->getById($this->request->get('id')))->log('restored');
569 1
            return $this->respondWithSuccess($this->modelTitle . ' restored!');
570
        }
571
572 1
        return $this->respondWithError($this->modelTitle . ' was not restored. Something wrong happened!');
573
    }
574
575
    /**
576
     * @return \Illuminate\Http\JsonResponse
577
     */
578
    public function bulkRestore()
579
    {
580
        if ($this->repository->bulkRestore(explode(',', $this->request->get('ids')))) {
581
            $this->fireEvent();
582
            return $this->respondWithSuccess($this->modelTitle . ' items restored!');
583
        }
584
585
        return $this->respondWithError($this->modelTitle . ' items were not restored. Something wrong happened!');
586
    }
587
588
    /**
589
     * @return \Illuminate\Http\JsonResponse
590
     */
591 2
    public function feature()
592
    {
593 2
        if (($id = $this->request->get('id'))) {
594 2
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
595 2
            $featured = !$this->request->get('active');
596
597 2
            if ($this->repository->isUniqueFeature()) {
598
                if ($featured) {
599
                    $this->repository->updateBasic(null, [$featuredField => false]);
600
                    $this->repository->updateBasic($id, [$featuredField => $featured]);
601
                }
602
            } else {
603 2
                $this->repository->updateBasic($id, [$featuredField => $featured]);
604
            }
605
606 2
            activity()->performedOn(
607 2
                $this->repository->getById($id)
608 1
            )->log(
609 1
                ($this->request->get('active') ? 'un' : '') . 'featured'
610
            );
611
612 1
            $this->fireEvent();
613 1
            return $this->respondWithSuccess($this->modelTitle . ' ' . ($this->request->get('active') ? 'un' : '') . 'featured!');
614
        }
615
616
        return $this->respondWithError($this->modelTitle . ' was not featured. Something wrong happened!');
617
    }
618
619
    /**
620
     * @return \Illuminate\Http\JsonResponse
621
     */
622
    public function bulkFeature()
623
    {
624
        if (($ids = explode(',', $this->request->get('ids')))) {
625
            $featuredField = $this->request->get('featureField') ?? $this->featureField;
626
            $featured = $this->request->get('feature') ?? true;
627
            // we don't need to check if unique feature since bulk operation shouldn't be allowed in this case
628
            $this->repository->updateBasic($ids, [$featuredField => $featured]);
629
            $this->fireEvent();
630
            return $this->respondWithSuccess($this->modelTitle . ' items ' . ($this->request->get('feature') ? '' : 'un') . 'featured!');
631
        }
632
633
        return $this->respondWithError($this->modelTitle . ' items were not featured. Something wrong happened!');
634
    }
635
636
    /**
637
     * @return \Illuminate\Http\JsonResponse
638
     */
639 2
    public function reorder()
640
    {
641 2
        if (($values = $this->request->get('ids')) && !empty($values)) {
642 2
            $this->repository->setNewOrder($values);
643 1
            $this->fireEvent();
644 1
            return $this->respondWithSuccess($this->modelTitle . ' order changed!');
645
        }
646
647
        return $this->respondWithError($this->modelTitle . ' order was not changed. Something wrong happened!');
648
    }
649
650
    /**
651
     * @return \Illuminate\Http\JsonResponse
652
     */
653 1
    public function tags()
654
    {
655 1
        $query = $this->request->input('q');
656 1
        $tags = $this->repository->getTags($query);
0 ignored issues
show
Bug introduced by
The method getTags() does not exist on A17\Twill\Repositories\ModuleRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

656
        /** @scrutinizer ignore-call */ 
657
        $tags = $this->repository->getTags($query);
Loading history...
657
658
        return Response::json(['items' => $tags->map(function ($tag) {
659
            return $tag->name;
660 1
        })], 200);
661
    }
662
663
    /**
664
     * @param array $prependScope
665
     * @return array
666
     */
667 6
    protected function getIndexData($prependScope = [])
668
    {
669 6
        $scopes = $this->filterScope($prependScope);
670 6
        $items = $this->getIndexItems($scopes);
671
672
        $data = [
673 6
            'tableData' => $this->getIndexTableData($items),
674 6
            'tableColumns' => $this->getIndexTableColumns($items),
675 6
            'tableMainFilters' => $this->getIndexTableMainFilters($items),
676 6
            'filters' => json_decode($this->request->get('filter'), true) ?? [],
677 6
            'hiddenFilters' => array_keys(Arr::except($this->filters, array_keys($this->defaultFilters))),
678 6
            'filterLinks' => $this->filterLinks ?? [],
679 6
            'maxPage' => method_exists($items, 'lastPage') ? $items->lastPage() : 1,
680 6
            'defaultMaxPage' => method_exists($items, 'total') ? ceil($items->total() / $this->perPage) : 1,
681 6
            'offset' => method_exists($items, 'perPage') ? $items->perPage() : count($items),
682 6
            'defaultOffset' => $this->perPage,
683 6
        ] + $this->getIndexUrls($this->moduleName, $this->routePrefix);
684
685 6
        $baseUrl = $this->getPermalinkBaseUrl();
686
687
        $options = [
688 6
            'moduleName' => $this->moduleName,
689 6
            'reorder' => $this->getIndexOption('reorder'),
690 6
            'create' => $this->getIndexOption('create'),
691 6
            'translate' => $this->moduleHas('translations'),
692 6
            'translateTitle' => $this->titleIsTranslatable(),
693 6
            'permalink' => $this->getIndexOption('permalink'),
694 6
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
695 6
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
696 6
            'baseUrl' => $baseUrl,
697 6
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
698
        ];
699
700 6
        return array_replace_recursive($data + $options, $this->indexData($this->request));
701
    }
702
703
    /**
704
     * @param Request $request
705
     * @return array
706
     */
707 5
    protected function indexData($request)
708
    {
709 5
        return [];
710
    }
711
712
    /**
713
     * @param array $scopes
714
     * @param bool $forcePagination
715
     * @return \Illuminate\Database\Eloquent\Collection
716
     */
717 12
    protected function getIndexItems($scopes = [], $forcePagination = false)
718
    {
719 12
        return $this->transformIndexItems($this->repository->get(
720 12
            $this->indexWith,
721
            $scopes,
722 12
            $this->orderScope(),
723 12
            $this->request->get('offset') ?? $this->perPage ?? 50,
724
            $forcePagination
725
        ));
726
    }
727
728
    /**
729
     * @param \Illuminate\Database\Eloquent\Collection $items
730
     * @return \Illuminate\Database\Eloquent\Collection
731
     */
732 10
    protected function transformIndexItems($items)
733
    {
734 10
        return $items;
735
    }
736
737
    /**
738
     * @param \Illuminate\Database\Eloquent\Collection $items
739
     * @return array
740
     */
741 6
    protected function getIndexTableData($items)
742
    {
743 6
        $translated = $this->moduleHas('translations');
744
        return $items->map(function ($item) use ($translated) {
745
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
746 3
                return $this->getItemColumnData($item, $column);
747 3
            })->toArray();
748
749 3
            $name = $columnsData[$this->titleColumnKey];
750
751 3
            if (empty($name)) {
752
                if ($this->moduleHas('translations')) {
753
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
754
755
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
756
                        $name = $fallBackTranslation->{$this->titleColumnKey};
757
                    }
758
                }
759
760
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
761
            }
762
763 3
            unset($columnsData[$this->titleColumnKey]);
764
765 3
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
766 3
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
767 3
            $canEdit = $this->getIndexOption('edit');
768
769 3
            return array_replace([
770 3
                'id' => $item->id,
771 3
                'name' => $name,
772 3
                'publish_start_date' => $item->publish_start_date,
773 3
                'publish_end_date' => $item->publish_end_date,
774 3
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
775 3
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
776 3
            ] + ($this->getIndexOption('editInModal') ? [
777 1
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
778 1
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
779 3
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
780 3
                'published' => $item->published,
781 3
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
782
                'featured' => $item->{$this->featureField},
783 3
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
784
                'deleted' => true,
785 3
            ] : []) + ($translated ? [
786 3
                'languages' => $item->getActiveLanguages(),
787 3
            ] : []) + $columnsData, $this->indexItemData($item));
788 6
        })->toArray();
789
    }
790
791
    /**
792
     * @param \A17\Twill\Models\Model $item
793
     * @return array
794
     */
795 2
    protected function indexItemData($item)
796
    {
797 2
        return [];
798
    }
799
800
    /**
801
     * @param \A17\Twill\Models\Model $item
802
     * @param array $column
803
     * @return array
804
     */
805 5
    protected function getItemColumnData($item, $column)
806
    {
807 5
        if (isset($column['thumb']) && $column['thumb']) {
808 2
            if (isset($column['present']) && $column['present']) {
809
                return [
810
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
811
                ];
812
            } else {
813 2
                $variant = isset($column['variant']);
814 2
                $role = $variant ? $column['variant']['role'] : head(array_keys($item->mediasParams));
0 ignored issues
show
Bug introduced by
The property mediasParams does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
815 2
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
816 2
                $params = $variant && isset($column['variant']['params'])
817
                ? $column['variant']['params']
818 2
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
819
820
                return [
821 2
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
822
                ];
823
            }
824
        }
825
826 5
        if (isset($column['nested']) && $column['nested']) {
827
            $field = $column['nested'];
828
            $nestedCount = $item->{$column['nested']}->count();
829
            $value = '<a href="';
830
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
831
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
832
                ? Str::plural($column['title'])
833
                : Str::singular($column['title']))) . '</a>';
834
        } else {
835 5
            $field = $column['field'];
836 5
            $value = $item->$field;
837
        }
838
839 5
        if (isset($column['relationship'])) {
840
            $field = $column['relationship'] . ucfirst($column['field']);
841
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
842 5
        } elseif (isset($column['present']) && $column['present']) {
843
            $value = $item->presentAdmin()->{$column['field']};
844
        }
845
846
        return [
847 5
            "$field" => $value,
848
        ];
849
    }
850
851
    /**
852
     * @param \Illuminate\Database\Eloquent\Collection $items
853
     * @return array
854
     */
855 6
    protected function getIndexTableColumns($items)
0 ignored issues
show
Unused Code introduced by
The parameter $items is not used and could be removed. ( Ignorable by Annotation )

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

855
    protected function getIndexTableColumns(/** @scrutinizer ignore-unused */ $items)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
856
    {
857 6
        $tableColumns = [];
858 6
        $visibleColumns = $this->request->get('columns') ?? false;
859
860 6
        if (isset(Arr::first($this->indexColumns)['thumb'])
861 6
            && Arr::first($this->indexColumns)['thumb']
862
        ) {
863 4
            array_push($tableColumns, [
864 4
                'name' => 'thumbnail',
865 4
                'label' => 'Thumbnail',
866 4
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
867
                'optional' => true,
868
                'sortable' => false,
869
            ]);
870 4
            array_shift($this->indexColumns);
871
        }
872
873 6
        if ($this->getIndexOption('feature')) {
874
            array_push($tableColumns, [
875
                'name' => 'featured',
876
                'label' => 'Featured',
877
                'visible' => true,
878
                'optional' => false,
879
                'sortable' => false,
880
            ]);
881
        }
882
883 6
        if ($this->getIndexOption('publish')) {
884 6
            array_push($tableColumns, [
885 6
                'name' => 'published',
886
                'label' => 'Published',
887
                'visible' => true,
888
                'optional' => false,
889
                'sortable' => false,
890
            ]);
891
        }
892
893 6
        array_push($tableColumns, [
894 6
            'name' => 'name',
895 6
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? 'Name',
896
            'visible' => true,
897
            'optional' => false,
898 6
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
899
        ]);
900
901 6
        unset($this->indexColumns[$this->titleColumnKey]);
902
903 6
        foreach ($this->indexColumns as $column) {
904 4
            $columnName = isset($column['relationship'])
905
            ? $column['relationship'] . ucfirst($column['field'])
906 4
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
907
908 4
            array_push($tableColumns, [
909 4
                'name' => $columnName,
910 4
                'label' => $column['title'],
911 4
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
912 4
                'optional' => $column['optional'] ?? true,
913 4
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
914 4
                'html' => $column['html'] ?? false,
915
            ]);
916
        }
917
918 6
        if ($this->moduleHas('translations')) {
919 5
            array_push($tableColumns, [
920 5
                'name' => 'languages',
921 5
                'label' => 'Languages',
922 5
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
923
                'optional' => true,
924
                'sortable' => false,
925
            ]);
926
        }
927
928 6
        return $tableColumns;
929
    }
930
931
    /**
932
     * @param \Illuminate\Database\Eloquent\Collection $items
933
     * @param array $scopes
934
     * @return array
935
     */
936 5
    protected function getIndexTableMainFilters($items, $scopes = [])
937
    {
938 5
        $statusFilters = [];
939
940 5
        $scope = ($this->submodule ? [
941
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
942 5
        ] : []) + $scopes;
943
944 5
        array_push($statusFilters, [
945 5
            'name' => 'All items',
946 5
            'slug' => 'all',
947 5
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
948
        ]);
949
950 5
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
951 5
            array_push($statusFilters, [
952 5
                'name' => 'Mine',
953 5
                'slug' => 'mine',
954 5
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
955
            ]);
956
        }
957
958 5
        if ($this->getIndexOption('publish')) {
959 5
            array_push($statusFilters, [
960 5
                'name' => 'Published',
961 5
                'slug' => 'published',
962 5
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
963
            ], [
964 5
                'name' => 'Draft',
965 5
                'slug' => 'draft',
966 5
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
967
            ]);
968
        }
969
970 5
        if ($this->getIndexOption('restore')) {
971 5
            array_push($statusFilters, [
972 5
                'name' => 'Trash',
973 5
                'slug' => 'trash',
974 5
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
975
            ]);
976
        }
977
978 5
        return $statusFilters;
979
    }
980
981
    /**
982
     * @param string $moduleName
983
     * @param string $routePrefix
984
     * @return array
985
     */
986 6
    protected function getIndexUrls($moduleName, $routePrefix)
987
    {
988 6
        return Collection::make([
989 6
            'store',
990
            'publish',
991
            'bulkPublish',
992
            'restore',
993
            'bulkRestore',
994
            'reorder',
995
            'feature',
996
            'bulkFeature',
997
            'bulkDelete',
998
        ])->mapWithKeys(function ($endpoint) use ($moduleName, $routePrefix) {
0 ignored issues
show
Unused Code introduced by
The import $moduleName is not used and could be removed.

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

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

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

Loading history...
999
            return [
1000 6
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1001 6
                    $this->moduleName, $this->routePrefix, $endpoint,
1002 6
                    $this->submodule ? [$this->submoduleParentId] : []
1003
                ) : null,
1004
            ];
1005 6
        })->toArray();
1006
    }
1007
1008
    /**
1009
     * @param string $option
1010
     * @return bool
1011
     */
1012 29
    protected function getIndexOption($option)
1013
    {
1014
        return once(function () use ($option) {
1015
            $customOptionNamesMapping = [
1016 29
                'store' => 'create',
1017
            ];
1018
1019 29
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1020
1021
            $authorizableOptions = [
1022 29
                'create' => 'edit',
1023
                'edit' => 'edit',
1024
                'publish' => 'publish',
1025
                'feature' => 'feature',
1026
                'reorder' => 'reorder',
1027
                'delete' => 'delete',
1028
                'restore' => 'delete',
1029
                'bulkPublish' => 'publish',
1030
                'bulkRestore' => 'delete',
1031
                'bulkFeature' => 'feature',
1032
                'bulkDelete' => 'delete',
1033
                'bulkEdit' => 'edit',
1034
                'editInModal' => 'edit',
1035
            ];
1036
1037 29
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1038 29
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1039 29
        });
1040
    }
1041
1042
    /**
1043
     * @param array $prependScope
1044
     * @return array
1045
     */
1046 2
    protected function getBrowserData($prependScope = [])
1047
    {
1048 2
        if ($this->request->has('except')) {
1049
            $prependScope['exceptIds'] = $this->request->get('except');
1050
        }
1051
1052 2
        $scopes = $this->filterScope($prependScope);
1053 2
        $items = $this->getBrowserItems($scopes);
1054 2
        $data = $this->getBrowserTableData($items);
1055
1056 2
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1057
    }
1058
1059
    /**
1060
     * @param \Illuminate\Database\Eloquent\Collection $items
1061
     * @return array
1062
     */
1063 2
    protected function getBrowserTableData($items)
1064
    {
1065 2
        $withImage = $this->moduleHas('medias');
1066
1067
        return $items->map(function ($item) use ($withImage) {
1068
            $columnsData = Collection::make($this->browserColumns)->mapWithKeys(function ($column) use ($item, $withImage) {
0 ignored issues
show
Unused Code introduced by
The import $withImage is not used and could be removed.

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

Loading history...
1069 2
                return $this->getItemColumnData($item, $column);
1070 2
            })->toArray();
1071
1072 2
            $name = $columnsData[$this->titleColumnKey];
1073 2
            unset($columnsData[$this->titleColumnKey]);
1074
1075
            return [
1076 2
                'id' => $item->id,
1077 2
                'name' => $name,
1078 2
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1079 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

1079
                'endpointType' => $this->repository->/** @scrutinizer ignore-call */ getMorphClass(),
Loading history...
1080 2
            ] + $columnsData + ($withImage && !array_key_exists('thumbnail', $columnsData) ? [
1081 2
                'thumbnail' => $item->defaultCmsImage(['w' => 100, 'h' => 100]),
1082 2
            ] : []);
1083 2
        })->toArray();
1084
    }
1085
1086
    /**
1087
     * @param array $scopes
1088
     * @return \Illuminate\Database\Eloquent\Collection
1089
     */
1090 2
    protected function getBrowserItems($scopes = [])
1091
    {
1092 2
        return $this->getIndexItems($scopes, true);
1093
    }
1094
1095
    /**
1096
     * @param array $prepend
1097
     * @return array
1098
     */
1099 12
    protected function filterScope($prepend = [])
1100
    {
1101 12
        $scope = [];
1102
1103 12
        $requestFilters = $this->getRequestFilters();
1104
1105 12
        $this->filters = array_merge($this->filters, $this->defaultFilters);
1106
1107 12
        if (array_key_exists('status', $requestFilters)) {
1108 1
            switch ($requestFilters['status']) {
1109 1
                case 'published':
1110 1
                    $scope['published'] = true;
1111 1
                    break;
1112
                case 'draft':
1113
                    $scope['draft'] = true;
1114
                    break;
1115
                case 'trash':
1116
                    $scope['onlyTrashed'] = true;
1117
                    break;
1118
                case 'mine':
1119
                    $scope['mine'] = true;
1120
                    break;
1121
            }
1122
1123 1
            unset($requestFilters['status']);
1124
        }
1125
1126 12
        foreach ($this->filters as $key => $field) {
1127 12
            if (array_key_exists($key, $requestFilters)) {
1128 2
                $value = $requestFilters[$key];
1129 2
                if ($value == 0 || !empty($value)) {
1130
                    // add some syntaxic sugar to scope the same filter on multiple columns
1131 2
                    $fieldSplitted = explode('|', $field);
1132 2
                    if (count($fieldSplitted) > 1) {
1133
                        $requestValue = $requestFilters[$key];
1134
                        Collection::make($fieldSplitted)->each(function ($scopeKey) use (&$scope, $requestValue) {
1135
                            $scope[$scopeKey] = $requestValue;
1136
                        });
1137
                    } else {
1138 2
                        $scope[$field] = $requestFilters[$key];
1139
                    }
1140
                }
1141
            }
1142
        }
1143
1144 12
        return $prepend + $scope;
1145
    }
1146
1147
    /**
1148
     * @return array
1149
     */
1150 7
    protected function getRequestFilters()
1151
    {
1152 7
        if ($this->request->has('search')) {
1153
            return ['search' => $this->request->get('search')];
1154
        }
1155
1156 7
        return json_decode($this->request->get('filter'), true) ?? [];
1157
    }
1158
1159
    /**
1160
     * @return array
1161
     */
1162 12
    protected function orderScope()
1163
    {
1164 12
        $orders = [];
1165 12
        if ($this->request->has('sortKey') && $this->request->has('sortDir')) {
1166 1
            if (($key = $this->request->get('sortKey')) == 'name') {
1167
                $sortKey = $this->titleColumnKey;
1168 1
            } elseif (!empty($key)) {
1169 1
                $sortKey = $key;
1170
            }
1171
1172 1
            if (isset($sortKey)) {
1173 1
                $orders[$this->indexColumns[$sortKey]['sortKey'] ?? $sortKey] = $this->request->get('sortDir');
1174
            }
1175
        }
1176
1177
        // don't apply default orders if reorder is enabled
1178 12
        $reorder = $this->getIndexOption('reorder');
1179 12
        $defaultOrders = ($reorder ? [] : ($this->defaultOrders ?? []));
1180
1181 12
        return $orders + $defaultOrders;
1182
    }
1183
1184
    /**
1185
     * @param int $id
1186
     * @param \A17\Twill\Models\Model|null $item
1187
     * @return array
1188
     */
1189 4
    protected function form($id, $item = null)
1190
    {
1191 4
        $item = $item ?? $this->repository->getById($id, $this->formWith, $this->formWithCount);
1192
1193 4
        $fullRoutePrefix = 'admin.' . ($this->routePrefix ? $this->routePrefix . '.' : '') . $this->moduleName . '.';
1194 4
        $previewRouteName = $fullRoutePrefix . 'preview';
1195 4
        $restoreRouteName = $fullRoutePrefix . 'restoreRevision';
1196
1197 4
        $baseUrl = $item->urlWithoutSlug ?? $this->getPermalinkBaseUrl();
0 ignored issues
show
Bug introduced by
The property urlWithoutSlug does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
1198
1199
        $data = [
1200 4
            'item' => $item,
1201 4
            'moduleName' => $this->moduleName,
1202 4
            'routePrefix' => $this->routePrefix,
1203 4
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
1204 4
            'publish' => $item->canPublish ?? true,
0 ignored issues
show
Bug introduced by
The property canPublish does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
1205 4
            'translate' => $this->moduleHas('translations'),
1206 4
            'permalink' => $this->getIndexOption('permalink'),
1207 4
            'form_fields' => $this->repository->getFormFields($item),
1208 4
            'baseUrl' => $baseUrl,
1209 4
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
1210 4
            'saveUrl' => $this->getModuleRoute($item->id, 'update'),
1211 4
            'editor' => $this->moduleHas('revisions') && $this->moduleHas('blocks') && !$this->disableEditor,
1212 4
            'blockPreviewUrl' => Route::has('admin.blocks.preview')? URL::route('admin.blocks.preview') : '#',
1213 4
            'revisions' => $this->moduleHas('revisions') ? $item->revisionsArray() : null,
1214 4
        ] + (Route::has($previewRouteName) ? [
1215 4
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', $item->id),
0 ignored issues
show
Bug introduced by
$item->id of type integer is incompatible with the type array expected by parameter $parameters of moduleRoute(). ( Ignorable by Annotation )

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

1215
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1216 4
        ] : [])
1217 4
             + (Route::has($restoreRouteName) ? [
1218 4
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1219 4
        ] : []);
1220
1221 4
        return array_replace_recursive($data, $this->formData($this->request));
1222
    }
1223
1224
    /**
1225
     * @param int $id
1226
     * @return array
1227
     */
1228 1
    protected function modalFormData($id)
1229
    {
1230 1
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1231 1
        $fields = $this->repository->getFormFields($item);
1232 1
        $data = [];
1233
1234 1
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1235 1
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1236 1
                $data['fields'][] = [
1237 1
                    'name' => $fieldName,
1238 1
                    'value' => $fieldValue,
1239
                ];
1240
            }
1241
1242 1
            $data['languages'] = $item->getActiveLanguages();
1243
1244 1
            unset($fields['translations']);
1245
        }
1246
1247 1
        foreach ($fields as $fieldName => $fieldValue) {
1248 1
            $data['fields'][] = [
1249 1
                'name' => $fieldName,
1250 1
                'value' => $fieldValue,
1251
            ];
1252
        }
1253
1254 1
        return array_replace_recursive($data, $this->formData($this->request));
1255
    }
1256
1257
    /**
1258
     * @param Request $request
1259
     * @return array
1260
     */
1261 4
    protected function formData($request)
1262
    {
1263 4
        return [];
1264
    }
1265
1266
    /**
1267
     * @param Request $item
1268
     * @return array
1269
     */
1270 1
    protected function previewData($item)
0 ignored issues
show
Unused Code introduced by
The parameter $item is not used and could be removed. ( Ignorable by Annotation )

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

1270
    protected function previewData(/** @scrutinizer ignore-unused */ $item)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1271
    {
1272 1
        return [];
1273
    }
1274
1275
    /**
1276
     * @return \A17\Twill\Http\Requests\Admin\Request
1277
     */
1278 21
    protected function validateFormRequest()
1279
    {
1280
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, $field) {
0 ignored issues
show
Unused Code introduced by
The parameter $field is not used and could be removed. ( Ignorable by Annotation )

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

1280
        $unauthorizedFields = Collection::make($this->fieldsPermissions)->filter(function ($permission, /** @scrutinizer ignore-unused */ $field) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

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