Passed
Pull Request — master (#551)
by
unknown
06:03
created

ModuleController::applyFiltersDefaultOptions()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 11.1035

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 7
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 15
ccs 3
cts 8
cp 0.375
crap 11.1035
rs 9.6111
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
     * Filters that are selected by default in the index view.
124
     *
125
     * Example: 'filter_key' => 'default_filter_value'
126
     *
127
     * @var array
128
     */
129
    protected $filtersDefaultOptions = [];
130
131
    /**
132
     * Default orders for the index view.
133
     *
134
     * @var array
135
     */
136
    protected $defaultOrders = [
137
        'created_at' => 'desc',
138
    ];
139
140
    /**
141
     * @var int
142
     */
143
    protected $perPage = 20;
144
145
    /**
146
     * Name of the index column to use as name column.
147
     *
148
     * @var string
149
     */
150
    protected $titleColumnKey = 'title';
151
152
    /**
153
     * Attribute to use as title in forms.
154
     *
155
     * @var string
156
     */
157
    protected $titleFormKey;
158
159
    /**
160
     * Feature field name if the controller is using the feature route (defaults to "featured").
161
     *
162
     * @var string
163
     */
164
    protected $featureField = 'featured';
165
166
    /**
167
     * Indicates if this module is edited through a parent module.
168
     *
169
     * @var bool
170
     */
171
    protected $submodule = false;
172
173
    /**
174
     * @var int|null
175
     */
176
    protected $submoduleParentId = null;
177
178
    /**
179
     * Can be used in child classes to disable the content editor (full screen block editor).
180
     *
181
     * @var bool
182
     */
183
    protected $disableEditor = false;
184
185
    /**
186
     * List of permissions keyed by a request field. Can be used to prevent unauthorized field updates.
187
     *
188
     * @var array
189
     */
190
    protected $fieldsPermissions = [];
191
192 40
    public function __construct(Application $app, Request $request)
193
    {
194 40
        parent::__construct();
195 40
        $this->app = $app;
196 40
        $this->request = $request;
197
198 40
        $this->setMiddlewarePermission();
199
200 40
        $this->modelName = $this->getModelName();
201 40
        $this->routePrefix = $this->getRoutePrefix();
202 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...
203 40
        $this->repository = $this->getRepository();
204 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...
205 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...
206
207
        /*
208
         * Default filters for the index view
209
         * By default, the search field will run a like query on the title field
210
         */
211 40
        if (!isset($this->defaultFilters)) {
212 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...
213 26
                'search' => ($this->moduleHas('translations') ? '' : '%') . $this->titleColumnKey,
214
            ];
215
        }
216
217
        /*
218
         * Apply any filters that are selected by default
219
         */
220 40
        $this->applyFiltersDefaultOptions();
221
222
        /*
223
         * Available columns of the index view
224
         */
225 40
        if (!isset($this->indexColumns)) {
226 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...
227 13
                $this->titleColumnKey => [
228 13
                    'title' => ucfirst($this->titleColumnKey),
229 13
                    'field' => $this->titleColumnKey,
230
                    'sort' => true,
231
                ],
232
            ];
233
        }
234
235
        /*
236
         * Available columns of the browser view
237
         */
238 40
        if (!isset($this->browserColumns)) {
239 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...
240 40
                $this->titleColumnKey => [
241 40
                    'title' => ucfirst($this->titleColumnKey),
242 40
                    'field' => $this->titleColumnKey,
243
                ],
244
            ];
245
        }
246 40
    }
247
248
    /**
249
     * @return void
250
     */
251 40
    protected function setMiddlewarePermission()
252
    {
253 40
        $this->middleware('can:list', ['only' => ['index', 'show']]);
254 40
        $this->middleware('can:edit', ['only' => ['store', 'edit', 'update']]);
255 40
        $this->middleware('can:publish', ['only' => ['publish', 'feature', 'bulkPublish', 'bulkFeature']]);
256 40
        $this->middleware('can:reorder', ['only' => ['reorder']]);
257 40
        $this->middleware('can:delete', ['only' => ['destroy', 'bulkDelete', 'restore', 'bulkRestore', 'forceDelete', 'bulkForceDelete', 'restoreRevision']]);
258 40
    }
259
260
    /**
261
     * @param int|null $parentModuleId
262
     * @return array|\Illuminate\View\View
263
     */
264 6
    public function index($parentModuleId = null)
