Passed
Pull Request — master (#1285)
by Diego
04:30
created

PluginsResource::install()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 2
b 0
f 0
nc 2
nop 1
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 3.072
rs 10
1
<?php
2
3
namespace JeroenNoten\LaravelAdminLte\Console\PackageResources;
4
5
use Illuminate\Support\Facades\File;
6
use JeroenNoten\LaravelAdminLte\Helpers\CommandHelper;
7
8
class PluginsResource extends PackageResource
9
{
10
    /**
11
     * The available plugins data. A plugin can contain next data keys:
12
     * - name: The name of the plugin.
13
     * - source: The source of the plugin (relative to base source)
14
     * - target: The target of the plugin (relative to base target)
15
     * - resources: An array with resources data items.
16
     * - ignore: A list of file patterns to ignore.
17
     * - recursive: Whether to copy files recursively (default is true).
18
     *
19
     * When the target is not specified, the source will be used as the
20
     * relative path to the base target destination. A resource can contain the
21
     * same keys that are availables for a plugin.
22
     *
23
     * @var array
24
     */
25
    protected $plugins = [
26
        'bootstrap4DualListbox' => [
27
            'name' => 'Bootstrap4 Dual Listbox',
28
            'source' => 'bootstrap4-duallistbox',
29
        ],
30
        'bootstrapColorpicker' => [
31
            'name' => 'Bootstrap Colorpicker',
32
            'source' => 'bootstrap-colorpicker',
33
        ],
34
        'bootstrapSlider' => [
35
            'name' => 'Bootstrap Slider',
36
            'source' => 'bootstrap-slider',
37
        ],
38
        'bootstrapSwitch' => [
39
            'name' => 'Bootstrap Switch',
40
            'source' => 'bootstrap-switch',
41
        ],
42
        'bsCustomFileInput' => [
43
            'name' => 'bs-custom-file-input',
44
            'source' => 'bs-custom-file-input',
45
        ],
46
        'chartJs' => [
47
            'name' => 'Chart.js',
48
            'source' => 'chart.js',
49
        ],
50
        'datatables' => [
51
            'name' => 'Datatables',
52
            'resources' => [
53
                ['source' => 'datatables', 'target' => 'datatables/js'],
54
                ['source' => 'datatables-bs4', 'target' => 'datatables'],
55
            ],
56
        ],
57
        'datatablesPlugins' => [
58
            'name' => 'Datatables Plugins',
59
            'resources' => [
60
                ['source' => 'datatables-autofill', 'target' => 'datatables-plugins/autofill'],
61
                ['source' => 'datatables-buttons', 'target' => 'datatables-plugins/buttons'],
62
                ['source' => 'datatables-colreorder', 'target' => 'datatables-plugins/colreorder'],
63
                ['source' => 'datatables-fixedcolumns', 'target' => 'datatables-plugins/fixedcolumns'],
64
                ['source' => 'datatables-fixedheader', 'target' => 'datatables-plugins/fixedheader'],
65
                ['source' => 'datatables-keytable', 'target' => 'datatables-plugins/keytable'],
66
                ['source' => 'datatables-responsive', 'target' => 'datatables-plugins/responsive'],
67
                ['source' => 'datatables-rowgroup', 'target' => 'datatables-plugins/rowgroup'],
68
                ['source' => 'datatables-rowreorder', 'target' => 'datatables-plugins/rowreorder'],
69
                ['source' => 'datatables-scroller', 'target' => 'datatables-plugins/scroller'],
70
                ['source' => 'datatables-select', 'target' => 'datatables-plugins/select'],
71
                ['source' => 'pdfmake', 'target' => 'datatables-plugins/pdfmake'],
72
                ['source' => 'jszip', 'target' => 'datatables-plugins/jszip'],
73
            ],
74
        ],
75
        'daterangepicker' => [
76
            'name' => 'Date Range Picker',
77
            'resources' => [
78
                ['source' => 'daterangepicker'],
79
                ['source' => 'moment'],
80
            ],
81
        ],
82
        'ekkoLightbox' => [
83
            'name' => 'Ekko Lightbox',
84
            'source' => 'ekko-lightbox',
85
        ],
86
        'fastclick' => [
87
            'name' => 'FastClick',
88
            'source' => 'fastclick',
89
        ],
90
        'filterizr' => [
91
            'name' => 'Filterizr',
92
            'source' => 'filterizr',
93
            'ignore' => ['*.d.ts'],
94
            'recursive' => false,
95
        ],
96
        'flagIconCss' => [
97
            'name' => 'Flag Icon Css',
98
            'source' => 'flag-icon-css',
99
        ],
100
        'flot' => [
101
            'name' => 'Flot',
102
            'source' => 'flot',
103
        ],
104
        'fullcalendar' => [
105
            'name' => 'Fullcalendar',
106
            'source' => 'fullcalendar',
107
            'ignore' => ['*.d.ts', '*.json', '*.md'],
108
        ],
109
        'icheckBootstrap' => [
110
            'name' => 'iCheck Bootstrap',
111
            'source' => 'icheck-bootstrap',
112
            'ignore' => ['*.json', '*.md'],
113
        ],
114
        'inputmask' => [
115
            'name' => 'InputMask',
116
            'source' => 'inputmask',
117
        ],
118
        'ionRangslider' => [
119
            'name' => 'Ion.RangeSlider',
120
            'source' => 'ion-rangeslider',
121
            'ignore' => ['*.json', '*.md', '.editorconfig'],
122
        ],
123
        'jqueryKnob' => [
124
            'name' => 'jQuery Knob',
125
            'source' => 'jquery-knob',
126
        ],
127
        'jqueryMapael' => [
128
            'name' => 'jQuery Mapael',
129
            'resources' => [
130
                ['source' => 'jquery-mapael'],
131
                ['source' => 'raphael'],
132
                ['source' => 'jquery-mousewheel'],
133
            ],
134
            'ignore' => ['*.json', '*.md', '.editorconfig'],
135
        ],
136
        'jqueryUi' => [
137
            'name' => 'jQuery UI',
138
            'resources' => [
139
                ['source' => 'jquery-ui'],
140
                ['source' => 'jquery-ui/images'],
141
            ],
142
            'recursive' => false,
143
        ],
144
        'jqueryValidation' => [
145
            'name' => 'jQuery Validation',
146
            'source' => 'jquery-validation',
147
        ],
148
        'jqvmap' => [
149
            'name' => 'jQVMap',
150
            'source' => 'jqvmap',
151
        ],
152
        'jsgrid' => [
153
            'name' => 'jsGrid',
154
            'resources' => [
155
                ['source' => 'jsgrid'],
156
                ['source' => 'jsgrid/i18n'],
157
            ],
158
            'recursive' => false,
159
        ],
160
        'paceProgress' => [
161
            'name' => 'Pace Progress',
162
            'source' => 'pace-progress',
163
        ],
164
        'select2' => [
165
            'name' => 'Select 2 with Bootstrap 4 Theme',
166
            'resources' => [
167
                ['source' => 'select2'],
168
                ['source' => 'select2-bootstrap4-theme'],
169
            ],
170
            'ignore' => ['*.json', '*.md'],
171
        ],
172
        'sparklines' => [
173
            'name' => 'Sparklines',
174
            'source' => 'sparklines',
175
        ],
176
        'summernote' => [
177
            'name' => 'Summernote',
178
            'source' => 'summernote',
179
        ],
180
        'sweetalert2' => [
181
            'name' => 'Sweetalert 2 with Bootstrap 4 Theme',
182
            'resources' => [
183
                ['source' => 'sweetalert2'],
184
                ['source' => 'sweetalert2-theme-bootstrap-4'],
185
            ],
186
        ],
187
        'tempusdominusBootstrap4' => [
188
            'name' => 'Tempus Dominus for Bootstrap 4',
189
            'resources' => [
190
                ['source' => 'tempusdominus-bootstrap-4'],
191
                ['source' => 'moment'],
192
            ],
193
        ],
194
        'toastr' => [
195
            'name' => 'Toastr',
196
            'source' => 'toastr',
197
        ],
198
    ];
199
200
    /**
201
     * Create a new resource instance.
202
     *
203
     * @return void
204
     */
205 23
    public function __construct()
206
    {
207
        // Fill the basic resource data.
208
209 23
        $this->description = 'The set of extra plugins available with AdminLTE';
210 23
        $this->required = false;
211
212
        // Define the base source folder of the plugins.
213
214 23
        $this->source = base_path('vendor/almasaeed2010/adminlte/plugins');
215
216
        // Define the base target destination for the plugins.
217
218 23
        $this->target = public_path('vendor');
219
220
        // Fill the set of installation messages templates.
221
222 23
        $this->messages = [
223 23
            'install' => 'Do you want to plublish the :plugin plugin?',
224 23
            'overwrite' => 'The :plugin plugin was already published. Want to replace?',
225 23
            'remove' => 'Do you really want to remove the :plugin plugin?',
226 23
        ];
227
    }
228
229
    /**
230
     * Gets the plugins source data.
231
     *
232
     * @param  string  $pluginKey  A plugin key
233
     * @return array
234
     */
235 6
    public function getSourceData($pluginKey = null)
236
    {
237
        // Check if we need to get data of a specific AdminLTE plugin.
238
239 6
        if (! empty($pluginKey)) {
240 6
            return $this->plugins[$pluginKey] ?? [];
241
        }
242
243
        // Otherwise, return all the AdminLTE plugins data.
244
245 2
        return $this->plugins;
246
    }
247
248
    /**
249
     * Installs or publishes the specified plugin.
250
     *
251
     * @param  string  $pluginKey  A plugin key
252
     * @return bool
253
     */
254 6
    public function install($pluginKey = null)
255
    {
256 6
        if (isset($pluginKey) && isset($this->plugins[$pluginKey])) {
257 6
            $plugin = $this->preparePlugin($this->plugins[$pluginKey]);
258
259 6
            return $this->installPlugin($plugin);
260
        }
261
262
        return false;
263
    }
264
265
    /**
266
     * Uninstalls the specified plugin.
267
     *
268
     * @param  string  $pluginKey  A plugin key
269
     * @return bool
270
     */
271 6
    public function uninstall($pluginKey = null)
272
    {
273 6
        if (isset($pluginKey) && isset($this->plugins[$pluginKey])) {
274 6
            $plugin = $this->preparePlugin($this->plugins[$pluginKey]);
275
276 6
            return $this->uninstallPlugin($plugin);
277
        }
278
279
        return true;
280
    }
281
282
    /**
283
     * Checks whether a plugin already exists in the target location.
284
     *
285
     * @param  string  $pluginKey  A plugin key
286
     * @return bool
287
     */
288 5
    public function exists($pluginKey = null)
289
    {
290 5
        if (isset($pluginKey) && isset($this->plugins[$pluginKey])) {
291 4
            $plugin = $this->preparePlugin($this->plugins[$pluginKey]);
292
293 4
            return $this->pluginExists($plugin);
294
        }
295
296 1
        return false;
297
    }
298
299
    /**
300
     * Checks whether a plugin is correctly installed, i.e. if the source items
301
     * matches with the items available at the target location.
302
     *
303
     * @param  string  $pluginKey  A plugin key
304
     * @return bool
305
     */
306 7
    public function installed($pluginKey = null)
307
    {
308 7
        if (isset($pluginKey) && isset($this->plugins[$pluginKey])) {
309 6
            $plugin = $this->preparePlugin($this->plugins[$pluginKey]);
310
311 6
            return $this->pluginInstalled($plugin);
312
        }
313
314 1
        return false;
315
    }
316
317
    /**
318
     * Prepares a plugin with some sort of normalizations in its data.
319
     *
320
     * @param  array  $plugin  An array with the plugin data
321
     * @return array
322
     */
323 6
    protected function preparePlugin($plugin)
324
    {
325
        // Add source and target when not defined.
326
327 6
        $plugin['source'] = $plugin['source'] ?? '';
328 6
        $plugin['target'] = $plugin['target'] ?? $plugin['source'];
329
330
        // Add fully qualified paths and default values.
331
332 6
        $DS = DIRECTORY_SEPARATOR;
333 6
        $plugin['source'] = $this->source.$DS.$plugin['source'];
334 6
        $plugin['target'] = $this->target.$DS.$plugin['target'];
335 6
        $plugin['ignore'] = $plugin['ignore'] ?? [];
336 6
        $plugin['recursive'] = $plugin['recursive'] ?? true;
337
338
        // Add fully qualified paths and default values on the resources.
339
340 6
        if (isset($plugin['resources'])) {
341 3
            foreach ($plugin['resources'] as $key => $res) {
342 3
                $res['target'] = $res['target'] ?? $res['source'];
343 3
                $res['source'] = $plugin['source'].$DS.$res['source'];
344 3
                $res['target'] = $plugin['target'].$DS.$res['target'];
345 3
                $res['ignore'] = $res['ignore'] ?? $plugin['ignore'];
346 3
                $res['recursive'] = $res['recursive'] ?? $plugin['recursive'];
347 3
                $plugin['resources'][$key] = $res;
348
            }
349
        }
350
351
        // Return normalized plugin data.
352
353 6
        return $plugin;
354
    }
355
356
    /**
357
     * Installs the specified AdminLTE plugin.
358
     *
359
     * @param  array  $plugin  An array with the plugin data
360
     * @return bool
361
     */
362 6
    protected function installPlugin($plugin)
363
    {
364
        // Check if we need to export the entire plugin.
365
366 6
        if (! isset($plugin['resources'])) {
367 6
            return $this->publishResource($plugin);
368
        }
369
370
        // Otherwise, publish only the specified plugin resources.
371
372 2
        foreach ($plugin['resources'] as $res) {
373 2
            if (! $this->publishResource($res)) {
374
                return false;
375
            }
376
        }
377
378 2
        return true;
379
    }
380
381
    /**
382
     * Publishes the specified resource (usually a file or folder).
383
     *
384
     * @param  array  $res  An array with the resource data
385
     * @return bool
386
     */
387 6
    protected function publishResource($res)
388
    {
389
        // Check whether the resource is a file or a directory.
390
391 6
        if (File::isDirectory($res['source'])) {
392 6
            return CommandHelper::copyDirectory(
393 6
                $res['source'],
394 6
                $res['target'],
395 6
                $res['force'] ?? true,
396 6
                $res['recursive'] ?? true,
397 6
                $res['ignore'] ?? []
398 6
            );
399
        }
400
401
        File::ensureDirectoryExists(File::dirname($res['target']));
402
403
        return File::copy($res['source'], $res['target']);
404
    }
405
406
    /**
407
     * Checks whether the specified plugin already exists in the target
408
     * location.
409
     *
410
     * @param  array  $plugin  An array with the plugin data
411
     * @return bool
412
     */
413 4
    protected function pluginExists($plugin)
414
    {
415
        // When the plugin is not a resources list, just check if target exists.
416
417 4
        if (! isset($plugin['resources'])) {
418 4
            return File::exists($plugin['target']);
419
        }
420
421
        // Otherwise, check if any of the plugin resources already exists.
422
423 2
        foreach ($plugin['resources'] as $res) {
424 2
            if (File::exists($res['target'])) {
425 1
                return true;
426
            }
427
        }
428
429 2
        return false;
430
    }
431
432
    /**
433
     * Checks whether the specified plugin is correctly installed.
434
     *
435
     * @param  array  $plugin  An array with the plugin data
436
     * @return bool
437
     */
438 6
    protected function pluginInstalled($plugin)
439
    {
440
        // Check whether the plugin has resources or not.
441
442 6
        if (! isset($plugin['resources'])) {
443 6
            return $this->resourceInstalled($plugin);
444
        }
445
446 3
        foreach ($plugin['resources'] as $res) {
447 3
            if (! $this->resourceInstalled($res)) {
448 3
                return false;
449
            }
450
        }
451
452 2
        return true;
453
    }
454
455
    /**
456
     * Checks whether the specified resource is correctly installed.
457
     *
458
     * @param  array  $res  An array with the resource data
459
     * @return bool
460
     */
461 6
    protected function resourceInstalled($res)
462
    {
463
        // Check whether the resource is a file or a directory.
464
465 6
        if (File::isDirectory($res['source'])) {
466 6
            return (bool) CommandHelper::compareDirectories(
467 6
                $res['source'],
468 6
                $res['target'],
469 6
                $res['recursive'] ?? true,
470 6
                $res['ignore'] ?? []
471 6
            );
472
        }
473
474
        return CommandHelper::compareFiles($res['source'], $res['target']);
475
    }
476
477
    /**
478
     * Uninstalls the specified plugin.
479
     *
480
     * @param  array  $plugin  An array with the plugin data
481
     * @return bool
482
     */
483 6
    protected function uninstallPlugin($plugin)
484
    {
485
        // If the plugin doensn't have resources, remove the main target
486
        // location folder.
487
488 6
        if (! isset($plugin['resources'])) {
489 6
            return $this->uninstallResource($plugin);
490
        }
491
492
        // Otherwise, remove only the specified plugin resources.
493
494 2
        foreach ($plugin['resources'] as $res) {
495 2
            if (! $this->uninstallResource($res)) {
496
                return false;
497
            }
498
        }
499
500 2
        return true;
501
    }
502
503
    /**
504
     * Removes the specified resource (usually a folder).
505
     *
506
     * @param  array  $res  An array with the resource data
507
     * @return bool
508
     */
509 6
    protected function uninstallResource($res)
510
    {
511 6
        $target = $res['target'];
512
513
        // Uninstall the specified resource. Note the target location is always
514
        // a folder. When the target folder does not exists, we consider the
515
        // resource as uninstalled.
516
517 6
        if (File::isDirectory($target)) {
518 6
            return File::deleteDirectory($target);
519
        }
520
521 3
        return true;
522
    }
523
}
524