Completed
Push — master ( 6d41ca...2bcfec )
by
unknown
07:51 queued 10s
created

AdminLtePluginCommand::removePlugins()   B

Complexity

Conditions 10
Paths 27

Size

Total Lines 44

Duplication

Lines 5
Ratio 11.36 %

Importance

Changes 0
Metric Value
dl 5
loc 44
rs 7.6666
c 0
b 0
f 0
cc 10
nc 27
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace JeroenNoten\LaravelAdminLte\Console;
4
5
use Illuminate\Console\Command;
6
use JeroenNoten\LaravelAdminLte\Http\Helpers\CommandHelper;
7
8
class AdminLtePluginCommand extends Command
9
{
10
    protected $signature = 'adminlte:plugins '.
11
        '{operation? : Operation command, Available commands; list, install, update & remove}'.
12
        '{--plugin=* : Plugin Key}'.
13
        '{--interactive : The installation will guide you through the process}';
14
15
    protected $description = 'Manages additional plugin files for AdminLTE';
16
17
    protected $plugins = [
18
        'bootstrapColorpicker' => [
19
            'name' => 'Bootstrap Colorpicker',
20
            'package_path' => 'bootstrap-colorpicker',
21
            'assets_path' => 'bootstrap-colorpicker',
22
        ],
23
        'bootstrapSlider' => [
24
            'name' => 'Bootstrap Slider',
25
            'package_path' => 'bootstrap-slider',
26
            'assets_path' => 'bootstrap-slider',
27
        ],
28
        'bootstrap4Duallistbox' => [
29
            'name' => 'Bootstrap4 Duallistbox',
30
            'package_path' => 'bootstrap4-duallistbox',
31
            'assets_path' => 'bootstrap4-duallistbox',
32
        ],
33
        'chartJs' => [
34
            'name' => 'Chart.js',
35
            'package_path' => 'chart.js',
36
            'assets_path' => 'chart.js',
37
        ],
38
        'datatables' => [
39
            'name' => 'Datatables',
40
            'package_path' => [
41
                'datatables',
42
                'datatables-bs4',
43
            ],
44
            'assets_path' => [
45
                'datatables/js',
46
                'datatables',
47
            ],
48
        ],
49
        'datatablesPlugins' => [
50
            'name' => 'Datatables Plugins',
51
            'package_path' => [
52
                'datatables-autofill',
53
                'datatables-buttons',
54
                'datatables-colreorder',
55
                'datatables-fixedcolumns',
56
                'datatables-fixedheader',
57
                'datatables-keytable',
58
                'datatables-responsive',
59
                'datatables-rowgroup',
60
                'datatables-rowreorder',
61
                'datatables-scroller',
62
                'datatables-select',
63
                'pdfmake',
64
                'jszip',
65
            ],
66
            'assets_path' => [
67
                'datatables-plugins/autofill',
68
                'datatables-plugins/buttons',
69
                'datatables-plugins/colreorder',
70
                'datatables-plugins/fixedcolumns',
71
                'datatables-plugins/fixedheader',
72
                'datatables-plugins/keytable',
73
                'datatables-plugins/responsive',
74
                'datatables-plugins/rowgroup',
75
                'datatables-plugins/rowreorder',
76
                'datatables-plugins/scroller',
77
                'datatables-plugins/select',
78
                'datatables-plugins/pdfmake',
79
                'datatables-plugins/jszip',
80
            ],
81
        ],
82
        'daterangepicker' => [
83
            'name' => 'DateRangePicker',
84
            'package_path' => [
85
                'daterangepicker',
86
                'moment',
87
            ],
88
            'assets_path' => [
89
                'daterangepicker',
90
                'moment',
91
            ],
92
        ],
93
        'ekkoLightbox' => [
94
            'name' => 'Ekko Lightbox',
95
            'package_path' => 'ekko-lightbox',
96
            'assets_path' => 'ekko-lightbox',
97
        ],
98
        'fastclick' => [
99
            'name' => 'Fastclick',
100
            'package_path' => 'fastclick',
101
            'assets_path' => 'fastclick',
102
        ],
103
        'filterizr' => [
104
            'name' => 'Filterizr',
105
            'package_path' => 'filterizr',
106
            'assets_path' => 'filterizr',
107
            'ignore_ending' => '*.d.ts',
108
            'recursive' => false,
109
        ],
110
        'flagIconCss' => [
111
            'name' => 'Flag Icon Css',
112
            'package_path' => 'flag-icon-css',
113
            'assets_path' => 'flag-icon-css',
114
        ],
115
        'flot' => [
116
            'name' => 'Flot',
117
            'package_path' => 'flot',
118
            'assets_path' => 'flot',
119
        ],
120
        'fullcalendar' => [
121
            'name' => 'Fullcalendar',
122
            'package_path' => 'fullcalendar',
123
            'assets_path' => 'fullcalendar',
124
            'ignore_ending' => [
125
                '*.d.ts', '*.json', '*.md',
126
            ],
127
        ],
128
        'fullcalendarPlugins' => [
129
            'name' => 'Fullcalendar Plugins',
130
            'package_path' => [
131
                'fullcalendar-bootstrap',
132
                'fullcalendar-daygrid',
133
                'fullcalendar-interaction',
134
                'fullcalendar-timegrid',
135
            ],
136
            'assets_path' => [
137
                'fullcalendar-plugins/bootstrap',
138
                'fullcalendar-plugins/daygrid',
139
                'fullcalendar-plugins/interaction',
140
                'fullcalendar-plugins/timegrid',
141
            ],
142
            'ignore_ending' => [
143
                '*.d.ts', '*.json', '*.md',
144
            ],
145
        ],
146
        'icheckBootstrap' => [
147
            'name' => 'iCheck Bootstrap',
148
            'package_path' => 'icheck-bootstrap',
149
            'assets_path' => 'icheck-bootstrap',
150
            'ignore_ending' => [
151
                '*.json', '*.md',
152
            ],
153
        ],
154
        'inputmask' => [
155
            'name' => 'InputMask',
156
            'package_path' => 'inputmask',
157
            'assets_path' => 'inputmask',
158
        ],
159
        'ionRangslider' => [
160
            'name' => 'ion RangeSlider',
161
            'package_path' => 'ion-rangeslider',
162
            'assets_path' => 'ion-rangeslider',
163
            'ignore_ending' => [
164
                '*.json', '*.md', '.editorconfig',
165
            ],
166
        ],
167
        'jqueryKnob' => [
168
            'name' => 'jQuery Knob',
169
            'package_path' => 'jquery-knob',
170
            'assets_path' => 'jquery-knob',
171
        ],
172
        'jqueryMapael' => [
173
            'name' => 'jQuery Mapael',
174
            'package_path' => [
175
                'jquery-mapael',
176
                'raphael',
177
                'jquery-mousewheel',
178
            ],
179
            'assets_path' => [
180
                'jquery-mapael',
181
                'raphael',
182
                'jquery-mousewheel',
183
            ],
184
            'ignore_ending' => [
185
                '*.json', '*.md', '.editorconfig',
186
            ],
187
        ],
188
        'jqueryUi' => [
189
            'name' => 'jQuery UI',
190
            'package_path' => [
191
                'jquery-ui',
192
                'jquery-ui/images',
193
            ],
194
            'assets_path' => [
195
                'jquery-ui',
196
                'jquery-ui/images',
197
            ],
198
            'recursive' => false,
199
            'ignore_ending' => [
200
                '*.json', '*.md', '*.html', '.editorconfig',
201
            ],
202
        ],
203
        'jqvmap' => [
204
            'name' => 'jQVMap',
205
            'package_path' => 'jqvmap',
206
            'assets_path' => 'jqvmap',
207
        ],
208
        'jsgrid' => [
209
            'name' => 'jsGrid',
210
            'package_path' => [
211
                'jsgrid',
212
                'jsgrid/i18n',
213
            ],
214
            'assets_path' => [
215
                'jsgrid',
216
                'jsgrid/i18n',
217
            ],
218
            'recursive' => false,
219
        ],
220
        'paceProgress' => [
221
            'name' => 'Pace Progress',
222
            'package_path' => 'pace-progress',
223
            'assets_path' => 'pace-progress',
224
        ],
225
        'select2' => [
226
            'name' => 'Select 2 with Bootstrap 4 Theme',
227
            'package_path' => [
228
                'select2',
229
                'select2-bootstrap4-theme',
230
            ],
231
            'assets_path' => [
232
                'select2',
233
                'select2-bootstrap4-theme',
234
            ],
235
            'ignore_ending' => [
236
                '*.json', '*.md',
237
            ],
238
        ],
239
        'sparklines' => [
240
            'name' => 'Sparklines',
241
            'package_path' => 'sparklines',
242
            'assets_path' => 'sparklines',
243
        ],
244
        'summernote' => [
245
            'name' => 'Summernote',
246
            'package_path' => 'summernote',
247
            'assets_path' => 'summernote',
248
        ],
249
        'sweetalert2' => [
250
            'name' => 'Sweetalert 2 with Bootstrap 4 Theme',
251
            'package_path' => [
252
                'sweetalert2',
253
                'sweetalert2-theme-bootstrap-4',
254
            ],
255
            'assets_path' => [
256
                'sweetalert2',
257
                'sweetalert2-theme-bootstrap-4',
258
            ],
259
        ],
260
        'tempusdominusBootstrap4' => [
261
            'name' => 'Tempusdominus Bootstrap 4',
262
            'package_path' => 'tempusdominus-bootstrap-4',
263
            'assets_path' => 'tempusdominus-bootstrap-4',
264
        ],
265
        'toastr' => [
266
            'name' => 'Toastr',
267
            'package_path' => 'toastr',
268
            'assets_path' => 'toastr',
269
        ],
270
    ];
271
272
    protected $assets_path = 'vendor/';
273
274
    protected $package_path = 'vendor/almasaeed2010/adminlte/';
275
276
    /**
277
     * Execute the console command.
278
     *
279
     * @return void
280
     */
281
    public function handle()
282
    {
283
        $operation = $this->arguments()['operation'];
284
285
        if (! $operation) {
286
            $operation = 'list';
287
        }
288
289
        switch ($operation) {
290
        case 'install':
291
            $this->copyPlugins();
292
            $this->info('AdminLTE Plugin Install complete.');
293
294
            break;
295
296
        case 'update':
297
            $this->copyPlugins(true);
298
            $this->info('AdminLTE Plugin Update complete.');
299
300
            break;
301
302
        case 'remove':
303
            $this->removePlugins();
304
            $this->info('AdminLTE Plugin Remove complete.');
305
306
            break;
307
308
        default:
309
            if ($operation != 'list' && $operation != '') {
310
                $this->error('Invalid operation!');
311
            } else {
312
                $this->listPlugins();
313
            }
314
315
            break;
316
        }
317
    }
318
319
    /**
320
     * List Plugins.
321
     *
322
     * @return void
323
     */
324
    protected function listPlugins()
325
    {
326
        $headers = ['Plugin Name', 'Plugin Key', 'Status'];
327
        $plugins = [];
328
329
        $bar = $this->output->createProgressBar(count($this->plugins));
330
331
        $this->line('Checking Plugins');
332
333
        $bar->start();
334
335
        foreach ($this->plugins as $plugin_key => $plugin) {
336
            $plugins[] = [$plugin['name'], $plugin_key, $this->checkPlugin($plugin_key)];
337
            $bar->advance();
338
        }
339
340
        $bar->finish();
341
342
        $this->line('');
343
        $this->line('Plugins Checked');
344
345
        $this->table($headers, $plugins);
346
    }
347
348
    /**
349
     * Check Plugin.
350
     *
351
     * @return void
352
     */
353
    protected function checkPlugin($plugin_key)
354
    {
355
        $plugin_exist = true;
356
        $plugin_missmatch = false;
357
        $plugin_child_exist = true;
358
        $plugin_child_missmatch = false;
359
        $plugin_public_path = public_path($this->assets_path);
360
        $plugin_base_path = base_path($this->package_path.'plugins/');
361
        $plugin_package_path = $this->plugins[$plugin_key]['package_path'];
362
        $plugin_assets_path = $this->plugins[$plugin_key]['assets_path'];
363
        $plugin_ignore = $this->plugins[$plugin_key]['ignore'] ?? [];
364
        $plugin_ignore_ending = $this->plugins[$plugin_key]['ignore_ending'] ?? [];
365
        $plugin_recursive = $this->plugins[$plugin_key]['recursive'] ?? true;
366
367
        if (is_array($plugin_assets_path)) {
368
            foreach ($plugin_assets_path as $key => $assets_path) {
369
                if (! file_exists($plugin_public_path.$assets_path)) {
370
                    $plugin_exist = false;
371
                    $plugin_child_exist = false;
372 View Code Duplication
                } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
373
                    $compare = CommandHelper::compareDirectories($plugin_base_path.$plugin_package_path[$key], $plugin_public_path.$assets_path, '', $plugin_ignore, $plugin_ignore_ending, $plugin_recursive);
374
375
                    if (! $plugin_child_missmatch && $compare) {
376
                        $plugin_child_missmatch = false;
377
                    } else {
378
                        $plugin_child_missmatch = true;
379
                    }
380
                }
381
            }
382 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383
            if (! file_exists($plugin_public_path.$plugin_assets_path)) {
384
                $plugin_exist = false;
385
            } else {
386
                if (! $compare = CommandHelper::compareDirectories($plugin_base_path.$plugin_package_path, $plugin_public_path.$plugin_assets_path, '', $plugin_ignore, $plugin_ignore_ending, $plugin_recursive)) {
387
                    $plugin_missmatch = true;
388
                }
389
            }
390
        }
391
392
        if ($plugin_exist && $plugin_child_exist && (! $plugin_missmatch && ! $plugin_child_missmatch)) {
393
            return 'Installed';
394
        } elseif ($plugin_exist && (($plugin_missmatch || $plugin_child_missmatch) || ! $plugin_child_exist)) {
395
            return 'Update Available';
396
        } elseif (! $plugin_exist) {
397
            return 'Not Installed';
398
        }
399
    }
