Passed
Pull Request — 1.2 (#405)
by
unknown
08:49
created

ModuleController::__construct()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 46
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 24
nc 18
nop 2
dl 0
loc 46
ccs 24
cts 24
cp 1
crap 6
rs 8.9137
c 0
b 0
f 0
1
<?php
2
3
namespace A17\Twill\Http\Controllers\Admin;
4
5
use A17\Twill\Helpers\FlashLevel;
6
use 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 19
            $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 19
                $this->titleColumnKey => [
214 19
                    'title' => $this->titleColumnKey === 'title' ? 'Title' : ucfirst($this->titleColumnKey),
215 19
                    '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 17
        activity()->performedOn($item)->log('created');
298
299 17
        $this->fireEvent($input);
300
301 17
        Session::put($this->moduleName . '_retain', true);
302
303 17
        if ($this->getIndexOption('editInModal')) {
304 2
            return $this->respondWithSuccess('Content saved. All good!');
305
        }
306
307 15
        return $this->respondWithRedirect(moduleRoute(
308 15
            $this->moduleName,
309 15
            $this->routePrefix,
310 15
            'edit',
311 15
            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 6
    public function update($id, $submoduleId = null)
364
    {
365 6
        $this->submodule = isset($submoduleId);
366 6
        $this->submoduleParentId = $id;
367
368 6
        $item = $this->repository->getById($submoduleId ?? $id);
369 6
        $input = $this->request->all();
370
371 6
        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 6
            $formRequest = $this->validateFormRequest();
380
381 6
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
382
383 6
            activity()->performedOn($item)->log('updated');
384
385 6
            $this->fireEvent();
386
387 6
            if (isset($input['cmsSaveType'])) {
388 6
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
389
                    return $this->respondWithRedirect($this->getBackLink());
390 6
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
391
                    return $this->respondWithRedirect(moduleRoute(
392
                        $this->moduleName,
393
                        $this->routePrefix,
394
                        'index',
395
                        ['openCreate' => true]
396
                    ));
397 6
                } 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 6
            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
            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
            'permalink' => $this->getIndexOption('permalink'),
721 6
            'bulkEdit' => $this->getIndexOption('bulkEdit'),
722 6
            'titleFormKey' => $this->titleFormKey ?? $this->titleColumnKey,
723 6
            'baseUrl' => $baseUrl,
724 6
            'permalinkPrefix' => $this->getPermalinkPrefix($baseUrl),
725
        ];
726
727 6
        return array_replace_recursive($data + $options, $this->indexData($this->request));
728
    }
729
730
    /**
731
     * @param Request $request
732
     * @return array
733
     */
734 5
    protected function indexData($request)
735
    {
736 5
        return [];
737
    }
738
739
    /**
740
     * @param array $scopes
741
     * @param bool $forcePagination
742
     * @return \Illuminate\Database\Eloquent\Collection
743
     */
744 12
    protected function getIndexItems($scopes = [], $forcePagination = false)
745
    {
746 12
        return $this->transformIndexItems($this->repository->get(
747 12
            $this->indexWith,
748
            $scopes,
749 12
            $this->orderScope(),
750 12
            $this->request->get('offset') ?? $this->perPage ?? 50,
751
            $forcePagination
752
        ));
753
    }
754
755
    /**
756
     * @param \Illuminate\Database\Eloquent\Collection $items
757
     * @return \Illuminate\Database\Eloquent\Collection
758
     */
759 10
    protected function transformIndexItems($items)
760
    {
761 10
        return $items;
762
    }
763
764
    /**
765
     * @param \Illuminate\Database\Eloquent\Collection $items
766
     * @return array
767
     */
768 6
    protected function getIndexTableData($items)
769
    {
770 6
        $translated = $this->moduleHas('translations');
771
        return $items->map(function ($item) use ($translated) {
772
            $columnsData = Collection::make($this->indexColumns)->mapWithKeys(function ($column) use ($item) {
773 3
                return $this->getItemColumnData($item, $column);
774 3
            })->toArray();
775
776 3
            $name = $columnsData[$this->titleColumnKey];
777
778 3
            if (empty($name)) {
779
                if ($this->moduleHas('translations')) {
780
                    $fallBackTranslation = $item->translations()->where('active', true)->first();
781
782
                    if (isset($fallBackTranslation->{$this->titleColumnKey})) {
783
                        $name = $fallBackTranslation->{$this->titleColumnKey};
784
                    }
785
                }
786
787
                $name = $name ?? ('Missing ' . $this->titleColumnKey);
788
            }
789
790 3
            unset($columnsData[$this->titleColumnKey]);
791
792 3
            $itemIsTrashed = method_exists($item, 'trashed') && $item->trashed();
793 3
            $itemCanDelete = $this->getIndexOption('delete') && ($item->canDelete ?? true);
794 3
            $canEdit = $this->getIndexOption('edit');
795
796 3
            return array_replace([
797 3
                'id' => $item->id,
798 3
                'name' => $name,
799 3
                'publish_start_date' => $item->publish_start_date,
800 3
                'publish_end_date' => $item->publish_end_date,
801 3
                'edit' => $canEdit ? $this->getModuleRoute($item->id, 'edit') : null,
802 3
                'delete' => $itemCanDelete ? $this->getModuleRoute($item->id, 'destroy') : null,
803 3
            ] + ($this->getIndexOption('editInModal') ? [
804 1
                'editInModal' => $this->getModuleRoute($item->id, 'edit'),
805 1
                'updateUrl' => $this->getModuleRoute($item->id, 'update'),
806 3
            ] : []) + ($this->getIndexOption('publish') && ($item->canPublish ?? true) ? [
807 3
                'published' => $item->published,
808 3
            ] : []) + ($this->getIndexOption('feature') && ($item->canFeature ?? true) ? [
809
                'featured' => $item->{$this->featureField},
810 3
            ] : []) + (($this->getIndexOption('restore') && $itemIsTrashed) ? [
811
                'deleted' => true,
812 3
            ] : []) + ($translated ? [
813 3
                'languages' => $item->getActiveLanguages(),
814 3
            ] : []) + $columnsData, $this->indexItemData($item));
815 6
        })->toArray();
816
    }
817
818
    /**
819
     * @param \A17\Twill\Models\Model $item
820
     * @return array
821
     */
822 2
    protected function indexItemData($item)
823
    {
824 2
        return [];
825
    }
826
827
    /**
828
     * @param \A17\Twill\Models\Model $item
829
     * @param array $column
830
     * @return array
831
     */
832 5
    protected function getItemColumnData($item, $column)
833
    {
834 5
        if (isset($column['thumb']) && $column['thumb']) {
835 2
            if (isset($column['present']) && $column['present']) {
836
                return [
837
                    'thumbnail' => $item->presentAdmin()->{$column['presenter']},
838
                ];
839
            } else {
840 2
                $variant = isset($column['variant']);
841 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...
842 2
                $crop = $variant ? $column['variant']['crop'] : head(array_keys(head($item->mediasParams)));
843 2
                $params = $variant && isset($column['variant']['params'])
844
                ? $column['variant']['params']
845 2
                : ['w' => 80, 'h' => 80, 'fit' => 'crop'];
846
847
                return [
848 2
                    'thumbnail' => $item->cmsImage($role, $crop, $params),
849
                ];
850
            }
851
        }
852
853 5
        if (isset($column['nested']) && $column['nested']) {
854
            $field = $column['nested'];
855
            $nestedCount = $item->{$column['nested']}->count();
856
            $value = '<a href="';
857
            $value .= moduleRoute("$this->moduleName.$field", $this->routePrefix, 'index', [$item->id]);
858
            $value .= '">' . $nestedCount . " " . (strtolower($nestedCount > 1
859
                ? Str::plural($column['title'])
860
                : Str::singular($column['title']))) . '</a>';
861
        } else {
862 5
            $field = $column['field'];
863 5
            $value = $item->$field;
864
        }
865
866 5
        if (isset($column['relationship'])) {
867
            $field = $column['relationship'] . ucfirst($column['field']);
868
            $value = Arr::get($item, "{$column['relationship']}.{$column['field']}");
869 5
        } elseif (isset($column['present']) && $column['present']) {
870
            $value = $item->presentAdmin()->{$column['field']};
871
        }
872
873
        return [
874 5
            "$field" => $value,
875
        ];
876
    }
877
878
    /**
879
     * @param \Illuminate\Database\Eloquent\Collection $items
880
     * @return array
881
     */
882 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

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

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

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

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

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