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

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

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

431
            /** @scrutinizer ignore-call */ 
432
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
432
        }
433
434 1
        if ($this->request->has('activeLanguage')) {
435
            App::setLocale($this->request->get('activeLanguage'));
436
        }
437
438 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...
439
440 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
441 1
            'item' => $item,
442 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

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

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

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

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

883
    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...
884
    {
885 6
        $tableColumns = [];
886 6
        $visibleColumns = $this->request->get('columns') ?? false;
887
888 6
        if (isset(Arr::first($this->indexColumns)['thumb'])
889 6
            && Arr::first($this->indexColumns)['thumb']
890
        ) {
891 4
            array_push($tableColumns, [
892 4
                'name' => 'thumbnail',
893 4
                'label' => 'Thumbnail',
894 4
                'visible' => $visibleColumns ? in_array('thumbnail', $visibleColumns) : true,
895
                'optional' => true,
896
                'sortable' => false,
897
            ]);
898 4
            array_shift($this->indexColumns);
899
        }
900
901 6
        if ($this->getIndexOption('feature')) {
902
            array_push($tableColumns, [
903
                'name' => 'featured',
904
                'label' => 'Featured',
905
                'visible' => true,
906
                'optional' => false,
907
                'sortable' => false,
908
            ]);
909
        }
910
911 6
        if ($this->getIndexOption('publish')) {
912 6
            array_push($tableColumns, [
913 6
                'name' => 'published',
914
                'label' => 'Published',
915
                'visible' => true,
916
                'optional' => false,
917
                'sortable' => false,
918
            ]);
919
        }
920
921 6
        array_push($tableColumns, [
922 6
            'name' => 'name',
923 6
            'label' => $this->indexColumns[$this->titleColumnKey]['title'] ?? 'Name',
924
            'visible' => true,
925
            'optional' => false,
926 6
            'sortable' => $this->getIndexOption('reorder') ? false : ($this->indexColumns[$this->titleColumnKey]['sort'] ?? false),
927
        ]);
928
929 6
        unset($this->indexColumns[$this->titleColumnKey]);
930
931 6
        foreach ($this->indexColumns as $column) {
932 4
            $columnName = isset($column['relationship'])
933
            ? $column['relationship'] . ucfirst($column['field'])
934 4
            : (isset($column['nested']) ? $column['nested'] : $column['field']);
935
936 4
            array_push($tableColumns, [
937 4
                'name' => $columnName,
938 4
                'label' => $column['title'],
939 4
                'visible' => $visibleColumns ? in_array($columnName, $visibleColumns) : ($column['visible'] ?? true),
940 4
                'optional' => $column['optional'] ?? true,
941 4
                'sortable' => $this->getIndexOption('reorder') ? false : ($column['sort'] ?? false),
942 4
                'html' => $column['html'] ?? false,
943
            ]);
944
        }
945
946 6
        if ($this->moduleHas('translations')) {
947 5
            array_push($tableColumns, [
948 5
                'name' => 'languages',
949 5
                'label' => 'Languages',
950 5
                'visible' => $visibleColumns ? in_array('languages', $visibleColumns) : true,
951
                'optional' => true,
952
                'sortable' => false,
953
            ]);
954
        }
955
956 6
        return $tableColumns;
957
    }
958
959
    /**
960
     * @param \Illuminate\Database\Eloquent\Collection $items
961
     * @param array $scopes
962
     * @return array
963
     */
964 5
    protected function getIndexTableMainFilters($items, $scopes = [])
965
    {
966 5
        $statusFilters = [];
967
968 5
        $scope = ($this->submodule ? [
969
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
970 5
        ] : []) + $scopes;
971
972 5
        array_push($statusFilters, [
973 5
            'name' => 'All items',
974 5
            'slug' => 'all',
975 5
            'number' => $this->repository->getCountByStatusSlug('all', $scope),
976
        ]);
977
978 5
        if ($this->moduleHas('revisions') && $this->getIndexOption('create')) {
979 5
            array_push($statusFilters, [
980 5
                'name' => 'Mine',
981 5
                'slug' => 'mine',
982 5
                'number' => $this->repository->getCountByStatusSlug('mine', $scope),
983
            ]);
984
        }
985
986 5
        if ($this->getIndexOption('publish')) {
987 5
            array_push($statusFilters, [
988 5
                'name' => 'Published',
989 5
                'slug' => 'published',
990 5
                'number' => $this->repository->getCountByStatusSlug('published', $scope),
991
            ], [
992 5
                'name' => 'Draft',
993 5
                'slug' => 'draft',
994 5
                'number' => $this->repository->getCountByStatusSlug('draft', $scope),
995
            ]);
996
        }
997
998 5
        if ($this->getIndexOption('restore')) {
999 5
            array_push($statusFilters, [
1000 5
                'name' => 'Trash',
1001 5
                'slug' => 'trash',
1002 5
                'number' => $this->repository->getCountByStatusSlug('trash', $scope),
1003
            ]);
1004
        }
1005
1006 5
        return $statusFilters;
1007
    }
