1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Manages plugin packages under mod. |
5
|
|
|
* |
6
|
|
|
* @todo This should eventually be merged into \ElggPlugin. |
7
|
|
|
* Currently \ElggPlugin objects are only used to get and save |
8
|
|
|
* plugin settings and user settings, so not every plugin |
9
|
|
|
* has an \ElggPlugin object. It's not implemented in \ElggPlugin |
10
|
|
|
* right now because of conflicts with at least the constructor, |
11
|
|
|
* enable(), disable(), and private settings. |
12
|
|
|
* |
13
|
|
|
* Around 1.9 or so we should each plugin over to using |
14
|
|
|
* \ElggPlugin and merge \ElggPluginPackage and \ElggPlugin. |
15
|
|
|
* |
16
|
|
|
* @package Elgg.Core |
17
|
|
|
* @subpackage Plugins |
18
|
|
|
* @since 1.8 |
19
|
|
|
*/ |
20
|
|
|
class ElggPluginPackage { |
21
|
|
|
|
22
|
|
|
const STATIC_CONFIG_FILENAME = 'elgg-plugin.php'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The required files in the package |
26
|
|
|
* |
27
|
|
|
* @var array |
28
|
|
|
*/ |
29
|
|
|
private $requiredFiles = [ |
30
|
|
|
'manifest.xml' |
31
|
|
|
]; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* The optional files that can be read and served through the markdown page handler |
35
|
|
|
* @var array |
36
|
|
|
*/ |
37
|
|
|
private $textFiles = [ |
38
|
|
|
'README.txt', |
39
|
|
|
'CHANGES.txt', |
40
|
|
|
'INSTALL.txt', |
41
|
|
|
'COPYRIGHT.txt', |
42
|
|
|
'LICENSE.txt', |
43
|
|
|
'README', |
44
|
|
|
'README.md', |
45
|
|
|
'README.markdown' |
46
|
|
|
]; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Valid types for provides. |
50
|
|
|
* |
51
|
|
|
* @var array |
52
|
|
|
*/ |
53
|
|
|
private $providesSupportedTypes = [ |
54
|
|
|
'plugin', |
55
|
|
|
'php_extension' |
56
|
|
|
]; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* The type of requires/conflicts supported |
60
|
|
|
* |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
private $depsSupportedTypes = [ |
64
|
|
|
'elgg_release', |
65
|
|
|
'php_version', |
66
|
|
|
'php_extension', |
67
|
|
|
'php_ini', |
68
|
|
|
'plugin', |
69
|
|
|
'priority', |
70
|
|
|
]; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* An invalid plugin error. |
74
|
|
|
*/ |
75
|
|
|
private $errorMsg = ''; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* The plugin's manifest object |
79
|
|
|
* |
80
|
|
|
* @var \ElggPluginManifest |
81
|
|
|
*/ |
82
|
|
|
protected $manifest; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* The plugin's full path |
86
|
|
|
* |
87
|
|
|
* @var string |
88
|
|
|
*/ |
89
|
|
|
protected $path; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Is the plugin valid? |
93
|
|
|
* |
94
|
|
|
* @var mixed Bool after validation check, null before. |
95
|
|
|
*/ |
96
|
|
|
protected $valid = null; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* The plugin ID (dir name) |
100
|
|
|
* |
101
|
|
|
* @var string |
102
|
|
|
*/ |
103
|
|
|
protected $id; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Load a plugin package from mod/$id or by full path. |
107
|
|
|
* |
108
|
|
|
* @param string $plugin The ID (directory name) or full path of the plugin. |
109
|
|
|
* @param bool $validate Automatically run isValid()? |
110
|
|
|
* |
111
|
|
|
* @throws PluginException |
112
|
|
|
*/ |
113
|
243 |
|
public function __construct($plugin, $validate = true) { |
114
|
243 |
|
$plugin_path = _elgg_config()->plugins_path; |
115
|
|
|
// @todo wanted to avoid another is_dir() call here. |
116
|
|
|
// should do some profiling to see how much it affects |
117
|
243 |
|
if (strpos($plugin, $plugin_path) === 0 || is_dir($plugin)) { |
118
|
|
|
// this is a path |
119
|
243 |
|
$path = \Elgg\Project\Paths::sanitize($plugin); |
120
|
|
|
|
121
|
|
|
// the id is the last element of the array |
122
|
243 |
|
$path_array = explode('/', trim($path, '/')); |
123
|
243 |
|
$id = array_pop($path_array); |
124
|
|
|
} else { |
125
|
|
|
// this is a plugin id |
126
|
|
|
// strict plugin names |
127
|
1 |
|
if (preg_match('/[^a-z0-9\.\-_]/i', $plugin)) { |
128
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidID', [$plugin])); |
129
|
|
|
} |
130
|
|
|
|
131
|
1 |
|
$path = "{$plugin_path}$plugin/"; |
132
|
1 |
|
$id = $plugin; |
133
|
|
|
} |
134
|
|
|
|
135
|
243 |
|
if (!is_dir($path)) { |
136
|
1 |
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPath', [$path])); |
137
|
|
|
} |
138
|
|
|
|
139
|
242 |
|
$this->path = $path; |
140
|
242 |
|
$this->id = $id; |
141
|
|
|
|
142
|
242 |
|
if ($validate && !$this->isValid()) { |
143
|
|
|
if ($this->errorMsg) { |
144
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPlugin:Details', |
145
|
|
|
[$plugin, $this->errorMsg])); |
146
|
|
|
} else { |
147
|
|
|
throw new \PluginException(_elgg_services()->translator->translate('PluginException:InvalidPlugin', [$plugin])); |
148
|
|
|
} |
149
|
|
|
} |
150
|
242 |
|
} |
151
|
|
|
|
152
|
|
|
/******************************** |
153
|
|
|
* Validation and sanity checks * |
154
|
|
|
********************************/ |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Checks if this is a valid Elgg plugin. |
158
|
|
|
* |
159
|
|
|
* Checks for requires files as defined at the start of this |
160
|
|
|
* class. Will check require manifest fields via \ElggPluginManifest |
161
|
|
|
* for Elgg 1.8 plugins. |
162
|
|
|
* |
163
|
|
|
* @note This doesn't check dependencies or conflicts. |
164
|
|
|
* Use {@link \ElggPluginPackage::canActivate()} or |
165
|
|
|
* {@link \ElggPluginPackage::checkDependencies()} for that. |
166
|
|
|
* |
167
|
|
|
* @return bool |
168
|
|
|
*/ |
169
|
33 |
|
public function isValid() { |
170
|
33 |
|
if (!isset($this->valid)) { |
171
|
33 |
|
$this->valid = $this->validate(); |
172
|
|
|
} |
173
|
|
|
|
174
|
33 |
|
return $this->valid; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @return bool |
179
|
|
|
*/ |
180
|
33 |
|
private function validate() { |
181
|
|
|
// check required files. |
182
|
33 |
|
$have_req_files = true; |
183
|
33 |
|
foreach ($this->requiredFiles as $file) { |
184
|
33 |
|
if (!is_readable($this->path . $file)) { |
185
|
|
|
$have_req_files = false; |
|
|
|
|
186
|
|
|
$this->errorMsg = |
187
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:MissingFile', [$file]); |
188
|
|
|
|
189
|
33 |
|
return false; |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// check required files |
194
|
33 |
|
if (!$have_req_files) { |
195
|
|
|
return $this->valid = false; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// check for valid manifest. |
199
|
33 |
|
if (!$this->loadManifest()) { |
200
|
|
|
return false; |
201
|
|
|
} |
202
|
|
|
|
203
|
33 |
|
if (!$this->isNamedCorrectly()) { |
204
|
|
|
return false; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
// can't require or conflict with yourself or something you provide. |
208
|
|
|
// make sure provides are all valid. |
209
|
33 |
|
if (!$this->hasSaneDependencies()) { |
210
|
|
|
return false; |
211
|
|
|
} |
212
|
|
|
|
213
|
33 |
|
if (!$this->hasReadableConfigFile()) { |
214
|
|
|
return false; |
215
|
|
|
} |
216
|
|
|
|
217
|
33 |
|
return true; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* Check that, if the plugin has a static config file, it is readable. We wait to read the contents |
222
|
|
|
* because we don't want to risk crashing the whole plugins page. |
223
|
|
|
* |
224
|
|
|
* @return bool |
225
|
|
|
*/ |
226
|
33 |
|
private function hasReadableConfigFile() { |
227
|
33 |
|
$file = "{$this->path}/" . self::STATIC_CONFIG_FILENAME; |
228
|
33 |
|
if (!is_file($file)) { |
229
|
30 |
|
return true; |
230
|
|
|
} |
231
|
|
|
|
232
|
5 |
|
if (is_readable($file)) { |
233
|
5 |
|
return true; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
$this->errorMsg = |
237
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:UnreadableConfig'); |
238
|
|
|
|
239
|
|
|
return false; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Check that the plugin is installed in the directory with name specified |
244
|
|
|
* in the manifest's "id" element. |
245
|
|
|
* |
246
|
|
|
* @return bool |
247
|
|
|
*/ |
248
|
33 |
|
private function isNamedCorrectly() { |
249
|
33 |
|
$manifest = $this->getManifest(); |
250
|
33 |
|
if ($manifest) { |
251
|
33 |
|
$required_id = $manifest->getID(); |
252
|
33 |
|
if (!empty($required_id) && ($required_id !== $this->id)) { |
253
|
|
|
$this->errorMsg = |
254
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidId', [$required_id]); |
255
|
|
|
|
256
|
|
|
return false; |
257
|
|
|
} |
258
|
|
|
} |
259
|
|
|
|
260
|
33 |
|
return true; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Check the plugin doesn't require or conflict with itself |
265
|
|
|
* or something provides. Also check that it only list |
266
|
|
|
* valid provides. Deps are checked in checkDependencies() |
267
|
|
|
* |
268
|
|
|
* @note Plugins always provide themselves. |
269
|
|
|
* |
270
|
|
|
* @todo Don't let them require and conflict the same thing |
271
|
|
|
* |
272
|
|
|
* @return bool |
273
|
|
|
*/ |
274
|
33 |
|
private function hasSaneDependencies() { |
275
|
|
|
// protection against plugins with no manifest file |
276
|
33 |
|
if (!$this->getManifest()) { |
277
|
|
|
return false; |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
// Note: $conflicts and $requires are not unused. They're called dynamically |
281
|
33 |
|
$conflicts = $this->getManifest()->getConflicts(); |
282
|
33 |
|
$requires = $this->getManifest()->getRequires(); |
283
|
33 |
|
$provides = $this->getManifest()->getProvides(); |
284
|
|
|
|
285
|
33 |
|
foreach ($provides as $provide) { |
286
|
|
|
// only valid provide types |
287
|
33 |
|
if (!in_array($provide['type'], $this->providesSupportedTypes)) { |
288
|
|
|
$this->errorMsg = |
289
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidProvides', [$provide['type']]); |
290
|
|
|
|
291
|
|
|
return false; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
// doesn't conflict or require any of its provides |
295
|
33 |
|
$name = $provide['name']; |
296
|
33 |
|
foreach (['conflicts', 'requires'] as $dep_type) { |
297
|
33 |
|
foreach (${$dep_type} as $dep) { |
298
|
33 |
|
if (!in_array($dep['type'], $this->depsSupportedTypes)) { |
299
|
|
|
$this->errorMsg = |
300
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:InvalidDependency', [$dep['type']]); |
301
|
|
|
|
302
|
|
|
return false; |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
// make sure nothing is providing something it conflicts or requires. |
306
|
33 |
|
if (isset($dep['name']) && $dep['name'] == $name) { |
307
|
30 |
|
$version_compare = version_compare($provide['version'], $dep['version'], $dep['comparison']); |
308
|
|
|
|
309
|
30 |
|
if ($version_compare) { |
310
|
|
|
$this->errorMsg = |
311
|
|
|
_elgg_services()->translator->translate('ElggPluginPackage:InvalidPlugin:CircularDep', |
312
|
|
|
[$dep['type'], $dep['name'], $this->id]); |
313
|
|
|
|
314
|
33 |
|
return false; |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
} |
320
|
|
|
|
321
|
33 |
|
return true; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
|
325
|
|
|
/************ |
326
|
|
|
* Manifest * |
327
|
|
|
************/ |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Returns a parsed manifest file. |
331
|
|
|
* |
332
|
|
|
* @return \ElggPluginManifest |
333
|
|
|
*/ |
334
|
241 |
|
public function getManifest() { |
335
|
241 |
|
if (!$this->manifest) { |
336
|
210 |
|
if (!$this->loadManifest()) { |
337
|
|
|
return false; |
|
|
|
|
338
|
|
|
} |
339
|
|
|
} |
340
|
|
|
|
341
|
241 |
|
return $this->manifest; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Loads the manifest into this->manifest as an |
346
|
|
|
* \ElggPluginManifest object. |
347
|
|
|
* |
348
|
|
|
* @return bool |
349
|
|
|
*/ |
350
|
241 |
|
private function loadManifest() { |
351
|
241 |
|
$file = $this->path . 'manifest.xml'; |
352
|
|
|
|
353
|
|
|
try { |
354
|
241 |
|
$this->manifest = new \ElggPluginManifest($file, $this->id); |
355
|
|
|
} catch (Exception $e) { |
356
|
|
|
$this->errorMsg = $e->getMessage(); |
357
|
|
|
|
358
|
|
|
return false; |
359
|
|
|
} |
360
|
|
|
|
361
|
241 |
|
if ($this->manifest instanceof \ElggPluginManifest) { |
362
|
241 |
|
return true; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
$this->errorMsg = _elgg_services()->translator->translate('unknown_error'); |
366
|
|
|
|
367
|
|
|
return false; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/**************** |
371
|
|
|
* Readme Files * |
372
|
|
|
***************/ |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Returns an array of present and readable text files |
376
|
|
|
* |
377
|
|
|
* @return array |
378
|
|
|
*/ |
379
|
1 |
|
public function getTextFilenames() { |
380
|
1 |
|
return $this->textFiles; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
/*********************** |
384
|
|
|
* Dependencies system * |
385
|
|
|
***********************/ |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Returns if the Elgg system meets the plugin's dependency |
389
|
|
|
* requirements. This includes both requires and conflicts. |
390
|
|
|
* |
391
|
|
|
* Full reports can be requested. The results are returned |
392
|
|
|
* as an array of arrays in the form array( |
393
|
|
|
* 'type' => requires|conflicts, |
394
|
|
|
* 'dep' => array( dependency array ), |
395
|
|
|
* 'status' => bool if depedency is met, |
396
|
|
|
* 'comment' => optional comment to display to the user. |
397
|
|
|
* ) |
398
|
|
|
* |
399
|
|
|
* @param bool $full_report Return a full report. |
400
|
|
|
* |
401
|
|
|
* @return bool|array |
402
|
|
|
*/ |
403
|
3 |
|
public function checkDependencies($full_report = false) { |
404
|
|
|
// Note: $conflicts and $requires are not unused. They're called dynamically |
405
|
3 |
|
$requires = $this->getManifest()->getRequires(); |
406
|
3 |
|
$conflicts = $this->getManifest()->getConflicts(); |
407
|
|
|
|
408
|
3 |
|
$enabled_plugins = elgg_get_plugins('active'); |
409
|
3 |
|
$this_id = $this->getID(); |
410
|
3 |
|
$report = []; |
411
|
|
|
|
412
|
|
|
// first, check if any active plugin conflicts with us. |
413
|
3 |
|
foreach ($enabled_plugins as $plugin) { |
414
|
3 |
|
$temp_conflicts = []; |
415
|
3 |
|
$temp_manifest = $plugin->getManifest(); |
416
|
3 |
|
if ($temp_manifest instanceof \ElggPluginManifest) { |
417
|
3 |
|
$temp_conflicts = $plugin->getManifest()->getConflicts(); |
418
|
|
|
} |
419
|
3 |
|
foreach ($temp_conflicts as $conflict) { |
420
|
3 |
|
if ($conflict['type'] == 'plugin' && $conflict['name'] == $this_id) { |
421
|
|
|
$result = $this->checkDepPlugin($conflict, $enabled_plugins, false); |
422
|
|
|
|
423
|
|
|
// rewrite the conflict to show the originating plugin |
424
|
|
|
$conflict['name'] = $plugin->getDisplayName(); |
425
|
|
|
|
426
|
|
|
if (!$full_report && !$result['status']) { |
427
|
|
|
$css_id = preg_replace('/[^a-z0-9-]/i', '-', $plugin->getManifest()->getID()); |
428
|
|
|
$link = elgg_view('output/url', [ |
429
|
|
|
'text' => $plugin->getDisplayName(), |
430
|
|
|
'href' => "#$css_id", |
431
|
|
|
]); |
432
|
|
|
|
433
|
|
|
$key = 'ElggPluginPackage:InvalidPlugin:ConflictsWithPlugin'; |
434
|
|
|
$this->errorMsg = _elgg_services()->translator->translate($key, [$link]); |
435
|
|
|
|
436
|
|
|
return $result['status']; |
437
|
|
|
} else { |
438
|
|
|
$report[] = [ |
439
|
|
|
'type' => 'conflicted', |
440
|
|
|
'dep' => $conflict, |
441
|
|
|
'status' => $result['status'], |
442
|
3 |
|
'value' => $this->getManifest()->getVersion() |
443
|
|
|
]; |
444
|
|
|
} |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
} |
448
|
|
|
|
449
|
3 |
|
$check_types = ['requires', 'conflicts']; |
450
|
|
|
|
451
|
3 |
|
if ($full_report) { |
452
|
|
|
// Note: $suggests is not unused. It's called dynamically |
453
|
|
|
$suggests = $this->getManifest()->getSuggests(); |
454
|
|
|
$check_types[] = 'suggests'; |
455
|
|
|
} |
456
|
|
|
|
457
|
3 |
|
foreach ($check_types as $dep_type) { |
458
|
3 |
|
$inverse = ($dep_type == 'conflicts') ? true : false; |
459
|
|
|
|
460
|
3 |
|
foreach (${$dep_type} as $dep) { |
461
|
3 |
|
switch ($dep['type']) { |
462
|
|
|
case 'elgg_release': |
463
|
3 |
|
$result = $this->checkDepElgg($dep, elgg_get_version(true), $inverse); |
|
|
|
|
464
|
3 |
|
break; |
465
|
|
|
|
466
|
|
|
case 'plugin': |
467
|
|
|
$result = $this->checkDepPlugin($dep, $enabled_plugins, $inverse); |
468
|
|
|
break; |
469
|
|
|
|
470
|
|
|
case 'priority': |
471
|
|
|
$result = $this->checkDepPriority($dep, $enabled_plugins, $inverse); |
472
|
|
|
break; |
473
|
|
|
|
474
|
|
|
case 'php_version': |
475
|
|
|
$result = $this->checkDepPhpVersion($dep, $inverse); |
476
|
|
|
break; |
477
|
|
|
|
478
|
|
|
case 'php_extension': |
479
|
|
|
$result = $this->checkDepPhpExtension($dep, $inverse); |
480
|
|
|
break; |
481
|
|
|
|
482
|
|
|
case 'php_ini': |
483
|
|
|
$result = $this->checkDepPhpIni($dep, $inverse); |
484
|
|
|
break; |
485
|
|
|
|
486
|
|
|
default: |
487
|
|
|
$result = null;//skip further check |
488
|
|
|
break; |
489
|
|
|
} |
490
|
|
|
|
491
|
3 |
|
if ($result !== null) { |
492
|
|
|
// unless we're doing a full report, break as soon as we fail. |
493
|
3 |
|
if (!$full_report && !$result['status']) { |
494
|
|
|
$this->errorMsg = "Missing dependencies."; |
495
|
|
|
|
496
|
|
|
return $result['status']; |
497
|
|
|
} else { |
498
|
|
|
// build report element and comment |
499
|
3 |
|
$report[] = [ |
500
|
3 |
|
'type' => $dep_type, |
501
|
3 |
|
'dep' => $dep, |
502
|
3 |
|
'status' => $result['status'], |
503
|
3 |
|
'value' => $result['value'] |
504
|
|
|
]; |
505
|
|
|
} |
506
|
|
|
} |
507
|
|
|
} |
508
|
|
|
} |
509
|
|
|
|
510
|
3 |
|
if ($full_report) { |
511
|
|
|
// add provides to full report |
512
|
|
|
$provides = $this->getManifest()->getProvides(); |
513
|
|
|
|
514
|
|
|
foreach ($provides as $provide) { |
515
|
|
|
$report[] = [ |
516
|
|
|
'type' => 'provides', |
517
|
|
|
'dep' => $provide, |
518
|
|
|
'status' => true, |
519
|
|
|
'value' => '' |
520
|
|
|
]; |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
return $report; |
524
|
|
|
} |
525
|
|
|
|
526
|
3 |
|
return true; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
/** |
530
|
|
|
* Checks if $plugins meets the requirement by $dep. |
531
|
|
|
* |
532
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
533
|
|
|
* @param array $plugins A list of plugins as returned by elgg_get_plugins(); |
534
|
|
|
* @param bool $inverse Inverse the results to use as a conflicts. |
535
|
|
|
* |
536
|
|
|
* @return bool |
537
|
|
|
*/ |
538
|
|
|
private function checkDepPlugin(array $dep, array $plugins, $inverse = false) { |
539
|
|
|
$r = _elgg_services()->plugins->checkProvides('plugin', $dep['name'], $dep['version'], $dep['comparison']); |
540
|
|
|
|
541
|
|
|
if ($inverse) { |
542
|
|
|
$r['status'] = !$r['status']; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
return $r; |
|
|
|
|
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* Checks if $plugins meets the requirement by $dep. |
550
|
|
|
* |
551
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
552
|
|
|
* @param array $plugins A list of plugins as returned by elgg_get_plugins(); |
553
|
|
|
* @param bool $inverse Inverse the results to use as a conflicts. |
554
|
|
|
* |
555
|
|
|
* @return bool |
556
|
|
|
*/ |
557
|
|
|
private function checkDepPriority(array $dep, array $plugins, $inverse = false) { |
558
|
|
|
// grab the \ElggPlugin using this package. |
559
|
|
|
$plugin_package = elgg_get_plugin_from_id($this->getID()); |
560
|
|
|
if (!$plugin_package) { |
561
|
|
|
return [ |
|
|
|
|
562
|
|
|
'status' => true, |
563
|
|
|
'value' => 'uninstalled' |
564
|
|
|
]; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
$test_plugin = elgg_get_plugin_from_id($dep['plugin']); |
568
|
|
|
|
569
|
|
|
// If this isn't a plugin or the plugin isn't installed or active |
570
|
|
|
// priority doesn't matter. Use requires to check if a plugin is active. |
571
|
|
|
if (!$test_plugin || !$test_plugin->isActive()) { |
572
|
|
|
return [ |
|
|
|
|
573
|
|
|
'status' => true, |
574
|
|
|
'value' => 'uninstalled' |
575
|
|
|
]; |
576
|
|
|
} |
577
|
|
|
|
578
|
|
|
$plugin_priority = $plugin_package->getPriority(); |
579
|
|
|
$test_plugin_priority = $test_plugin->getPriority(); |
580
|
|
|
|
581
|
|
|
switch ($dep['priority']) { |
582
|
|
|
case 'before': |
583
|
|
|
$status = $plugin_priority < $test_plugin_priority; |
584
|
|
|
break; |
585
|
|
|
|
586
|
|
|
case 'after': |
587
|
|
|
$status = $plugin_priority > $test_plugin_priority; |
588
|
|
|
break; |
589
|
|
|
|
590
|
|
|
default; |
591
|
|
|
$status = false; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
// get the current value |
595
|
|
|
if ($plugin_priority < $test_plugin_priority) { |
596
|
|
|
$value = 'before'; |
597
|
|
|
} else { |
598
|
|
|
$value = 'after'; |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
if ($inverse) { |
602
|
|
|
$status = !$status; |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
return [ |
|
|
|
|
606
|
|
|
'status' => $status, |
607
|
|
|
'value' => $value |
608
|
|
|
]; |
609
|
|
|
} |
610
|
|
|
|
611
|
|
|
/** |
612
|
|
|
* Checks if $elgg_version meets the requirement by $dep. |
613
|
|
|
* |
614
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
615
|
|
|
* @param array $elgg_version An Elgg version (either YYYYMMDDXX or X.Y.Z) |
616
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
617
|
|
|
* |
618
|
|
|
* @return bool |
619
|
|
|
*/ |
620
|
3 |
|
private function checkDepElgg(array $dep, $elgg_version, $inverse = false) { |
621
|
3 |
|
$status = version_compare($elgg_version, $dep['version'], $dep['comparison']); |
|
|
|
|
622
|
|
|
|
623
|
3 |
|
if ($inverse) { |
624
|
|
|
$status = !$status; |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
return [ |
|
|
|
|
628
|
3 |
|
'status' => $status, |
629
|
3 |
|
'value' => $elgg_version |
630
|
|
|
]; |
631
|
|
|
} |
632
|
|
|
|
633
|
|
|
/** |
634
|
|
|
* Checks if $php_version meets the requirement by $dep. |
635
|
|
|
* |
636
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
637
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
638
|
|
|
* |
639
|
|
|
* @return bool |
640
|
|
|
*/ |
641
|
|
|
private function checkDepPhpVersion(array $dep, $inverse = false) { |
642
|
|
|
$php_version = phpversion(); |
643
|
|
|
$status = version_compare($php_version, $dep['version'], $dep['comparison']); |
644
|
|
|
|
645
|
|
|
if ($inverse) { |
646
|
|
|
$status = !$status; |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
return [ |
|
|
|
|
650
|
|
|
'status' => $status, |
651
|
|
|
'value' => $php_version |
652
|
|
|
]; |
653
|
|
|
} |
654
|
|
|
|
655
|
|
|
/** |
656
|
|
|
* Checks if the PHP extension in $dep is loaded. |
657
|
|
|
* |
658
|
|
|
* @todo Can this be merged with the plugin checker? |
659
|
|
|
* |
660
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
661
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
662
|
|
|
* |
663
|
|
|
* @return array An array in the form array( |
664
|
|
|
* 'status' => bool |
665
|
|
|
* 'value' => string The version provided |
666
|
|
|
* ) |
667
|
|
|
*/ |
668
|
|
|
private function checkDepPhpExtension(array $dep, $inverse = false) { |
669
|
|
|
$name = $dep['name']; |
670
|
|
|
$version = $dep['version']; |
671
|
|
|
$comparison = $dep['comparison']; |
672
|
|
|
|
673
|
|
|
// not enabled. |
674
|
|
|
$status = extension_loaded($name); |
675
|
|
|
|
676
|
|
|
// enabled. check version. |
677
|
|
|
$ext_version = phpversion($name); |
678
|
|
|
|
679
|
|
|
if ($status) { |
680
|
|
|
// some extensions (like gd) don't provide versions. neat. |
681
|
|
|
// don't check version info and return a lie. |
682
|
|
|
if ($ext_version && $version) { |
683
|
|
|
$status = version_compare($ext_version, $version, $comparison); |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
if (!$ext_version) { |
687
|
|
|
$ext_version = '???'; |
688
|
|
|
} |
689
|
|
|
} |
690
|
|
|
|
691
|
|
|
// some php extensions can be emulated, so check provides. |
692
|
|
|
if ($status == false) { |
693
|
|
|
$provides = _elgg_services()->plugins->checkProvides('php_extension', $name, $version, $comparison); |
694
|
|
|
$status = $provides['status']; |
695
|
|
|
$ext_version = $provides['value']; |
696
|
|
|
} |
697
|
|
|
|
698
|
|
|
if ($inverse) { |
699
|
|
|
$status = !$status; |
700
|
|
|
} |
701
|
|
|
|
702
|
|
|
return [ |
703
|
|
|
'status' => $status, |
704
|
|
|
'value' => $ext_version |
705
|
|
|
]; |
706
|
|
|
} |
707
|
|
|
|
708
|
|
|
/** |
709
|
|
|
* Check if the PHP ini setting satisfies $dep. |
710
|
|
|
* |
711
|
|
|
* @param array $dep An Elgg manifest.xml deps array |
712
|
|
|
* @param bool $inverse Inverse the result to use as a conflicts. |
713
|
|
|
* |
714
|
|
|
* @return bool |
715
|
|
|
*/ |
716
|
|
|
private function checkDepPhpIni($dep, $inverse = false) { |
717
|
|
|
$name = $dep['name']; |
718
|
|
|
$value = $dep['value']; |
719
|
|
|
$comparison = $dep['comparison']; |
720
|
|
|
|
721
|
|
|
// ini_get() normalizes truthy values to 1 but falsey values to 0 or ''. |
722
|
|
|
// version_compare() considers '' < 0, so normalize '' to 0. |
723
|
|
|
// \ElggPluginManifest normalizes all bool values and '' to 1 or 0. |
724
|
|
|
$setting = ini_get($name); |
725
|
|
|
|
726
|
|
|
if ($setting === '') { |
727
|
|
|
$setting = 0; |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
$status = version_compare($setting, $value, $comparison); |
731
|
|
|
|
732
|
|
|
if ($inverse) { |
733
|
|
|
$status = !$status; |
734
|
|
|
} |
735
|
|
|
|
736
|
|
|
return [ |
737
|
|
|
'status' => $status, |
738
|
|
|
'value' => $setting |
739
|
|
|
]; |
740
|
|
|
} |
741
|
|
|
|
742
|
|
|
/** |
743
|
|
|
* Returns the Plugin ID |
744
|
|
|
* |
745
|
|
|
* @return string |
746
|
|
|
*/ |
747
|
5 |
|
public function getID() { |
748
|
5 |
|
return $this->id; |
749
|
|
|
} |
750
|
|
|
|
751
|
|
|
/** |
752
|
|
|
* Returns the last error message. |
753
|
|
|
* |
754
|
|
|
* @return string |
755
|
|
|
*/ |
756
|
|
|
public function getError() { |
757
|
|
|
return $this->errorMsg; |
758
|
|
|
} |
759
|
|
|
} |
760
|
|
|
|