plugins.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. <?php
  2. /**
  3. * Elgg plugins library
  4. * Contains functions for managing plugins
  5. *
  6. * @package Elgg.Core
  7. * @subpackage Plugins
  8. */
  9. /**
  10. * Tells \ElggPlugin::start() to include the start.php file.
  11. */
  12. define('ELGG_PLUGIN_INCLUDE_START', 1);
  13. /**
  14. * Tells \ElggPlugin::start() to automatically register the plugin's views.
  15. */
  16. define('ELGG_PLUGIN_REGISTER_VIEWS', 2);
  17. /**
  18. * Tells \ElggPlugin::start() to automatically register the plugin's languages.
  19. */
  20. define('ELGG_PLUGIN_REGISTER_LANGUAGES', 4);
  21. /**
  22. * Tells \ElggPlugin::start() to automatically register the plugin's classes.
  23. */
  24. define('ELGG_PLUGIN_REGISTER_CLASSES', 8);
  25. /**
  26. * Prefix for plugin setting names
  27. *
  28. * @todo Can't namespace these because many plugins directly call
  29. * private settings via $entity->$name.
  30. */
  31. //define('ELGG_PLUGIN_SETTING_PREFIX', 'plugin:setting:');
  32. /**
  33. * Prefix for plugin user setting names
  34. */
  35. define('ELGG_PLUGIN_USER_SETTING_PREFIX', 'plugin:user_setting:');
  36. /**
  37. * Internal settings prefix
  38. *
  39. * @todo This could be resolved by promoting \ElggPlugin to a 5th type.
  40. */
  41. define('ELGG_PLUGIN_INTERNAL_PREFIX', 'elgg:internal:');
  42. /**
  43. * Returns a list of plugin directory names from a base directory.
  44. *
  45. * @param string $dir A dir to scan for plugins. Defaults to config's plugins_path.
  46. * Must have a trailing slash.
  47. *
  48. * @return array Array of directory names (not full paths)
  49. * @since 1.8.0
  50. * @access private
  51. */
  52. function _elgg_get_plugin_dirs_in_dir($dir = null) {
  53. return _elgg_services()->plugins->getDirsInDir($dir);
  54. }
  55. /**
  56. * Discovers plugins in the plugins_path setting and creates \ElggPlugin
  57. * entities for them if they don't exist. If there are plugins with entities
  58. * but not actual files, will disable the \ElggPlugin entities and mark as inactive.
  59. * The \ElggPlugin object holds config data, so don't delete.
  60. *
  61. * @return bool
  62. * @since 1.8.0
  63. * @access private
  64. */
  65. function _elgg_generate_plugin_entities() {
  66. return _elgg_services()->plugins->generateEntities();
  67. }
  68. /**
  69. * Cache a reference to this plugin by its ID
  70. *
  71. * @param \ElggPlugin $plugin
  72. *
  73. * @access private
  74. */
  75. function _elgg_cache_plugin_by_id(\ElggPlugin $plugin) {
  76. return _elgg_services()->plugins->cache($plugin);
  77. }
  78. /**
  79. * Returns an \ElggPlugin object with the path $path.
  80. *
  81. * @param string $plugin_id The id (dir name) of the plugin. NOT the guid.
  82. * @return \ElggPlugin|null
  83. * @since 1.8.0
  84. */
  85. function elgg_get_plugin_from_id($plugin_id) {
  86. return _elgg_services()->plugins->get($plugin_id);
  87. }
  88. /**
  89. * Returns if a plugin exists in the system.
  90. *
  91. * @warning This checks only plugins that are registered in the system!
  92. * If the plugin cache is outdated, be sure to regenerate it with
  93. * {@link _elgg_generate_plugin_objects()} first.
  94. *
  95. * @param string $id The plugin ID.
  96. * @since 1.8.0
  97. * @return bool
  98. */
  99. function elgg_plugin_exists($id) {
  100. return _elgg_services()->plugins->exists($id);
  101. }
  102. /**
  103. * Returns the highest priority of the plugins
  104. *
  105. * @return int
  106. * @since 1.8.0
  107. * @access private
  108. */
  109. function _elgg_get_max_plugin_priority() {
  110. return _elgg_services()->plugins->getMaxPriority();
  111. }
  112. /**
  113. * Returns if a plugin is active for a current site.
  114. *
  115. * @param string $plugin_id The plugin ID
  116. * @param int $site_guid The site guid
  117. * @since 1.8.0
  118. * @return bool
  119. */
  120. function elgg_is_active_plugin($plugin_id, $site_guid = null) {
  121. return _elgg_services()->plugins->isActive($plugin_id, $site_guid);
  122. }
  123. /**
  124. * Loads all active plugins in the order specified in the tool admin panel.
  125. *
  126. * @note This is called on every page load. If a plugin is active and problematic, it
  127. * will be disabled and a visible error emitted. This does not check the deps system because
  128. * that was too slow.
  129. *
  130. * @return bool
  131. * @since 1.8.0
  132. * @access private
  133. */
  134. function _elgg_load_plugins() {
  135. return _elgg_services()->plugins->load();
  136. }
  137. /**
  138. * Returns an ordered list of plugins
  139. *
  140. * @param string $status The status of the plugins. active, inactive, or all.
  141. * @param mixed $site_guid Optional site guid
  142. * @return \ElggPlugin[]
  143. * @since 1.8.0
  144. */
  145. function elgg_get_plugins($status = 'active', $site_guid = null) {
  146. return _elgg_services()->plugins->find($status, $site_guid);
  147. }
  148. /**
  149. * Reorder plugins to an order specified by the array.
  150. * Plugins not included in this array will be appended to the end.
  151. *
  152. * @note This doesn't use the \ElggPlugin->setPriority() method because
  153. * all plugins are being changed and we don't want it to automatically
  154. * reorder plugins.
  155. *
  156. * @param array $order An array of plugin ids in the order to set them
  157. * @return bool
  158. * @since 1.8.0
  159. * @access private
  160. */
  161. function _elgg_set_plugin_priorities(array $order) {
  162. return _elgg_services()->plugins->setPriorities($order);
  163. }
  164. /**
  165. * Reindexes all plugin priorities starting at 1.
  166. *
  167. * @todo Can this be done in a single sql command?
  168. * @return bool
  169. * @since 1.8.0
  170. * @access private
  171. */
  172. function _elgg_reindex_plugin_priorities() {
  173. return _elgg_services()->plugins->reindexPriorities();
  174. }
  175. /**
  176. * Namespaces a string to be used as a private setting name for a plugin.
  177. *
  178. * For user_settings, two namespaces are added: a user setting namespace and the
  179. * plugin id.
  180. *
  181. * For internal (plugin priority), there is a single internal namespace added.
  182. *
  183. * @param string $type The type of setting: user_setting or internal.
  184. * @param string $name The name to namespace.
  185. * @param string $id The plugin's ID to namespace with. Required for user_setting.
  186. * @return string
  187. * @since 1.8.0
  188. * @access private
  189. */
  190. function _elgg_namespace_plugin_private_setting($type, $name, $id = null) {
  191. return _elgg_services()->plugins->namespacePrivateSetting($type, $name, $id);
  192. }
  193. /**
  194. * Returns an array of all provides from all active plugins.
  195. *
  196. * Array in the form array(
  197. * 'provide_type' => array(
  198. * 'provided_name' => array(
  199. * 'version' => '1.8',
  200. * 'provided_by' => 'provider_plugin_id'
  201. * )
  202. * )
  203. * )
  204. *
  205. * @param string $type The type of provides to return
  206. * @param string $name A specific provided name to return. Requires $provide_type.
  207. *
  208. * @return array
  209. * @since 1.8.0
  210. * @access private
  211. */
  212. function _elgg_get_plugins_provides($type = null, $name = null) {
  213. return _elgg_services()->plugins->getProvides($type, $name);
  214. }
  215. /**
  216. * Deletes all cached data on plugins being provided.
  217. *
  218. * @return boolean
  219. * @since 1.9.0
  220. * @access private
  221. */
  222. function _elgg_invalidate_plugins_provides_cache() {
  223. return _elgg_services()->plugins->invalidateProvidesCache();
  224. }
  225. /**
  226. * Checks if a plugin is currently providing $type and $name, and optionally
  227. * checking a version.
  228. *
  229. * @param string $type The type of the provide
  230. * @param string $name The name of the provide
  231. * @param string $version A version to check against
  232. * @param string $comparison The comparison operator to use in version_compare()
  233. *
  234. * @return array An array in the form array(
  235. * 'status' => bool Does the provide exist?,
  236. * 'value' => string The version provided
  237. * )
  238. * @since 1.8.0
  239. * @access private
  240. */
  241. function _elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') {
  242. return _elgg_services()->plugins->checkProvides($type, $name, $version, $comparison);
  243. }
  244. /**
  245. * Returns an array of parsed strings for a dependency in the
  246. * format: array(
  247. * 'type' => requires, conflicts, or provides.
  248. * 'name' => The name of the requirement / conflict
  249. * 'value' => A string representing the expected value: <1, >=3, !=enabled
  250. * 'local_value' => The current value, ("Not installed")
  251. * 'comment' => Free form text to help resovle the problem ("Enable / Search for plugin <link>")
  252. * )
  253. *
  254. * @param array $dep An \ElggPluginPackage dependency array
  255. * @return array
  256. * @since 1.8.0
  257. * @access private
  258. */
  259. function _elgg_get_plugin_dependency_strings($dep) {
  260. return _elgg_services()->plugins->getDependencyStrings($dep);
  261. }
  262. /**
  263. * Returns an array of all plugin user settings for a user.
  264. *
  265. * @param int $user_guid The user GUID or 0 for the currently logged in user.
  266. * @param string $plugin_id The plugin ID (Required)
  267. * @param bool $return_obj Return settings as an object? This can be used to in reusable
  268. * views where the settings are passed as $vars['entity'].
  269. * @return array
  270. * @since 1.8.0
  271. * @see \ElggPlugin::getAllUserSettings()
  272. */
  273. function elgg_get_all_plugin_user_settings($user_guid = 0, $plugin_id = null, $return_obj = false) {
  274. return _elgg_services()->plugins->getAllUserSettings($user_guid, $plugin_id, $return_obj);
  275. }
  276. /**
  277. * Set a user specific setting for a plugin.
  278. *
  279. * @param string $name The name. Note: cannot be "title".
  280. * @param mixed $value The value.
  281. * @param int $user_guid The user GUID or 0 for the currently logged in user.
  282. * @param string $plugin_id The plugin ID (Required)
  283. *
  284. * @return bool
  285. * @since 1.8.0
  286. * @see \ElggPlugin::setUserSetting()
  287. */
  288. function elgg_set_plugin_user_setting($name, $value, $user_guid = 0, $plugin_id = null) {
  289. return _elgg_services()->plugins->setUserSetting($name, $value, $user_guid, $plugin_id);
  290. }
  291. /**
  292. * Unsets a user-specific plugin setting
  293. *
  294. * @param string $name Name of the setting
  295. * @param int $user_guid The user GUID or 0 for the currently logged in user.
  296. * @param string $plugin_id The plugin ID (Required)
  297. *
  298. * @return bool
  299. * @since 1.8.0
  300. * @see \ElggPlugin::unsetUserSetting()
  301. */
  302. function elgg_unset_plugin_user_setting($name, $user_guid = 0, $plugin_id = null) {
  303. return _elgg_services()->plugins->unsetUserSetting($name, $user_guid, $plugin_id);
  304. }
  305. /**
  306. * Get a user specific setting for a plugin.
  307. *
  308. * @param string $name The name of the setting.
  309. * @param int $user_guid The user GUID or 0 for the currently logged in user.
  310. * @param string $plugin_id The plugin ID (Required)
  311. * @param mixed $default The default value to return if none is set
  312. *
  313. * @return mixed
  314. * @since 1.8.0
  315. * @see \ElggPlugin::getUserSetting()
  316. */
  317. function elgg_get_plugin_user_setting($name, $user_guid = 0, $plugin_id = null, $default = null) {
  318. return _elgg_services()->plugins->getUserSetting($name, $user_guid, $plugin_id, $default);
  319. }
  320. /**
  321. * Set a setting for a plugin.
  322. *
  323. * @param string $name The name of the setting - note, can't be "title".
  324. * @param mixed $value The value.
  325. * @param string $plugin_id The plugin ID (Required)
  326. *
  327. * @return bool
  328. * @since 1.8.0
  329. * @see \ElggPlugin::setSetting()
  330. */
  331. function elgg_set_plugin_setting($name, $value, $plugin_id = null) {
  332. return _elgg_services()->plugins->setSetting($name, $value, $plugin_id);
  333. }
  334. /**
  335. * Get setting for a plugin.
  336. *
  337. * @param string $name The name of the setting.
  338. * @param string $plugin_id The plugin ID (Required)
  339. * @param mixed $default The default value to return if none is set
  340. *
  341. * @return mixed
  342. * @since 1.8.0
  343. * @see \ElggPlugin::getSetting()
  344. */
  345. function elgg_get_plugin_setting($name, $plugin_id = null, $default = null) {
  346. return _elgg_services()->plugins->getSetting($name, $plugin_id, $default);
  347. }
  348. /**
  349. * Unsets a plugin setting.
  350. *
  351. * @param string $name The name of the setting.
  352. * @param string $plugin_id The plugin ID (Required)
  353. *
  354. * @return bool
  355. * @since 1.8.0
  356. * @see \ElggPlugin::unsetSetting()
  357. */
  358. function elgg_unset_plugin_setting($name, $plugin_id = null) {
  359. return _elgg_services()->plugins->unsetSetting($name, $plugin_id);
  360. }
  361. /**
  362. * Unsets all plugin settings for a plugin.
  363. *
  364. * @param string $plugin_id The plugin ID (Required)
  365. *
  366. * @return bool
  367. * @since 1.8.0
  368. * @see \ElggPlugin::unsetAllSettings()
  369. */
  370. function elgg_unset_all_plugin_settings($plugin_id = null) {
  371. return _elgg_services()->plugins->unsetAllSettings($plugin_id);
  372. }
  373. /**
  374. * Returns entities based upon plugin user settings.
  375. * Takes all the options for {@link elgg_get_entities_from_private_settings()}
  376. * in addition to the ones below.
  377. *
  378. * @param array $options Array in the format:
  379. *
  380. * plugin_id => STR The plugin id. Required.
  381. *
  382. * plugin_user_setting_names => null|ARR private setting names
  383. *
  384. * plugin_user_setting_values => null|ARR metadata values
  385. *
  386. * plugin_user_setting_name_value_pairs => null|ARR (
  387. * name => 'name',
  388. * value => 'value',
  389. * 'operand' => '=',
  390. * )
  391. * Currently if multiple values are sent via
  392. * an array (value => array('value1', 'value2')
  393. * the pair's operand will be forced to "IN".
  394. *
  395. * plugin_user_setting_name_value_pairs_operator => null|STR The operator to use for combining
  396. * (name = value) OPERATOR (name = value); default AND
  397. *
  398. * @return mixed int If count, int. If not count, array. false on errors.
  399. * @since 1.8.0
  400. */
  401. function elgg_get_entities_from_plugin_user_settings(array $options = array()) {
  402. return _elgg_services()->plugins->getEntitiesFromUserSettings($options);
  403. }
  404. /**
  405. * Runs unit tests for plugin API.
  406. *
  407. * @param string $hook unit_test
  408. * @param string $type system
  409. * @param mixed $value Array of tests
  410. * @param mixed $params Params
  411. *
  412. * @return array
  413. * @access private
  414. */
  415. function _elgg_plugins_test($hook, $type, $value, $params) {
  416. global $CONFIG;
  417. $value[] = $CONFIG->path . 'engine/tests/ElggCorePluginsAPITest.php';
  418. return $value;
  419. }
  420. /**
  421. * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case.
  422. *
  423. * @param string $event deactivate
  424. * @param string $type plugin
  425. * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key
  426. * @return bool false to block plugin deactivation action
  427. *
  428. * @access private
  429. */
  430. function _plugins_deactivate_dependency_check($event, $type, $params) {
  431. $plugin_id = $params['plugin_entity']->getManifest()->getPluginID();
  432. $plugin_name = $params['plugin_entity']->getManifest()->getName();
  433. $active_plugins = elgg_get_plugins();
  434. $dependents = array();
  435. foreach ($active_plugins as $plugin) {
  436. $manifest = $plugin->getManifest();
  437. $requires = $manifest->getRequires();
  438. foreach ($requires as $required) {
  439. if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) {
  440. // there are active dependents
  441. $dependents[$manifest->getPluginID()] = $plugin;
  442. }
  443. }
  444. }
  445. if ($dependents) {
  446. $list = '<ul>';
  447. // construct error message and prevent disabling
  448. foreach ($dependents as $dependent) {
  449. $list .= '<li>' . $dependent->getManifest()->getName() . '</li>';
  450. }
  451. $list .= '</ul>';
  452. register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list)));
  453. return false;
  454. }
  455. }
  456. /**
  457. * Initialize the plugin system
  458. *
  459. * @return void
  460. * @access private
  461. */
  462. function _elgg_plugins_init() {
  463. if (elgg_is_admin_logged_in()) {
  464. elgg_register_ajax_view('object/plugin/full');
  465. }
  466. elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_plugins_test');
  467. // note - plugins are booted by the time this handler is registered
  468. // deactivation due to error may have already occurred
  469. elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check');
  470. /**
  471. * @see \Elgg\Database\Plugins::invalidateIsActiveCache
  472. */
  473. $svc = _elgg_services()->plugins;
  474. elgg_register_event_handler('deactivate', 'plugin', array($svc, 'invalidateIsActiveCache'));
  475. elgg_register_event_handler('activate', 'plugin', array($svc, 'invalidateIsActiveCache'));
  476. elgg_register_action("plugins/settings/save", '', 'admin');
  477. elgg_register_action("plugins/usersettings/save");
  478. elgg_register_action('admin/plugins/activate', '', 'admin');
  479. elgg_register_action('admin/plugins/deactivate', '', 'admin');
  480. elgg_register_action('admin/plugins/activate_all', '', 'admin');
  481. elgg_register_action('admin/plugins/deactivate_all', '', 'admin');
  482. elgg_register_action('admin/plugins/set_priority', '', 'admin');
  483. elgg_register_library('elgg:markdown', elgg_get_root_path() . 'vendors/markdown/markdown.php');
  484. }
  485. return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
  486. $events->registerHandler('init', 'system', '_elgg_plugins_init');
  487. };