400
401
    /**
402
     * Copy all Plugin Assets to Public Directory.
403
     */
404
    protected function copyPlugins($force = null)
405
    {
406
        if (! $plugins = $this->option('plugin')) {
407
            $plugins = $this->plugins;
408
        }
409
410
        $bar = $this->output->createProgressBar(count($plugins));
411
412
        $bar->start();
413
414
        if ($this->option('interactive')) {
415
            if (! $this->confirm('Install the plugin package assets?')) {
416
                return;
417
            }
418
        }
419
420
        foreach ($plugins as $plugin_key => $plugin) {
0 ignored issues
show
Bug introduced by
The expression $plugins of type array|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
421
            if (is_string($plugin)) {
422
                $plugin_key = $plugin;
423
            }
424
425
            if (! isset($this->plugins[$plugin_key])) {
426
                $this->error('Plugin Key not found: '.$plugin_key.'.');
427
                continue;
428
            }
429
430
            $plugin_keys[] = $plugin_key;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$plugin_keys was never initialized. Although not strictly required by PHP, it is generally a good practice to add $plugin_keys = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
431
            $plugin = $this->plugins[$plugin_key];
432
433 View Code Duplication
            if ($this->option('interactive')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
                if (! $this->confirm('Install the '.$plugin['name'].' assets?')) {
435
                    continue;
436
                }
437
            }
438 View Code Duplication
            if (is_array($plugin['package_path'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
439
                foreach ($plugin['package_path'] as $key => $plugin_package_path) {
440
                    $plugin_assets_path = $plugin['assets_path'][$key];
441
                    CommandHelper::directoryCopy(base_path($this->package_path).'plugins/'.$plugin_package_path, public_path($this->assets_path).$plugin_assets_path, $force, ($plugin['recursive'] ?? true), ($plugin['ignore'] ?? []), ($plugin['ignore_ending'] ?? null));
442
                }
443
            } else {
444
                CommandHelper::directoryCopy(base_path($this->package_path).'plugins/'.$plugin['package_path'], public_path($this->assets_path).$plugin['assets_path'], $force, ($plugin['recursive'] ?? true), ($plugin['ignore'] ?? []), ($plugin['ignore_ending'] ?? null));
445
            }
446
447
            $bar->advance();
448
        }
449
450
        $bar->finish();
451
        $this->line('');
452
    }
453
454
    /**
455
     * Removes all Plugin Assets to Public Directory.
456
     */
457
    protected function removePlugins()
458
    {
459
        if (! $this->confirm('Do you really want to remove the plugin package assets?')) {
460
            return;
461
        }
462
463
        if (! $plugins = $this->option('plugin')) {
464
            $plugins = $this->plugins;
465
        }
466
467
        $plugin_keys = [];
468
469
        foreach ($plugins as $plugin_key => $plugin) {
0 ignored issues
show
Bug introduced by
The expression $plugins of type array|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
470
            if (is_string($plugin)) {
471
                $plugin_key = $plugin;
472
            }
473
474
            if (! isset($this->plugins[$plugin_key])) {
475
                $this->error('Plugin Key not found: '.$plugin_key.'.');
476
                continue;
477
            }
478
479
            $plugin_keys[] = $plugin_key;
480
            $plugin = $this->plugins[$plugin_key];
481
482 View Code Duplication
            if ($this->option('interactive')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
483
                if (! $this->confirm('Remove the '.$plugin['name'].' assets?')) {
484
                    continue;
485
                }
486
            }
487
            if (is_array($plugin['package_path'])) {
488
                foreach ($plugin['package_path'] as $key => $plugin_package_path) {
489
                    $plugin_assets_path = $plugin['assets_path'][$key];
490
                    $plugin_path = public_path($this->assets_path).$plugin_assets_path;
491
                    CommandHelper::removeDirectory($plugin_path);
492
                }
493
            } else {
494
                $plugin_path = public_path($this->assets_path).$plugin['assets_path'];
495
                CommandHelper::removeDirectory($plugin_path);
496
            }
497
        }
498
499
        $this->info('Plugins removed: '.implode(', ', $plugin_keys).'.');
500
    }
501
}
502