|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
* This file is part of the Zikula package. |
|
5
|
|
|
* |
|
6
|
|
|
* Copyright Zikula Foundation - http://zikula.org/ |
|
7
|
|
|
* |
|
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
9
|
|
|
* file that was distributed with this source code. |
|
10
|
|
|
*/ |
|
11
|
|
|
|
|
12
|
|
|
namespace Zikula\ExtensionsModule\Helper; |
|
13
|
|
|
|
|
14
|
|
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
|
15
|
|
|
use Symfony\Component\Finder\Finder; |
|
16
|
|
|
use Symfony\Component\HttpFoundation\Session\SessionInterface; |
|
17
|
|
|
use vierbergenlars\SemVer\expression; |
|
18
|
|
|
use vierbergenlars\SemVer\version; |
|
19
|
|
|
use Zikula\Bundle\CoreBundle\Bundle\Helper\BootstrapHelper; |
|
20
|
|
|
use Zikula\Bundle\CoreBundle\Bundle\MetaData; |
|
21
|
|
|
use Zikula\Bundle\CoreBundle\Bundle\Scanner; |
|
22
|
|
|
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface; |
|
23
|
|
|
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel; |
|
24
|
|
|
use Zikula\Common\Translator\TranslatorInterface; |
|
25
|
|
|
use Zikula\Core\Event\GenericEvent; |
|
26
|
|
|
use Zikula\Core\Exception\FatalErrorException; |
|
27
|
|
|
use Zikula\ExtensionsModule\Api\ExtensionApi; |
|
28
|
|
|
use Zikula\ExtensionsModule\Entity\ExtensionEntity; |
|
29
|
|
|
use Zikula\ExtensionsModule\Entity\Repository\ExtensionDependencyRepository; |
|
30
|
|
|
use Zikula\ExtensionsModule\Entity\Repository\ExtensionRepository; |
|
31
|
|
|
use Zikula\ExtensionsModule\Entity\Repository\ExtensionVarRepository; |
|
32
|
|
|
use Zikula\ExtensionsModule\ExtensionEvents; |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Helper functions for the extensions bundle |
|
36
|
|
|
*/ |
|
37
|
|
|
class BundleSyncHelper |
|
38
|
|
|
{ |
|
39
|
|
|
/** |
|
40
|
|
|
* @var ZikulaHttpKernelInterface |
|
41
|
|
|
*/ |
|
42
|
|
|
private $kernel; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @var ExtensionRepository |
|
46
|
|
|
*/ |
|
47
|
|
|
private $extensionRepository; |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* @var ExtensionVarRepository |
|
51
|
|
|
*/ |
|
52
|
|
|
private $extensionVarRepository; |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* @var ExtensionDependencyRepository |
|
56
|
|
|
*/ |
|
57
|
|
|
private $extensionDependencyRepository; |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* @var TranslatorInterface |
|
61
|
|
|
*/ |
|
62
|
|
|
private $translator; |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* @var EventDispatcherInterface |
|
66
|
|
|
*/ |
|
67
|
|
|
private $dispatcher; |
|
68
|
|
|
|
|
69
|
|
|
/** |
|
70
|
|
|
* @var ExtensionStateHelper |
|
71
|
|
|
*/ |
|
72
|
|
|
private $extensionStateHelper; |
|
73
|
|
|
|
|
74
|
|
|
/** |
|
75
|
|
|
* @var BootstrapHelper |
|
76
|
|
|
*/ |
|
77
|
|
|
private $bootstrapHelper; |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* @var ComposerValidationHelper |
|
81
|
|
|
*/ |
|
82
|
|
|
private $composerValidationHelper; |
|
83
|
|
|
|
|
84
|
|
|
/** |
|
85
|
|
|
* @var SessionInterface |
|
86
|
|
|
*/ |
|
87
|
|
|
protected $session; |
|
88
|
|
|
|
|
89
|
|
|
/** |
|
90
|
|
|
* BundleSyncHelper constructor. |
|
91
|
|
|
* |
|
92
|
|
|
* @param ZikulaHttpKernelInterface $kernel |
|
93
|
|
|
* @param ExtensionRepository $extensionRepository |
|
94
|
|
|
* @param ExtensionVarRepository $extensionVarRepository |
|
95
|
|
|
* @param ExtensionDependencyRepository $extensionDependencyRepository |
|
96
|
|
|
* @param TranslatorInterface $translator |
|
97
|
|
|
* @param EventDispatcherInterface $dispatcher |
|
98
|
|
|
* @param ExtensionStateHelper $extensionStateHelper |
|
99
|
|
|
* @param ComposerValidationHelper $composerValidationHelper |
|
100
|
|
|
* @param SessionInterface $session |
|
101
|
|
|
*/ |
|
102
|
|
|
public function __construct( |
|
103
|
|
|
ZikulaHttpKernelInterface $kernel, |
|
104
|
|
|
ExtensionRepository $extensionRepository, |
|
105
|
|
|
ExtensionVarRepository $extensionVarRepository, |
|
106
|
|
|
ExtensionDependencyRepository $extensionDependencyRepository, |
|
107
|
|
|
TranslatorInterface $translator, |
|
108
|
|
|
EventDispatcherInterface $dispatcher, |
|
109
|
|
|
ExtensionStateHelper $extensionStateHelper, |
|
110
|
|
|
BootstrapHelper $bootstrapHelper, |
|
111
|
|
|
ComposerValidationHelper $composerValidationHelper, |
|
112
|
|
|
SessionInterface $session |
|
113
|
|
|
) { |
|
114
|
|
|
$this->kernel = $kernel; |
|
115
|
|
|
$this->extensionRepository = $extensionRepository; |
|
116
|
|
|
$this->extensionVarRepository = $extensionVarRepository; |
|
117
|
|
|
$this->extensionDependencyRepository = $extensionDependencyRepository; |
|
118
|
|
|
$this->translator = $translator; |
|
119
|
|
|
$this->dispatcher = $dispatcher; |
|
120
|
|
|
$this->extensionStateHelper = $extensionStateHelper; |
|
121
|
|
|
$this->bootstrapHelper = $bootstrapHelper; |
|
122
|
|
|
$this->composerValidationHelper = $composerValidationHelper; |
|
123
|
|
|
$this->session = $session; |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
|
|
/** |
|
127
|
|
|
* Scan the file system for bundles. |
|
128
|
|
|
* |
|
129
|
|
|
* This function scans the file system for bundles and returns an array with all (potential) bundles found. |
|
130
|
|
|
* |
|
131
|
|
|
* @param array $directories |
|
132
|
|
|
* @return array Thrown if the user doesn't have admin permissions over the bundle |
|
133
|
|
|
* @throws \Exception |
|
134
|
|
|
*/ |
|
135
|
|
|
public function scanForBundles(array $directories = []) |
|
136
|
|
|
{ |
|
137
|
|
|
$directories = empty($directories) ? ['system', 'modules'] : $directories; |
|
138
|
|
|
|
|
139
|
|
|
// sync the filesystem and the bundles table |
|
140
|
|
|
$this->bootstrapHelper->load(); |
|
141
|
|
|
|
|
142
|
|
|
// Get all bundles on filesystem |
|
143
|
|
|
$bundles = []; |
|
144
|
|
|
|
|
145
|
|
|
$scanner = new Scanner(); |
|
146
|
|
|
$scanner->scan($directories, 5); |
|
147
|
|
|
$newModules = $scanner->getModulesMetaData(); |
|
148
|
|
|
|
|
149
|
|
|
// scan for all bundle-type bundles (psr-4) in either /system or /bundles |
|
150
|
|
|
/** @var MetaData $bundleMetaData */ |
|
151
|
|
|
foreach ($newModules as $name => $bundleMetaData) { |
|
152
|
|
|
foreach ($bundleMetaData->getPsr4() as $ns => $path) { |
|
153
|
|
|
$this->kernel->getAutoloader()->addPsr4($ns, $path); |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
$bundleClass = $bundleMetaData->getClass(); |
|
157
|
|
|
|
|
158
|
|
|
/** @var $bundle \Zikula\Core\AbstractModule */ |
|
159
|
|
|
$bundle = new $bundleClass(); |
|
160
|
|
|
$bundleMetaData->setTranslator($this->translator); |
|
161
|
|
|
$bundleMetaData->setDirectoryFromBundle($bundle); |
|
162
|
|
|
$bundleVersionArray = $bundleMetaData->getFilteredVersionInfoArray(); |
|
163
|
|
|
$bundleVersionArray['capabilities'] = serialize($bundleVersionArray['capabilities']); |
|
164
|
|
|
$bundleVersionArray['securityschema'] = serialize($bundleVersionArray['securityschema']); |
|
165
|
|
|
$bundleVersionArray['dependencies'] = serialize($bundleVersionArray['dependencies']); |
|
166
|
|
|
|
|
167
|
|
|
$finder = new Finder(); |
|
168
|
|
|
$finder->files()->in($bundle->getPath())->depth(0)->name('composer.json'); |
|
169
|
|
|
foreach ($finder as $splFileInfo) { |
|
170
|
|
|
// there will only be one loop here |
|
171
|
|
|
$this->composerValidationHelper->check($splFileInfo); |
|
172
|
|
|
if ($this->composerValidationHelper->isValid()) { |
|
173
|
|
|
$bundles[$bundle->getName()] = $bundleVersionArray; |
|
174
|
|
|
$bundles[$bundle->getName()]['oldnames'] = isset($bundleVersionArray['oldnames']) ? $bundleVersionArray['oldnames'] : ''; |
|
175
|
|
View Code Duplication |
} else { |
|
|
|
|
|
|
176
|
|
|
$this->session->getFlashBag()->add('error', $this->translator->__f('Cannot load %extension because the composer file is invalid.', ['%extension' => $bundle->getName()])); |
|
177
|
|
|
foreach ($this->composerValidationHelper->getErrors() as $error) { |
|
178
|
|
|
$this->session->getFlashBag()->add('error', $error); |
|
179
|
|
|
} |
|
180
|
|
|
} |
|
181
|
|
|
} |
|
182
|
|
|
} |
|
183
|
|
|
|
|
184
|
|
|
$this->validate($bundles); |
|
185
|
|
|
|
|
186
|
|
|
return $bundles; |
|
187
|
|
|
} |
|
188
|
|
|
|
|
189
|
|
|
/** |
|
190
|
|
|
* Validate the extensions and ensure there are no duplicate names, displaynames or urls. |
|
191
|
|
|
* |
|
192
|
|
|
* @param array $extensions |
|
193
|
|
|
* @throws FatalErrorException |
|
194
|
|
|
*/ |
|
195
|
|
|
private function validate(array $extensions) |
|
196
|
|
|
{ |
|
197
|
|
|
$modulenames = []; |
|
198
|
|
|
$displaynames = []; |
|
199
|
|
|
$urls = []; |
|
200
|
|
|
|
|
201
|
|
|
// check for duplicate name, displayname or url |
|
202
|
|
|
foreach ($extensions as $dir => $modInfo) { |
|
203
|
|
|
$fields = ['name', 'displayname', 'url']; |
|
204
|
|
|
foreach ($fields as $field) { |
|
205
|
|
|
if (isset($modulenames[strtolower($modInfo[$field])])) { |
|
206
|
|
|
throw new FatalErrorException($this->translator->__f('Fatal Error: Two extensions share the same %field. [%ext1%] and [%ext2%]', [ |
|
207
|
|
|
'%field' => $field, |
|
208
|
|
|
'%ext1%' => $modInfo['name'], |
|
209
|
|
|
'%ext2%' => $modulenames[strtolower($modInfo['name'])] |
|
210
|
|
|
])); |
|
211
|
|
|
} |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
$modulenames[strtolower($modInfo['name'])] = $dir; |
|
215
|
|
|
$displaynames[strtolower($modInfo['displayname'])] = $dir; |
|
216
|
|
|
$urls[strtolower($modInfo['url'])] = $dir; |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* Sync extensions in the filesystem and the database. |
|
222
|
|
|
* @param array $extensionsFromFile |
|
223
|
|
|
* @param bool $forceDefaults |
|
224
|
|
|
* @return array $upgradedExtensions[<name>] = <version> |
|
225
|
|
|
*/ |
|
226
|
|
|
public function syncExtensions(array $extensionsFromFile, $forceDefaults = false) |
|
227
|
|
|
{ |
|
228
|
|
|
// Get all extensions in DB, indexed by name |
|
229
|
|
|
$extensionsFromDB = $this->extensionRepository->getIndexedArrayCollection('name'); |
|
230
|
|
|
|
|
231
|
|
|
// see if any extensions have changed since last regeneration |
|
232
|
|
|
$this->syncUpdatedExtensions($extensionsFromFile, $extensionsFromDB, $forceDefaults); |
|
233
|
|
|
|
|
234
|
|
|
// See if any extensions have been lost since last sync |
|
235
|
|
|
$this->syncLostExtensions($extensionsFromFile, $extensionsFromDB); |
|
236
|
|
|
|
|
237
|
|
|
// See any extensions have been gained since last sync, |
|
238
|
|
|
// or if any current extensions have been upgraded |
|
239
|
|
|
$upgradedExtensions = $this->syncAddedExtensions($extensionsFromFile, $extensionsFromDB); |
|
240
|
|
|
|
|
241
|
|
|
// Clear and reload the dependencies table with all current dependencies |
|
242
|
|
|
$this->extensionDependencyRepository->reloadExtensionDependencies($extensionsFromFile); |
|
243
|
|
|
|
|
244
|
|
|
return $upgradedExtensions; |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
/** |
|
248
|
|
|
* Sync extensions that are already in the Database. |
|
249
|
|
|
* - update from old names |
|
250
|
|
|
* - update compatibility |
|
251
|
|
|
* - update user settings (or reset to defaults) |
|
252
|
|
|
* - ensure current core compatibility |
|
253
|
|
|
* |
|
254
|
|
|
* @param array $extensionsFromFile |
|
255
|
|
|
* @param array $extensionsFromDB |
|
256
|
|
|
* @param bool $forceDefaults |
|
257
|
|
|
*/ |
|
258
|
|
|
private function syncUpdatedExtensions(array $extensionsFromFile, array &$extensionsFromDB, $forceDefaults = false) |
|
259
|
|
|
{ |
|
260
|
|
|
foreach ($extensionsFromFile as $name => $extensionFromFile) { |
|
261
|
|
|
foreach ($extensionsFromDB as $dbname => $extensionFromDB) { |
|
262
|
|
|
if (isset($extensionFromDB['name']) && in_array($extensionFromDB['name'], (array)$extensionFromFile['oldnames'])) { |
|
263
|
|
|
// migrate its modvars |
|
264
|
|
|
$this->extensionVarRepository->updateName($dbname, $name); |
|
265
|
|
|
// rename the module register |
|
266
|
|
|
$this->extensionRepository->updateName($dbname, $name); |
|
267
|
|
|
// replace the old module with the new one in the $extensionsFromDB array |
|
268
|
|
|
$extensionsFromDB[$name] = $extensionFromDB; |
|
269
|
|
|
unset($extensionsFromDB[$dbname]); |
|
270
|
|
|
} |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
|
// If extension was previously determined to be incompatible with the core. return to original state |
|
274
|
|
|
if (isset($extensionsFromDB[$name]) && $extensionsFromDB[$name]['state'] > 10) { |
|
275
|
|
|
$extensionsFromDB[$name]['state'] = $extensionsFromDB[$name]['state'] - ExtensionApi::INCOMPATIBLE_CORE_SHIFT; |
|
276
|
|
|
$this->extensionStateHelper->updateState($extensionsFromDB[$name]['id'], $extensionsFromDB[$name]['state']); |
|
277
|
|
|
} |
|
278
|
|
|
|
|
279
|
|
|
// update the DB information for this extension to reflect user settings (e.g. url) |
|
280
|
|
|
if (isset($extensionsFromDB[$name]['id'])) { |
|
281
|
|
|
$extensionFromFile['id'] = $extensionsFromDB[$name]['id']; |
|
282
|
|
|
if ($extensionsFromDB[$name]['state'] != ExtensionApi::STATE_UNINITIALISED && $extensionsFromDB[$name]['state'] != ExtensionApi::STATE_INVALID) { |
|
283
|
|
|
unset($extensionFromFile['version']); |
|
284
|
|
|
} |
|
285
|
|
|
if (!$forceDefaults) { |
|
286
|
|
|
unset($extensionFromFile['displayname']); |
|
287
|
|
|
unset($extensionFromFile['description']); |
|
288
|
|
|
unset($extensionFromFile['url']); |
|
289
|
|
|
} |
|
290
|
|
|
|
|
291
|
|
|
unset($extensionFromFile['oldnames']); |
|
292
|
|
|
unset($extensionFromFile['dependencies']); |
|
293
|
|
|
$extensionFromFile['capabilities'] = unserialize($extensionFromFile['capabilities']); |
|
294
|
|
|
$extensionFromFile['securityschema'] = unserialize($extensionFromFile['securityschema']); |
|
295
|
|
|
$extension = $this->extensionRepository->find($extensionFromFile['id']); |
|
296
|
|
|
$extension->merge($extensionFromFile); |
|
297
|
|
|
$this->extensionRepository->persistAndFlush($extension); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
// check extension core requirement is compatible with current core |
|
301
|
|
|
$coreCompatibility = isset($extensionFromFile['corecompatibility']) |
|
302
|
|
|
? $extensionFromFile['corecompatibility'] |
|
303
|
|
|
: $this->formatCoreCompatibilityString($extensionFromFile['core_min'], $extensionFromFile['core_max']); |
|
304
|
|
|
$isCompatible = $this->isCoreCompatible($coreCompatibility); |
|
305
|
|
|
if (isset($extensionsFromDB[$name])) { |
|
306
|
|
|
if (!$isCompatible) { |
|
307
|
|
|
// extension is incompatible with current core |
|
308
|
|
|
$extensionsFromDB[$name]['state'] = $extensionsFromDB[$name]['state'] + ExtensionApi::INCOMPATIBLE_CORE_SHIFT; |
|
309
|
|
|
$this->extensionStateHelper->updateState($extensionsFromDB[$name]['id'], $extensionsFromDB[$name]['state']); |
|
310
|
|
|
} |
|
311
|
|
|
if (isset($extensionsFromDB[$name]['state'])) { |
|
312
|
|
|
$extensionFromFile['state'] = $extensionsFromDB[$name]['state']; |
|
313
|
|
|
} |
|
314
|
|
|
} |
|
315
|
|
|
} |
|
316
|
|
|
} |
|
317
|
|
|
|
|
318
|
|
|
/** |
|
319
|
|
|
* Remove extensions from the DB that have been removed from the filesystem. |
|
320
|
|
|
* |
|
321
|
|
|
* @param array $extensionsFromFile |
|
322
|
|
|
* @param array $extensionsFromDB |
|
323
|
|
|
*/ |
|
324
|
|
|
private function syncLostExtensions(array $extensionsFromFile, array &$extensionsFromDB) |
|
325
|
|
|
{ |
|
326
|
|
|
foreach ($extensionsFromDB as $name => $unusedVariable) { |
|
327
|
|
|
if (array_key_exists($name, $extensionsFromFile)) { |
|
328
|
|
|
continue; |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
$lostModule = $this->extensionRepository->get($name); // must obtain Entity because value from $extensionsFromDB is only an array |
|
332
|
|
|
if (!$lostModule) { |
|
333
|
|
|
throw new \RuntimeException($this->translator->__f('Error! Could not load data for module %s.', [$name])); |
|
334
|
|
|
} |
|
335
|
|
|
$lostModuleState = $lostModule->getState(); |
|
336
|
|
|
if (($lostModuleState == ExtensionApi::STATE_INVALID) |
|
337
|
|
|
|| ($lostModuleState == ExtensionApi::STATE_INVALID + ExtensionApi::INCOMPATIBLE_CORE_SHIFT)) { |
|
338
|
|
|
// extension was invalid and subsequently removed from file system, |
|
339
|
|
|
// or extension was incompatible with core and subsequently removed, delete it |
|
340
|
|
|
$this->extensionRepository->removeAndFlush($lostModule); |
|
341
|
|
|
} elseif (($lostModuleState == ExtensionApi::STATE_UNINITIALISED) |
|
342
|
|
|
|| ($lostModuleState == ExtensionApi::STATE_UNINITIALISED + ExtensionApi::INCOMPATIBLE_CORE_SHIFT)) { |
|
343
|
|
|
// extension was uninitialised and subsequently removed from file system, delete it |
|
344
|
|
|
$this->extensionRepository->removeAndFlush($lostModule); |
|
345
|
|
|
} else { |
|
346
|
|
|
// Set state of module to 'missing' |
|
347
|
|
|
// This state cannot be reached in with an ACTIVE bundle. - ACTIVE bundles are part of the pre-compiled Kernel. |
|
348
|
|
|
// extensions that are inactive can be marked as missing. |
|
349
|
|
|
$this->extensionStateHelper->updateState($lostModule->getId(), ExtensionApi::STATE_MISSING); |
|
350
|
|
|
} |
|
351
|
|
|
|
|
352
|
|
|
unset($extensionsFromDB[$name]); |
|
353
|
|
|
} |
|
354
|
|
|
} |
|
355
|
|
|
|
|
356
|
|
|
/** |
|
357
|
|
|
* Add extensions to the DB that have been added to the filesystem. |
|
358
|
|
|
* - add uninitialized extensions |
|
359
|
|
|
* - update missing or invalid extensions |
|
360
|
|
|
* |
|
361
|
|
|
* @param array $extensionsFromFile |
|
362
|
|
|
* @param array $extensionsFromDB |
|
363
|
|
|
* @return array $upgradedExtensions[<name>] => <version> |
|
364
|
|
|
*/ |
|
365
|
|
|
private function syncAddedExtensions(array $extensionsFromFile, array $extensionsFromDB) |
|
366
|
|
|
{ |
|
367
|
|
|
$upgradedExtensions = []; |
|
368
|
|
|
|
|
369
|
|
|
foreach ($extensionsFromFile as $name => $extensionFromFile) { |
|
370
|
|
|
if (empty($extensionsFromDB[$name])) { |
|
371
|
|
|
$extensionFromFile['state'] = ExtensionApi::STATE_UNINITIALISED; |
|
372
|
|
|
if (!$extensionFromFile['version']) { |
|
373
|
|
|
// set state to invalid if we can't determine a version |
|
374
|
|
|
$extensionFromFile['state'] = ExtensionApi::STATE_INVALID; |
|
375
|
|
|
} else { |
|
376
|
|
|
$coreCompatibility = isset($extensionFromFile['corecompatibility']) |
|
377
|
|
|
? $extensionFromFile['corecompatibility'] |
|
378
|
|
|
: $this->formatCoreCompatibilityString($extensionFromFile['core_min'], $extensionFromFile['core_max']); |
|
379
|
|
|
// shift state if module is incompatible with core version |
|
380
|
|
|
$extensionFromFile['state'] = $this->isCoreCompatible($coreCompatibility) |
|
381
|
|
|
? $extensionFromFile['state'] |
|
382
|
|
|
: $extensionFromFile['state'] + ExtensionApi::INCOMPATIBLE_CORE_SHIFT; |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
// unset vars that don't matter |
|
386
|
|
|
unset($extensionFromFile['oldnames']); |
|
387
|
|
|
unset($extensionFromFile['dependencies']); |
|
388
|
|
|
|
|
389
|
|
|
// unserialize vars |
|
390
|
|
|
$extensionFromFile['capabilities'] = unserialize($extensionFromFile['capabilities']); |
|
391
|
|
|
$extensionFromFile['securityschema'] = unserialize($extensionFromFile['securityschema']); |
|
392
|
|
|
|
|
393
|
|
|
// insert new module to db |
|
394
|
|
|
$newExtension = new ExtensionEntity(); |
|
395
|
|
|
$newExtension->merge($extensionFromFile); |
|
396
|
|
|
$vetoEvent = new GenericEvent($newExtension); |
|
397
|
|
|
$this->dispatcher->dispatch(ExtensionEvents::INSERT_VETO, $vetoEvent); |
|
398
|
|
|
if (!$vetoEvent->isPropagationStopped()) { |
|
399
|
|
|
$this->extensionRepository->persistAndFlush($newExtension); |
|
400
|
|
|
} |
|
401
|
|
|
} else { |
|
402
|
|
|
// extension is in the db already |
|
403
|
|
|
if (($extensionsFromDB[$name]['state'] == ExtensionApi::STATE_MISSING) |
|
404
|
|
|
|| ($extensionsFromDB[$name]['state'] == ExtensionApi::STATE_MISSING + ExtensionApi::INCOMPATIBLE_CORE_SHIFT)) { |
|
405
|
|
|
// extension was lost, now it is here again |
|
406
|
|
|
$this->extensionStateHelper->updateState($extensionsFromDB[$name]['id'], ExtensionApi::STATE_INACTIVE); |
|
407
|
|
|
} elseif ((($extensionsFromDB[$name]['state'] == ExtensionApi::STATE_INVALID) |
|
408
|
|
|
|| ($extensionsFromDB[$name]['state'] == ExtensionApi::STATE_INVALID + ExtensionApi::INCOMPATIBLE_CORE_SHIFT)) |
|
409
|
|
|
&& $extensionFromFile['version']) { |
|
410
|
|
|
$coreCompatibility = isset($extensionFromFile['corecompatibility']) |
|
411
|
|
|
? $extensionFromFile['corecompatibility'] |
|
412
|
|
|
: $this->formatCoreCompatibilityString($extensionFromFile['core_min'], $extensionFromFile['core_max']); |
|
413
|
|
|
$isCompatible = $this->isCoreCompatible($coreCompatibility); |
|
414
|
|
|
if ($isCompatible) { |
|
415
|
|
|
// extension was invalid, now it is valid |
|
416
|
|
|
$this->extensionStateHelper->updateState($extensionsFromDB[$name]['id'], ExtensionApi::STATE_UNINITIALISED); |
|
417
|
|
|
} |
|
418
|
|
|
} |
|
419
|
|
|
|
|
420
|
|
|
if ($extensionsFromDB[$name]['version'] != $extensionFromFile['version']) { |
|
421
|
|
|
if ($extensionsFromDB[$name]['state'] != ExtensionApi::STATE_UNINITIALISED && |
|
422
|
|
|
$extensionsFromDB[$name]['state'] != ExtensionApi::STATE_INVALID) { |
|
423
|
|
|
$this->extensionStateHelper->updateState($extensionsFromDB[$name]['id'], ExtensionApi::STATE_UPGRADED); |
|
424
|
|
|
$upgradedExtensions[$name] = $extensionFromFile['version']; |
|
425
|
|
|
} |
|
426
|
|
|
} |
|
427
|
|
|
} |
|
428
|
|
|
} |
|
429
|
|
|
|
|
430
|
|
|
return $upgradedExtensions; |
|
431
|
|
|
} |
|
432
|
|
|
|
|
433
|
|
|
/** |
|
434
|
|
|
* Determine if $min and $max values are compatible with Current Core version |
|
435
|
|
|
* |
|
436
|
|
|
* @param string $compatibilityString Semver |
|
437
|
|
|
* @return bool |
|
438
|
|
|
*/ |
|
439
|
|
|
private function isCoreCompatible($compatibilityString) |
|
440
|
|
|
{ |
|
441
|
|
|
$coreVersion = new version(ZikulaKernel::VERSION); |
|
442
|
|
|
$requiredVersionExpression = new expression($compatibilityString); |
|
443
|
|
|
|
|
444
|
|
|
return $requiredVersionExpression->satisfiedBy($coreVersion); |
|
445
|
|
|
} |
|
446
|
|
|
|
|
447
|
|
|
/** |
|
448
|
|
|
* Format a compatibility string suitable for semver comparison using vierbergenlars/php-semver |
|
449
|
|
|
* |
|
450
|
|
|
* @param null $coreMin |
|
451
|
|
|
* @param null $coreMax |
|
452
|
|
|
* @return string |
|
453
|
|
|
*/ |
|
454
|
|
|
private function formatCoreCompatibilityString($coreMin = null, $coreMax = null) |
|
455
|
|
|
{ |
|
456
|
|
|
$coreMin = !empty($coreMin) ? $coreMin : '1.4.0'; |
|
457
|
|
|
$coreMax = !empty($coreMax) ? $coreMax : '2.9.99'; |
|
458
|
|
|
|
|
459
|
|
|
return $coreMin . ' - ' . $coreMax; |
|
460
|
|
|
} |
|
461
|
|
|
} |
|
462
|
|
|
|
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.