1008
1009
    /**
1010
     * @param string $moduleName
1011
     * @param string $routePrefix
1012
     * @return array
1013
     */
1014 6
    protected function getIndexUrls($moduleName, $routePrefix)
1015
    {
1016 6
        return Collection::make([
1017 6
            'store',
1018
            'publish',
1019
            'bulkPublish',
1020
            'restore',
1021
            'bulkRestore',
1022
            'forceDelete',
1023
            'bulkForceDelete',
1024
            'reorder',
1025
            'feature',
1026
            'bulkFeature',
1027
            'bulkDelete',
1028
        ])->mapWithKeys(function ($endpoint) use ($moduleName, $routePrefix) {
0 ignored issues
show
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...
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...
1029
            return [
1030 6
                $endpoint . 'Url' => $this->getIndexOption($endpoint) ? moduleRoute(
1031 6
                    $this->moduleName, $this->routePrefix, $endpoint,
1032 6
                    $this->submodule ? [$this->submoduleParentId] : []
1033
                ) : null,
1034
            ];
1035 6
        })->toArray();
1036
    }
1037
1038
    /**
1039
     * @param string $option
1040
     * @return bool
1041
     */
1042 29
    protected function getIndexOption($option)
1043
    {
1044
        return once(function () use ($option) {
1045
            $customOptionNamesMapping = [
1046 29
                'store' => 'create',
1047
            ];
1048
1049 29
            $option = array_key_exists($option, $customOptionNamesMapping) ? $customOptionNamesMapping[$option] : $option;
1050
1051
            $authorizableOptions = [
1052 29
                'create' => 'edit',
1053
                'edit' => 'edit',
1054
                'publish' => 'publish',
1055
                'feature' => 'feature',
1056
                'reorder' => 'reorder',
1057
                'delete' => 'delete',
1058
                'restore' => 'delete',
1059
                'forceDelete' => 'delete',
1060
                'bulkForceDelete' => 'delete',
1061
                'bulkPublish' => 'publish',
1062
                'bulkRestore' => 'delete',
1063
                'bulkFeature' => 'feature',
1064
                'bulkDelete' => 'delete',
1065
                'bulkEdit' => 'edit',
1066
                'editInModal' => 'edit',
1067
            ];
1068
1069 29
            $authorized = array_key_exists($option, $authorizableOptions) ? Auth::guard('twill_users')->user()->can($authorizableOptions[$option]) : true;
1070 29
            return ($this->indexOptions[$option] ?? $this->defaultIndexOptions[$option] ?? false) && $authorized;
1071 29
        });
1072
    }
1073
1074
    /**
1075
     * @param array $prependScope
1076
     * @return array
1077
     */
1078 2
    protected function getBrowserData($prependScope = [])
1079
    {
1080 2
        if ($this->request->has('except')) {
1081
            $prependScope['exceptIds'] = $this->request->get('except');
1082
        }
1083
1084 2
        $scopes = $this->filterScope($prependScope);
1085 2
        $items = $this->getBrowserItems($scopes);
1086 2
        $data = $this->getBrowserTableData($items);
1087
1088 2
        return array_replace_recursive(['data' => $data], $this->indexData($this->request));
1089
    }
1090
1091
    /**
1092
     * @param \Illuminate\Database\Eloquent\Collection $items
1093
     * @return array
1094
     */
1095 2
    protected function getBrowserTableData($items)
1096
    {
1097 2
        $withImage = $this->moduleHas('medias');
1098
1099
        return $items->map(function ($item) use ($withImage) {
1100
            $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...
1101 2
                return $this->getItemColumnData($item, $column);
1102 2
            })->toArray();
1103
1104 2
            $name = $columnsData[$this->titleColumnKey];
1105 2
            unset($columnsData[$this->titleColumnKey]);
1106
1107
            return [
1108 2
                'id' => $item->id,
1109 2
                'name' => $name,
1110 2
                'edit' => moduleRoute($this->moduleName, $this->routePrefix, 'edit', $item->id),
1111 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

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

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

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

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