Passed
Push — master ( 8bd85a...756b0e )
by Josh
02:22
created

MODXPackages::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 7
rs 10
1
<?php
2
3
namespace LCI\Blend\Transport;
4
5
use LCI\Blend\Exception\TransportException;
6
use LCI\Blend\Exception\TransportNotFoundException;
7
use LCI\MODX\Console\Helpers\UserInteractionHandler;
8
use modTransportPackage;
0 ignored issues
show
Bug introduced by
The type modTransportPackage was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use modTransportProvider;
0 ignored issues
show
Bug introduced by
The type modTransportProvider was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use modX;
0 ignored issues
show
Bug introduced by
The type modX was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
11
use xPDO;
0 ignored issues
show
Bug introduced by
The type xPDO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use xPDOTransport;
0 ignored issues
show
Bug introduced by
The type xPDOTransport was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
14
class MODXPackages
15
{
16
    /** @var string  */
17
    protected $date_format = '';
18
19
    /** @var \modX  */
20
    protected $modx;
21
22
    /** @var array $providerCache */
23
    protected $providerCache = array();
24
25
    protected $provider_map = [];
26
27
    /** @var int $updates_cache_expire */
28
    protected $updates_cache_expire = 300;
29
30
    /** @var array  */
31
    protected $possible_package_signatures = [];
32
33
    /** @var \LCI\MODX\Console\Helpers\UserInteractionHandler */
34
    protected $userInteractionHandler;
35
36
    /**
37
     * MODXPackages constructor.
38
     * @param modX $modx
39
     * @param UserInteractionHandler $userInteractionHandler
40
     */
41
    public function __construct(modX $modx, UserInteractionHandler $userInteractionHandler)
42
    {
43
        $this->modx = $modx;
44
45
        $this->userInteractionHandler = $userInteractionHandler;
46
47
        $this->date_format = $this->modx->getOption('manager_date_format') .', '. $this->modx->getOption('manager_time_format');
48
    }
49
50
    // Code ideas for MODX packages from: core/model/modx/processors/workspace/packages/getlist.class.php and
51
    // https://github.com/modmore/SiteDashClient/blob/master/core/components/sitedashclient/src/Package/Update.php
52
53
    /**
54
     * @param int $limit
55
     * @param int $start
56
     * @param string $search
57
     * @return array
58
     */
59
    public function getList($limit=25, $start=0, $search='') {
60
        $data = [
61
            'limit' => $limit,
62
            'packages' => [],
63
            'start' => $start,
64
            'total' => 0
65
        ];
66
67
        /** @var array $package_list */
68
        $package_list = $this->modx->call(
69
            'transport.modTransportPackage',
70
            'listPackages',
71
            [
72
                &$this->modx,
73
                1,
74
                $limit > 0 ? $limit : 0,
75
                $start,
76
                $search
77
            ]
78
        );
79
80
81
        if ($package_list > 0) {
82
            /** @var modTransportPackage $package */
83
            foreach ($package_list['collection'] as $package) {
84
                if ($package->get('installed') == '0000-00-00 00:00:00') {
85
                    $package->set('installed', null);
86
                }
87
88
                $package_array = $package->toArray();
89
90
                $package_array['name'] = $package_array['package_name'];
91
92
                $version_info = $this->getVersionInfo($package_array['signature']);
93
                $package_array['version'] = $version_info['version'];
94
                $package_array['release'] = $version_info['release'];
95
96
                $package_array = $this->formatDates($package_array);
97
                $package_array['updates'] = $this->getAvailableUpdateInfo($package);
98
99
                $data['packages'][] = $package_array;
100
            }
101
        }
102
103
        $data['total'] = $package_list['total'];
104
        return $data;
105
    }
106
107
    /**
108
     * This is only needed if TransportNotFoundException is caught
109
     * @return array
110
     */
111
    public function getPossiblePackageSignatures(): array
112
    {
113
        return $this->possible_package_signatures;
114
    }
115
116
    /**
117
     * @param string $signature
118
     * @param int $preexisting_mode, see xPDOTransport
119
     *  xPDOTransport::PRESERVE_PREEXISTING = 0;
120
    xPDOTransport::REMOVE_PREEXISTING = 1;
121
    xPDOTransport::RESTORE_PREEXISTING = 2;
122
     * @return bool
123
     * @throws TransportException
124
     */
125
    public function unInstallPackage($signature, $preexisting_mode=xPDOTransport::REMOVE_PREEXISTING)
126
    {
127
        $package = $this->modx->getObject('transport.modTransportPackage', [
128
            'signature' => $signature,
129
        ]);
130
131
        if ($package instanceof \modTransportPackage) {
132
            /* uninstall package */
133
            $options = array(
134
                xPDOTransport::PREEXISTING_MODE => $preexisting_mode,
135
            );
136
137
            if (!$success = $package->uninstall($options)) {
138
                throw new TransportException('Error Package did not uninstall.');
139
            }
140
141
        } else {
142
            throw new TransportException('Package does not seem to be installed; can only remove packages that are installed.');
143
        }
144
145
        return $success;
146
    }
147
148
    /**
149
     * @param string $signature - ex: ace-1.8.0-pl
150
     * @param bool $latest_version
151
     * @param string $provider_name
152
     * @return bool
153
     * @throws TransportException
154
     * @throws TransportNotFoundException
155
     */
156
    public function requirePackage($signature, $latest_version=true, $provider_name='modx.com')
157
    {
158
        $package = $this->modx->getObject('transport.modTransportPackage', [
159
            'signature' => $signature,
160
        ]);
161
162
        $type = 'install';
163
164
        if ($package instanceof \modTransportPackage) {
165
            // get the latest installed
166
            $query = $this->modx->newQuery('transport.modTransportPackage');
167
            $query->where(['package_name' => $package->get('package_name')]);
168
            $query->sortby('version_major', 'DESC');
169
            $query->sortby('version_minor', 'DESC');
170
            $query->sortby('version_patch', 'DESC');
171
            $query->sortby('release_index', 'ASC');
172
            $query->limit(1);
173
174
            /** @var modTransportPackage $package */
175
            $package = $this->modx->getObject('transport.modTransportPackage', $query);
176
177
            $signature = $package->get('signature');
178
            //return true;
179
            $provider = $this->getPackageProvider($package);
180
            $type = 'update';
181
182
        } else {
183
            $provider = $this->getProvider($provider_name);
184
        }
185
186
        $transfer_options = [];
187
188
        // this only returns the
189
        $options = $this->getPackageLatestVersions($provider, $signature);
0 ignored issues
show
Bug introduced by
It seems like $provider can also be of type boolean; however, parameter $provider of LCI\Blend\Transport\MODX...PackageLatestVersions() does only seem to accept modTransportProvider, 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

189
        $options = $this->getPackageLatestVersions(/** @scrutinizer ignore-type */ $provider, $signature);
Loading history...
190
191
        if (isset($options[$signature])) {
192
            $transfer_options['location'] = $options[$signature]['location'];
193
        }
194
195
        if ($latest_version && count($options) > 0) {
196
            $opt = reset($options);
197
198
            $signature = $opt['signature'];
199
            $transfer_options['location'] = $opt['location'];
200
201
        } elseif ($type == 'update') {
202
            $this->userInteractionHandler->tellUser('Extra '.$signature.' is already installed, skipping!', userInteractionHandler::MASSAGE_ERROR);
203
            return true;
204
        }
205
206
        $package = $this->downloadPackageFiles($signature, $provider, $latest_version);
0 ignored issues
show
Bug introduced by
It seems like $provider can also be of type boolean; however, parameter $provider of LCI\Blend\Transport\MODX...:downloadPackageFiles() does only seem to accept modTransportProvider, 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

206
        $package = $this->downloadPackageFiles($signature, /** @scrutinizer ignore-type */ $provider, $latest_version);
Loading history...
207
208
        return $this->runPackageInstallUpdate($package);
0 ignored issues
show
Bug introduced by
It seems like $package can also be of type true; however, parameter $package of LCI\Blend\Transport\MODX...nPackageInstallUpdate() does only seem to accept modTransportPackage, 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

208
        return $this->runPackageInstallUpdate(/** @scrutinizer ignore-type */ $package);
Loading history...
209
    }
210
211
    // @TODO local packages
212
213
    /**
214
     * @param string $signature - ex: ace-1.8.0-pl
215
     * @param modTransportProvider $provider
216
     * @param bool $latest
217
     * @throws TransportNotFoundException
218
     * @return bool|modTransportPackage
219
     */
220
    protected function downloadPackageFiles($signature, modTransportProvider $provider, $latest=true)
221
    {
222
        $transfer_options = [];
223
224
        $options = $this->getPackageLatestVersions($provider, $signature);
225
226
        if (isset($options[$signature])) {
227
            $transfer_options['location'] = $options[$signature]['location'];
228
        }
229
230
        if ($latest && count($options) > 1) {
231
            // @TODO review:
232
            $opt = reset($options);
233
234
            $signature = $opt['signature'];
235
            $transfer_options['location'] = $opt['location'];
236
        }
237
238
        /** @var modTransportPackage|bool $package */
239
        $package = $provider->transfer($signature, null, $transfer_options);
240
        if (!$package) {
241
            $parts = $this->getVersionInfo($signature);
242
243
            $this->possible_package_signatures = $provider->find(['query' => $parts['base']]);
244
245
            throw new TransportNotFoundException('Failed to download package ' . $signature.
246
                ' from the '.$provider->get('name').' transport provider. Verify the signature and provider');
247
        }
248
249
        return $package;
250
    }
251
252
    /**
253
     * @param modTransportPackage $package
254
     * @return bool
255
     * @throws TransportException
256
     */
257
    protected function runPackageInstallUpdate(modTransportPackage $package)
258
    {
259
        $installed = $package->install([]);
260
        $this->modx->cacheManager->refresh(array($this->modx->getOption('cache_packages_key', null, 'packages') => []));
261
        $this->modx->cacheManager->refresh();
262
263
        if (!$installed) {
264
            throw new TransportException('Failed to install package ' . $package->signature);
265
        }
266
267
        $this->userInteractionHandler->tellUser('Extra '.$package->get('signature').' has been installed!', userInteractionHandler::MASSAGE_ERROR);
268
269
        $this->modx->invokeEvent('OnPackageInstall', array(
270
            'package' => $package,
271
            'action' => $package->previousVersionInstalled() ? \xPDOTransport::ACTION_UPGRADE : \xPDOTransport::ACTION_INSTALL
272
        ));
273
274
        return $installed;
275
    }
276
277
    /**
278
     * Get basic version information about the package
279
     *
280
     * @param string $signature
281
     * @return array
282
     */
283
    protected function getVersionInfo($signature)
284
    {
285
        $parts = explode('-', $signature);
286
        return [
287
            'base' => $parts[0],
288
            'version' => $parts[1],
289
            'release' => (isset($parts[2]) ? $parts[2] : '')
290
        ];
291
    }
292
293
    /**
294
     * Format installed, created and updated dates
295
     * @param array $packageArray
296
     * @return array
297
     */
298
    protected function formatDates(array $packageArray)
299
    {
300
        if ($packageArray['updated'] != '0000-00-00 00:00:00' && $packageArray['updated'] != null) {
301
            $packageArray['updated'] = utf8_encode(date($this->date_format, strtotime($packageArray['updated'])));
302
        } else {
303
            $packageArray['updated'] = '';
304
        }
305
306
        $packageArray['created']= utf8_encode(date($this->date_format, strtotime($packageArray['created'])));
307
308
        if ($packageArray['installed'] == null || $packageArray['installed'] == '0000-00-00 00:00:00') {
309
            $packageArray['installed'] = null;
310
        } else {
311
            $packageArray['installed'] = utf8_encode(date($this->date_format, strtotime($packageArray['installed'])));
312
        }
313
        return $packageArray;
314
    }
315
316
    /**
317
     * @param modTransportPackage $package
318
     * @return array
319
     */
320
    protected function getAvailableUpdateInfo(modTransportPackage $package)
321
    {
322
        $updates = [
323
            'count' => 0,
324
            'versions' => []
325
        ];
326
        if ($package->get('provider') > 0 && $this->modx->getOption('auto_check_pkg_updates',null,false)) {
327
            $updateCacheKey = 'mgr/providers/updates/'.$package->get('provider').'/'.$package->get('signature');
328
            $updateCacheOptions = array(
329
                xPDO::OPT_CACHE_KEY => $this->modx->cacheManager->getOption('cache_packages_key', null, 'packages'),
330
                xPDO::OPT_CACHE_HANDLER => $this->modx->cacheManager->getOption('cache_packages_handler', null, $this->modx->cacheManager->getOption(xPDO::OPT_CACHE_HANDLER)),
331
            );
332
            $updates = $this->modx->cacheManager->get($updateCacheKey, $updateCacheOptions);
333
334
            if (empty($updates)) {
335
                /* cache providers to speed up load time */
336
                /** @var modTransportProvider $provider */
337
                $provider = $this->getPackageProvider($package);
338
339
                if ($provider) {
0 ignored issues
show
introduced by
$provider is of type modTransportProvider, thus it always evaluated to true.
Loading history...
340
                    $options = $this->getPackageLatestVersions($provider, $package->get('signature'));
341
342
                    $updates['count'] = count($options);
343
                    $updates['versions'] = $options;
344
345
                    $this->modx->cacheManager->set($updateCacheKey, $updates, $this->updates_cache_expire, $updateCacheOptions);
346
                }
347
            }
348
        }
349
350
        return $updates;
351
    }
352
353
    /**
354
     * @param modTransportProvider $provider
355
     * @param string $signature
356
     * @return array - only returns values if the extra has a version that is more recent then the signature
357
     */
358
    protected function getPackageLatestVersions(modTransportProvider $provider, $signature)
359
    {
360
        $package_versions = $provider->latest($signature);
361
362
        $options = [];
363
        /** @var \SimpleXMLElement $package */
364
        foreach ($package_versions as $package_version) {
365
            $version_info = array_merge([
366
                'location' => (string)$package_version['location'],
367
                'signature' => (string)$package_version['signature'],
368
                'package_name' => (string)$package_version['package_name'],
369
                'release' => '',
370
                'version' => ''
371
            ],
372
                $this->getVersionInfo((string)$package_version['signature'])
373
            );
374
375
            $options[$version_info['signature']] = $version_info;
376
        }
377
378
        return $options;
379
    }
380
381
    /**
382
     * @param modTransportPackage $package
383
     * @return modTransportProvider|false
384
     */
385
    protected function getPackageProvider(modTransportPackage $package)
386
    {
387
        /* cache providers to speed up load time */
388
        /** @var modTransportProvider $provider */
389
        if (!empty($this->providerCache[$package->get('provider')])) {
390
            return $this->providerCache[$package->get('provider')];
391
        }
392
393
        if ($provider = $package->getOne('Provider')) {
394
            $this->providerCache[$provider->get('id')] = $provider;
395
        }
396
397
        return $provider;
398
    }
399
400
    /**
401
     * @param string $name
402
     * @return bool|modTransportProvider
403
     */
404
    protected function getProvider($name='modx.com')
405
    {
406
        if (isset($this->provider_map[$name]) && $this->providerCache[$this->provider_map[$name]]) {
407
            return $this->providerCache[$this->provider_map[$name]];
408
        }
409
410
        /** @var modTransportProvider|bool $provider */
411
        $provider = $this->modx->getObject('transport.modTransportProvider', ['name' => $name]);
412
413
        if ($provider) {
414
            $this->provider_map[$name] = $provider->get('id');
415
            $this->providerCache[$this->provider_map[$name]] = $provider;
416
        }
417
418
        return $provider;
419
    }
420
}
421