265
    {
266 6
        $this->submodule = isset($parentModuleId);
267 6
        $this->submoduleParentId = $parentModuleId;
268
269 6
        $indexData = $this->getIndexData($this->submodule ? [
270
            $this->getParentModuleForeignKey() => $this->submoduleParentId,
271 6
        ] : []);
272
273 6
        if ($this->request->ajax()) {
274 3
            return $indexData + ['replaceUrl' => true];
275
        }
276
277 3
        if ($this->request->has('openCreate') && $this->request->get('openCreate')) {
278
            $indexData += ['openCreate' => true];
279
        }
280
281 3
        $view = Collection::make([
282 3
            "$this->viewPrefix.index",
283 3
            "twill::$this->moduleName.index",
284 3
            "twill::layouts.listing",
285
        ])->first(function ($view) {
286 3
            return View::exists($view);
287 3
        });
288
289 3
        return View::make($view, $indexData);
290
    }
291
292
    /**
293
     * @return \Illuminate\Http\JsonResponse
294
     */
295 2
    public function browser()
296
    {
297 2
        return Response::json($this->getBrowserData());
298
    }
299
300
    /**
301
     * @param int|null $parentModuleId
302
     * @return \Illuminate\Http\JsonResponse
303
     */
304 21
    public function store($parentModuleId = null)
305
    {
306 21
        $input = $this->validateFormRequest()->all();
307 21
        $optionalParent = $parentModuleId ? [$this->getParentModuleForeignKey() => $parentModuleId] : [];
308
309 21
        $item = $this->repository->create($input + $optionalParent);
310
311 21
        activity()->performedOn($item)->log('created');
312
313 21
        $this->fireEvent($input);
314
315 21
        Session::put($this->moduleName . '_retain', true);
316
317 21
        if ($this->getIndexOption('editInModal')) {
318 2
            return $this->respondWithSuccess('Content saved. All good!');
319
        }
320
321 19
        return $this->respondWithRedirect(moduleRoute(
322 19
            $this->moduleName,
323 19
            $this->routePrefix,
324 19
            'edit',
325 19
            array_filter([$parentModuleId]) + [Str::singular($this->moduleName) => $item->id]
326
        ));
327
    }
328
329
    /**
330
     * @param int|$id
331
     * @param int|null $submoduleId
332
     * @return \Illuminate\Http\RedirectResponse
333
     */
334 1
    public function show($id, $submoduleId = null)
335
    {
336 1
        if ($this->getIndexOption('editInModal')) {
337
            return Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
338
        }
339
340 1
        return $this->redirectToForm($submoduleId ?? $id);
341
    }
342
343
    /**
344
     * @param int $id
345
     * @param int|null $submoduleId
346
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
347
     */
348 5
    public function edit($id, $submoduleId = null)
349
    {
350 5
        $this->submodule = isset($submoduleId);
351 5
        $this->submoduleParentId = $id;
352
353 5
        if ($this->getIndexOption('editInModal')) {
354 2
            return $this->request->ajax()
355 1
            ? Response::json($this->modalFormData($submoduleId ?? $id))
356 2
            : Redirect::to(moduleRoute($this->moduleName, $this->routePrefix, 'index'));
357
        }
358
359 3
        $this->setBackLink();
360
361 3
        $view = Collection::make([
362 3
            "$this->viewPrefix.form",
363 3
            "twill::$this->moduleName.form",
364 3
            "twill::layouts.form",
365
        ])->first(function ($view) {
366 3
            return View::exists($view);
367 3
        });
368
369 3
        return View::make($view, $this->form($submoduleId ?? $id));
370
    }
371
372
    /**
373
     * @param int $id
374
     * @param int|null $submoduleId
375
     * @return \Illuminate\Http\JsonResponse
376
     */
377 7
    public function update($id, $submoduleId = null)
378
    {
379 7
        $this->submodule = isset($submoduleId);
380 7
        $this->submoduleParentId = $id;
381
382 7
        $item = $this->repository->getById($submoduleId ?? $id);
383 7
        $input = $this->request->all();
384
385 7
        if (isset($input['cmsSaveType']) && $input['cmsSaveType'] === 'cancel') {
386
            return $this->respondWithRedirect(moduleRoute(
387
                $this->moduleName,
388
                $this->routePrefix,
389
                'edit',
390
                [Str::singular($this->moduleName) => $id]
391
            ));
392
        } else {
393 7
            $formRequest = $this->validateFormRequest();
394
395 7
            $this->repository->update($submoduleId ?? $id, $formRequest->all());
396
397 7
            activity()->performedOn($item)->log('updated');
398
399 7
            $this->fireEvent();
400
401 7
            if (isset($input['cmsSaveType'])) {
402 7
                if (Str::endsWith($input['cmsSaveType'], '-close')) {
403
                    return $this->respondWithRedirect($this->getBackLink());
404 7
                } elseif (Str::endsWith($input['cmsSaveType'], '-new')) {
405
                    return $this->respondWithRedirect(moduleRoute(
406
                        $this->moduleName,
407
                        $this->routePrefix,
408
                        'index',
409
                        ['openCreate' => true]
410
                    ));
411 7
                } elseif ($input['cmsSaveType'] === 'restore') {
412
                    Session::flash('status', "Revision restored.");
413
414
                    return $this->respondWithRedirect(moduleRoute(
415
                        $this->moduleName,
416
                        $this->routePrefix,
417
                        'edit',
418
                        [Str::singular($this->moduleName) => $id]
419
                    ));
420
                }
421
            }
422
423 7
            if ($this->moduleHas('revisions')) {
424 6
                return Response::json([
425 6
                    'message' => 'Content saved. All good!',
426
                    'variant' => FlashLevel::SUCCESS,
427 6
                    'revisions' => $item->revisionsArray(),
428
                ]);
429
            }
430
431 1
            return $this->respondWithSuccess('Content saved. All good!');
432
        }
433
    }
