Passed
Push — master ( c36252...33b7c5 )
by Quentin
04:57 queued 11s
created

ModuleController   F

Complexity

Total Complexity 202

Size/Duplication

Total Lines 1484
Duplicated Lines 0 %

Test Coverage

Coverage 83.14%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 635
c 5
b 0
f 0
dl 0
loc 1484
ccs 498
cts 599
cp 0.8314
rs 1.965
wmc 202

62 Methods

Rating   Name   Duplication   Size   Complexity  
A restore() 0 9 2
A setBackLink() 0 17 4
A bulkFeature() 0 12 3
A getBrowserData() 0 11 2
A indexItemData() 0 3 1
A bulkDelete() 0 8 2
A redirectToForm() 0 9 1
A preview() 0 19 4
A transformIndexItems() 0 3 1
A getBrowserTableData() 0 21 3
A indexData() 0 3 1
A getIndexOption() 0 27 4
A getIndexData() 0 33 4
A getBrowserItems() 0 3 1
A __construct() 0 46 5
A getIndexUrls() 0 20 3
A index() 0 26 5
C getIndexTableData() 0 48 16
A getParentModuleForeignKey() 0 3 1
B orderScope() 0 20 7
A store() 0 22 3
A getRepository() 0 3 1
A respondWithRedirect() 0 4 1
A getRoutePrefix() 0 8 2
B filterScope() 0 46 11
A edit() 0 22 3
A respondWithSuccess() 0 3 1
A tags() 0 8 1
A restoreRevision() 0 26 2
A destroy() 0 10 2
A bulkPublish() 0 18 4
A reorder() 0 9 3
A getModelName() 0 3 1
A setMiddlewarePermission() 0 7 1
A getModuleRoute() 0 7 2
A validateFormRequest() 0 11 1
A bulkRestore() 0 8 2
A getIndexItems() 0 8 1
A browser() 0 3 1
A fireEvent() 0 3 1
B update() 0 55 8
A modalFormData() 0 27 5
C getIndexTableColumns() 0 74 14
A publish() 0 24 5
A show() 0 7 2
A feature() 0 26 6
A moduleHas() 0 3 1
A getBackLink() 0 4 1
A respondWithError() 0 3 1
A formData() 0 3 1
C getItemColumnData() 0 43 15
A getNamespace() 0 3 1
A getBackLinkSessionKey() 0 3 2
B getIndexTableMainFilters() 0 43 6
B form() 0 33 8
A getViewPrefix() 0 3 1
A getPermalinkPrefix() 0 3 1
A respondWithJson() 0 5 1
A getModelTitle() 0 3 1
A previewData() 0 3 1
A getRequestFilters() 0 7 2
A getPermalinkBaseUrl() 0 7 5

How to fix   Complexity   

Complex Class

Complex classes like ModuleController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ModuleController, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

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

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

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

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

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

Loading history...
473
474 1
        return View::make($view, $this->form($id, $item));
0 ignored issues
show
Bug introduced by
It seems like $item can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $item of A17\Twill\Http\Controlle...oduleController::form() does only seem to accept A17\Twill\Models\Model|null, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

1269
    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...
1270
    {
1271 1
        return [];
1272
    }
1273
1274
    /**
1275
     * @return \A17\Twill\Http\Requests\Admin\Request
1276
     */
1277 21
    protected function validateFormRequest()
1278
    {
1279
        $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

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