434
435
    /**
436
     * @param int $id
437
     * @return \Illuminate\View\View
438
     */
439 1
    public function preview($id)
440
    {
441 1
        if ($this->request->has('revisionId')) {
442
            $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

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

445
            /** @scrutinizer ignore-call */ 
446
            $item = $this->repository->preview($id, $formRequest->all());
Loading history...
446
        }
447
448 1
        if ($this->request->has('activeLanguage')) {
449
            App::setLocale($this->request->get('activeLanguage'));
450
        }
451
452 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...
453
454 1
        return View::exists($previewView) ? View::make($previewView, array_replace([
455 1
            'item' => $item,
456 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

456
        ], $this->previewData(/** @scrutinizer ignore-type */ $item))) : View::make('twill::errors.preview', [
Loading history...
457 1
            'moduleName' => Str::singular($this->moduleName),
458
        ]);
459
    }
460
461
    /**
462
     * @param int $id
463
     * @return \Illuminate\View\View
464
     */
465 2
    public function restoreRevision($id)
466
    {
467 2
        if ($this->request->has('revisionId')) {
468 1
            $item = $this->repository->previewForRevision($id, $this->request->get('revisionId'));
469 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...
470 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...
471
        } else {
472 1
            throw new NotFoundHttpException();
473
        }
474
475 1
        $this->setBackLink();
476
477 1
        $view = Collection::make([
478 1
            "$this->viewPrefix.form",
479 1
            "twill::$this->moduleName.form",
480 1
            "twill::layouts.form",
481
        ])->first(function ($view) {
482 1
            return View::exists($view);
483 1
        });
484
485 1
        $revision = $item->revisions()->where('id', $this->request->get('revisionId'))->first();
486 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

486
        /** @scrutinizer ignore-call */ 
487
        $date = $revision->created_at->toDayDateTimeString();
Loading history...
487
488 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...
489
490 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

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

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

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

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

1280
            'previewUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'preview', /** @scrutinizer ignore-type */ $item->id),
Loading history...
1281 4
        ] : [])
1282 4
             + (Route::has($restoreRouteName) ? [
1283 4
            'restoreUrl' => moduleRoute($this->moduleName, $this->routePrefix, 'restoreRevision', $item->id),
1284 4
        ] : []);
1285
1286 4
        return array_replace_recursive($data, $this->formData($this->request));
1287
    }
1288
1289
    /**
1290
     * @param int $id
1291
     * @return array
1292
     */
1293 1
    protected function modalFormData($id)
1294
    {
1295 1
        $item = $this->repository->getById($id, $this->formWith, $this->formWithCount);
1296 1
        $fields = $this->repository->getFormFields($item);
1297 1
        $data = [];
1298
1299 1
        if ($this->moduleHas('translations') && isset($fields['translations'])) {
1300 1
            foreach ($fields['translations'] as $fieldName => $fieldValue) {
1301 1
                $data['fields'][] = [
1302 1
                    'name' => $fieldName,
1303 1
                    'value' => $fieldValue,
1304
                ];
1305
            }
1306
1307 1
            $data['languages'] = $item->getActiveLanguages();
1308
1309 1
            unset($fields['translations']);
1310
        }
1311
1312 1
        foreach ($fields as $fieldName => $fieldValue) {
1313 1
            $data['fields'][] = [
1314 1
                'name' => $fieldName,
1315 1
                'value' => $fieldValue,
1316
            ];
1317
        }
1318
1319 1
        return array_replace_recursive($data, $this->formData($this->request));
1320
    }
1321
1322
    /**
1323
     * @param Request $request
1324
     * @return array
1325
     */
1326 4
    protected function formData($request)
1327
    {
1328 4
        return [];
1329
    }
1330
1331
    /**
1332
     * @param Request $item
1333
     * @return array
1334
     */
1335 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

1335
    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...
1336
    {
1337 1
        return [];
1338
    }
1339
1340
    /**
1341
     * @return \A17\Twill\Http\Requests\Admin\Request
1342
     */
1343 21
    protected function validateFormRequest()
1344
    {
1345
        $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

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