PageRenderTime 54ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/broken-link-checker/includes/module-manager.php

https://bitbucket.org/lgorence/quickpress
PHP | 855 lines | 465 code | 109 blank | 281 comment | 98 complexity | 761a497ef6cbf84f8256184695c5b14c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. class blcModuleManager {
  3. /* @var blcConfigurationManager */
  4. var $plugin_conf;
  5. var $module_dir = '';
  6. var $_module_cache;
  7. var $_category_cache;
  8. var $_category_cache_active;
  9. var $_virtual_modules = array();
  10. var $loaded;
  11. var $instances;
  12. var $default_active_modules;
  13. /**
  14. * Class "constructor".
  15. *
  16. * @param array $default_active_modules An array of module ids specifying which modules are active by default.
  17. * @return void
  18. */
  19. function init($default_active_modules = null){
  20. $this->module_dir = realpath(dirname(__FILE__) . '/../modules');
  21. $this->plugin_conf = blc_get_configuration();
  22. $this->default_active_modules = $default_active_modules;
  23. $this->loaded = array();
  24. $this->instances = array();
  25. add_filter('extra_plugin_headers', array(&$this, 'inject_module_headers'));
  26. }
  27. /**
  28. * Get an instance of the module manager.
  29. *
  30. * @param array|null $default_active_modules
  31. * @return blcModuleManager
  32. */
  33. static function getInstance($default_active_modules = null){
  34. static $instance = null;
  35. if ( is_null($instance) ){
  36. $instance = new blcModuleManager();
  37. $instance->init($default_active_modules);
  38. }
  39. return $instance;
  40. }
  41. /**
  42. * Retrieve a list of all installed BLC modules.
  43. *
  44. * This is essentially a slightly modified copy of get_plugins().
  45. *
  46. * @return array An associative array of module headers indexed by module ID.
  47. */
  48. function get_modules(){
  49. if ( !isset($this->_module_cache) ){
  50. //Refresh the module cache.
  51. $relative_path = '/' . plugin_basename($this->module_dir);
  52. if ( !function_exists('get_plugins') ){
  53. //BUG: Potentional security flaw/bug. plugin.php is not meant to be loaded outside admin panel.
  54. require_once(ABSPATH . 'wp-admin/includes/plugin.php');
  55. }
  56. $modules = get_plugins( $relative_path );
  57. $this->_module_cache = array();
  58. foreach($modules as $module_filename => $module_header){
  59. //Figure out the module ID. If not specified, it is equal to module's filename (sans the .php)
  60. if ( !empty($module_header['ModuleID']) ){
  61. $module_id = strtolower(trim($module_header['ModuleID']));
  62. } else {
  63. $module_id = strtolower(basename($module_filename, '.php'));
  64. }
  65. $module_header = $this->normalize_module_header($module_header, $module_id, $module_filename);
  66. $this->_module_cache[$module_id] = $module_header;
  67. }
  68. $this->_category_cache = null;
  69. }
  70. return array_merge($this->_module_cache, $this->_virtual_modules);
  71. }
  72. /**
  73. * Retrieve modules that match a specific category, or all modules sorted by categories.
  74. *
  75. * If a category ID is specified, this method returns the modules that have the "ModuleCategory:"
  76. * file header set to that value, or an empty array if no modules match that category. The
  77. * return array is indexed by module id :
  78. * [module_id1 => module1_data, module_id1 => module2_data, ...]
  79. *
  80. * If category is omitted, this method returns a list of all categories plus the modules
  81. * they contain. Only categories that have at least one module will be included. The return
  82. * value is an array of arrays, indexed by category ID :
  83. * [category1 => [module1_id => module1_data, module2_id => module2_data, ...], ...]
  84. *
  85. *
  86. * @param string $category Category id, e.g. "parser" or "container". Optional.
  87. * @param bool $markup Apply markup to module headers. Not implemented.
  88. * @param bool $translate Translate module headers. Defaults to false.
  89. * @return array An array of categories or module data.
  90. */
  91. function get_modules_by_category($category = '', $markup = false, $translate = false){
  92. if ( !isset($this->_category_cache) ){
  93. $this->_category_cache = $this->sort_into_categories($this->get_modules());
  94. }
  95. if ( empty($category) ){
  96. if ( $markup || $translate ){
  97. //Translate/apply markup to module headers
  98. $processed = array();
  99. foreach($this->_category_cache as $category_id => $modules){
  100. $processed[$category_id] = array();
  101. foreach($modules as $module_id => $module_data){
  102. if ( $translate ){
  103. $module_data['Name'] = _x($module_data['Name'], 'module name', 'broken-link-checker');
  104. }
  105. $processed[$category_id][$module_id] = $module_data;
  106. }
  107. }
  108. return $processed;
  109. } else {
  110. return $this->_category_cache;
  111. }
  112. } else {
  113. if ( isset($this->_category_cache[$category]) ){
  114. if ( $markup || $translate ){
  115. //Translate/apply markup to module headers
  116. $processed = array();
  117. foreach($this->_category_cache[$category] as $module_id => $module_data){
  118. if ( $translate ){
  119. $module_data['Name'] = _x($module_data['Name'], 'module name', 'broken-link-checker');
  120. }
  121. $processed[$module_id] = $module_data;
  122. }
  123. return $processed;
  124. } else {
  125. return $this->_category_cache[$category];
  126. }
  127. } else {
  128. return array();
  129. }
  130. }
  131. }
  132. /**
  133. * Retrieve active modules that match a specific category, or all active modules sorted by categories.
  134. *
  135. * @see blcModuleManager::get_modules_by_category()
  136. *
  137. * @param string $category Category id. Optional.
  138. * @return array An associative array of categories or module data.
  139. */
  140. function get_active_by_category($category = ''){
  141. if ( !isset($this->_category_cache_active) ){
  142. $this->_category_cache_active = $this->sort_into_categories($this->get_active_modules());
  143. }
  144. if ( empty($category) ){
  145. return $this->_category_cache_active;
  146. } else {
  147. if ( isset($this->_category_cache_active[$category]) ){
  148. return $this->_category_cache_active[$category];
  149. } else {
  150. return array();
  151. }
  152. }
  153. }
  154. /**
  155. * Get the module ids of all active modules that belong to a specific category,
  156. * quoted and ready for use in SQL.
  157. *
  158. * @param string $category Category ID. If not specified, a list of all active modules will be returned.
  159. * @return string A comma separated list of single-quoted module ids, e.g. 'module-foo','module-bar','modbaz'
  160. */
  161. function get_escaped_ids($category = ''){
  162. global $wpdb;
  163. if ( empty($category) ){
  164. $modules = $this->get_active_modules();
  165. } else {
  166. $modules = $this->get_active_by_category($category);
  167. }
  168. $modules = array_map(array(&$wpdb, 'escape'), array_keys($modules));
  169. $modules = "'" . implode("', '", $modules) . "'";
  170. return $modules;
  171. }
  172. /**
  173. * Sort a list of modules into categories. Inside each category, modules are sorted by priority (descending).
  174. *
  175. * @access private
  176. *
  177. * @param array $modules
  178. * @return array
  179. */
  180. function sort_into_categories($modules){
  181. $categories = array();
  182. foreach($modules as $module_id => $module_data){
  183. $cat = $module_data['ModuleCategory'];
  184. if ( isset($categories[$cat]) ){
  185. $categories[$cat][$module_id] = $module_data;
  186. } else {
  187. $categories[$cat] = array($module_id => $module_data);
  188. }
  189. }
  190. foreach($categories as $cat => $cat_modules){
  191. uasort($categories[$cat], array(&$this, 'compare_priorities'));
  192. }
  193. return $categories;
  194. }
  195. /**
  196. * Callback for sorting modules by priority.
  197. *
  198. * @access private
  199. *
  200. * @param array $a First module header.
  201. * @param array $b Second module header.
  202. * @return int
  203. */
  204. function compare_priorities($a, $b){
  205. return $b['ModulePriority'] - $a['ModulePriority'];
  206. }
  207. /**
  208. * Retrieve a reference to an active module.
  209. *
  210. * Each module is instantiated only once, so if the module was already loaded you'll get back
  211. * a reference to the existing module object. If the module isn't loaded or instantiated yet,
  212. * the function will do it automatically (but only for active modules).
  213. *
  214. * @param string $module_id Module ID.
  215. * @param bool $autoload Optional. Whether to load the module file if it's not loaded yet. Defaults to TRUE.
  216. * @param string $category Optional. Return the module only if it's in a specific category. Categories are ignored by default.
  217. * @return blcModule A reference to a module object, or NULL on error.
  218. */
  219. function get_module($module_id, $autoload = true, $category=''){
  220. $no_result = null;
  221. if ( !is_string($module_id) ){
  222. //$backtrace = debug_backtrace();
  223. //FB::error($backtrace, "get_module called with a non-string argument");
  224. return $no_result;
  225. }
  226. if ( empty($this->loaded[$module_id]) ){
  227. if ( $autoload && $this->is_active($module_id) ){
  228. if ( !$this->load_module($module_id) ){
  229. return $no_result;
  230. }
  231. } else {
  232. return $no_result;
  233. }
  234. }
  235. if ( !empty($category) ){
  236. $data = $this->get_module_data($module_id);
  237. if ( $data['ModuleCategory'] != $category ){
  238. return $no_result;
  239. }
  240. }
  241. $module = $this->init_module($module_id);
  242. return $module;
  243. }
  244. /**
  245. * Retrieve the header data of a specific module.
  246. * Uses cached module info if available.
  247. *
  248. * @param string $module_id
  249. * @param bool $use_active_cache Check the active module cache before the general one. Defaults to true.
  250. * @return array Associative array of module data, or FALSE if the specified module was not found.
  251. */
  252. function get_module_data($module_id, $use_active_cache = true){
  253. //Check virtual modules
  254. if ( isset($this->_virtual_modules[$module_id]) ){
  255. return $this->_virtual_modules[$module_id];
  256. }
  257. //Check active modules
  258. if ( $use_active_cache && isset($this->plugin_conf->options['active_modules'][$module_id]) ){
  259. return $this->plugin_conf->options['active_modules'][$module_id];
  260. }
  261. //Otherwise, use the general module cache
  262. if ( !isset($this->_module_cache) ){
  263. $this->get_modules(); //Populates the cache
  264. }
  265. if ( isset($this->_module_cache[$module_id]) ){
  266. return $this->_module_cache[$module_id];
  267. } else {
  268. return false;
  269. }
  270. }
  271. /**
  272. * Retrieve a list of active modules.
  273. *
  274. * The list of active modules is stored in the 'active_modules' key of the
  275. * plugin configuration object. If this key is not set, this function will
  276. * create it and populate it using the list of default active modules passed
  277. * to the module manager's constructor.
  278. *
  279. * @return array Associative array of module data indexed by module ID.
  280. */
  281. function get_active_modules(){
  282. if ( isset($this->plugin_conf->options['active_modules']) ){
  283. return $this->plugin_conf->options['active_modules'];
  284. }
  285. $active = array();
  286. $modules = $this->get_modules();
  287. if ( is_array($this->default_active_modules) ){
  288. foreach($this->default_active_modules as $module_id){
  289. if ( array_key_exists($module_id, $modules) ){
  290. $active[$module_id] = $modules[$module_id];
  291. }
  292. }
  293. }
  294. $this->plugin_conf->options['active_modules'] = $active;
  295. $this->plugin_conf->save_options();
  296. return $this->plugin_conf->options['active_modules'];
  297. }
  298. /**
  299. * Determine if module is active.
  300. *
  301. * @param string $module_id
  302. * @return bool
  303. */
  304. function is_active($module_id){
  305. return array_key_exists($module_id, $this->get_active_modules());
  306. }
  307. /**
  308. * Activate a module.
  309. * Does nothing if the module is already active.
  310. *
  311. * @param string $module_id
  312. * @return bool True if module was activated successfully, false otherwise.
  313. */
  314. function activate($module_id){
  315. if ( $this->is_active($module_id) ){
  316. return true;
  317. }
  318. //Retrieve the module header
  319. $module_data = $this->get_module_data($module_id, false);
  320. if ( !$module_data ){
  321. return false;
  322. }
  323. //Attempt to load the module
  324. if ( $this->load_module($module_id, $module_data) ){
  325. //Okay, if it loads, we can assume it works.
  326. $this->plugin_conf->options['active_modules'][$module_id] = $module_data;
  327. $this->plugin_conf->save_options();
  328. //Invalidate the per-category active module cache
  329. $this->_category_cache_active = null;
  330. //Notify the module that it's been activated
  331. $module = $this->get_module($module_id);
  332. if ( $module ){
  333. $module->activated();
  334. }
  335. return true;
  336. } else {
  337. return false;
  338. }
  339. }
  340. /**
  341. * Deactivate a module.
  342. * Does nothing if the module is already inactive.
  343. *
  344. * @param string $module_id
  345. * @return bool
  346. */
  347. function deactivate($module_id){
  348. if ( !$this->is_active($module_id) ){
  349. return true;
  350. }
  351. //Some modules are supposed to be always active and thus can't be deactivated
  352. $module_data = $this->get_module_data($module_id, false);
  353. if ( isset($module_data['ModuleAlwaysActive']) && $module_data['ModuleAlwaysActive'] ){
  354. return false;
  355. }
  356. //Notify the module that it's being deactivated
  357. $module = $this->get_module($module_id);
  358. if ( $module ){
  359. $module->deactivated();
  360. }
  361. unset($this->plugin_conf->options['active_modules'][$module_id]);
  362. //Keep track of when each module was last deactivated. Used for parser resynchronization.
  363. if ( isset($this->plugin_conf->options['module_deactivated_when']) ){
  364. $this->plugin_conf->options['module_deactivated_when'][$module_id] = current_time('timestamp');
  365. } else {
  366. $this->plugin_conf->options['module_deactivated_when'] = array(
  367. $module_id => current_time('timestamp'),
  368. );
  369. }
  370. $this->plugin_conf->save_options();
  371. $this->_category_cache_active = null; //Invalidate the by-category cache since we just changed something
  372. return true;
  373. }
  374. /**
  375. * Determine when a module was last deactivated.
  376. *
  377. * @param string $module_id Module ID.
  378. * @return int Timestamp of last deactivation, or 0 if the module has never been deactivated.
  379. */
  380. function get_last_deactivation_time($module_id){
  381. if ( isset($this->plugin_conf->options['module_deactivated_when'][$module_id]) ){
  382. return $this->plugin_conf->options['module_deactivated_when'][$module_id];
  383. } else {
  384. return 0;
  385. }
  386. }
  387. /**
  388. * Set the current list of active modules. If any of the modules are not currently active,
  389. * they will be activated. Any currently active modules that are not on the new list will
  390. * be deactivated.
  391. *
  392. * @param array $ids An array of module IDs.
  393. * @return void
  394. */
  395. function set_active_modules($ids){
  396. $current_active = array_keys($this->get_active_modules());
  397. $activate = array_diff($ids, $current_active);
  398. $deactivate = array_diff($current_active, $ids);
  399. //Deactivate any modules not present in the new list
  400. foreach($deactivate as $module_id){
  401. $this->deactivate($module_id);
  402. }
  403. //Activate modules present in the new list but not in the old list
  404. foreach($activate as $module_id){
  405. $this->activate($module_id);
  406. }
  407. //Ensure all active modules have the latest headers
  408. $this->refresh_active_module_cache();
  409. //Invalidate the per-category active module cache
  410. $this->_category_cache_active = null;
  411. }
  412. /**
  413. * Send the activation message to all currently active plugins when the plugin is activated.
  414. *
  415. * @return void
  416. */
  417. function plugin_activated(){
  418. global $blclog;
  419. //Ensure all active modules have the latest headers
  420. $blclog->log('... Updating module cache');
  421. $this->refresh_active_module_cache();
  422. //Notify them that we've been activated
  423. $active = $this->get_active_modules();
  424. foreach($active as $module_id => $module_data){
  425. $blclog->log( sprintf('... Notifying module "%s"', $module_id) );
  426. $module = $this->get_module($module_id);
  427. if ( $module ){
  428. $module->activated();
  429. } else {
  430. $blclog->warn(sprintf('... Module "%s" failed to load!', $module_id));
  431. }
  432. }
  433. }
  434. /**
  435. * Refresh the cached data of all active modules.
  436. *
  437. * @return array An updated list of active modules.
  438. */
  439. function refresh_active_module_cache(){
  440. $modules = $this->get_modules();
  441. foreach($this->plugin_conf->options['active_modules'] as $module_id => $module_header){
  442. if ( array_key_exists($module_id, $modules) ){
  443. $this->plugin_conf->options['active_modules'][$module_id] = $modules[$module_id];
  444. }
  445. }
  446. $this->plugin_conf->save_options();
  447. $this->_category_cache_active = null; //Invalidate the by-category active module cache
  448. return $this->plugin_conf->options['active_modules'];
  449. }
  450. /**
  451. * Load active modules.
  452. *
  453. * @param string $context Optional. If specified, only the modules that match this context (or the "all" context) will be loaded.
  454. * @return void
  455. */
  456. function load_modules($context = ''){
  457. $active = $this->get_active_modules();
  458. //Avoid trying to load a virtual module before the module that registered it has been loaded.
  459. $active = $this->put_virtual_last($active);
  460. foreach($active as $module_id => $module_data){
  461. //Load the module
  462. $should_load = ($module_data['ModuleContext'] == 'all') || (!empty($context) && $module_data['ModuleContext'] == $context);
  463. if ( $should_load ){
  464. $this->load_module($module_id, $module_data);
  465. }
  466. }
  467. }
  468. /**
  469. * Load and possibly instantiate a specific module.
  470. *
  471. * @access private
  472. *
  473. * @param string $module_id
  474. * @param array $module_data
  475. * @return bool True if the module was successfully loaded, false otherwise.
  476. */
  477. function load_module($module_id, $module_data = null){
  478. //Only load each module once.
  479. if ( !empty($this->loaded[$module_id]) ){
  480. return true;
  481. }
  482. if ( !isset($module_data) ){
  483. $module_data = $this->get_module_data($module_id);
  484. if ( empty($module_data) ){
  485. return false;
  486. }
  487. }
  488. //Load a normal module
  489. if ( empty($module_data['virtual']) ){
  490. //Skip invalid and missing modules
  491. if ( empty($module_data['file']) ){
  492. return false;
  493. }
  494. //Get the full path to the module file
  495. $filename = $this->module_dir . '/' . $module_data['file'];
  496. if ( !file_exists($filename) ){
  497. return false;
  498. }
  499. //Load it
  500. include $filename;
  501. $this->loaded[$module_id] = true;
  502. } else {
  503. //Virtual modules don't need to be explicitly loaded, but they must
  504. //be registered.
  505. if ( !array_key_exists($module_id, $this->_virtual_modules) ) {
  506. return false;
  507. }
  508. $this->loaded[$module_id] = true;
  509. }
  510. //Instantiate the main module class unless lazy init is on (default is off)
  511. if ( !array_key_exists($module_id, $this->instances) ){ //Only try to instantiate once
  512. if ( !$module_data['ModuleLazyInit'] ){
  513. $this->init_module($module_id, $module_data);
  514. }
  515. }
  516. return true;
  517. }
  518. /**
  519. * Instantiate a certain module.
  520. *
  521. * @param string $module_id
  522. * @param array $module_data Optional. The header data of the module that needs to be instantiated. If not specified, it will be retrieved automatically.
  523. * @return object The newly instantiated module object (extends blcModule), or NULL on error.
  524. */
  525. function init_module($module_id, $module_data = null){
  526. //Each module is only instantiated once.
  527. if ( isset($this->instances[$module_id]) ){
  528. return $this->instances[$module_id];
  529. }
  530. if ( !isset($module_data) ){
  531. $module_data = $this->get_module_data($module_id);
  532. if ( empty($module_data) ){
  533. return null;
  534. }
  535. }
  536. if ( !empty($module_data['ModuleClassName']) && class_exists($module_data['ModuleClassName']) ){
  537. $className = $module_data['ModuleClassName'];
  538. $this->instances[$module_id] = new $className(
  539. $module_id,
  540. $module_data,
  541. $this->plugin_conf,
  542. $this
  543. );
  544. return $this->instances[$module_id];
  545. };
  546. return null;
  547. }
  548. function is_instantiated($module_id){
  549. return !empty($this->instances[$module_id]);
  550. }
  551. /**
  552. * Register a virtual module.
  553. *
  554. * Virtual modules are the same as normal modules, except that they can be added
  555. * on the fly, e.g. by other modules.
  556. *
  557. * @param string $module_id Module Id.
  558. * @param string $module_data Associative array of module data. All module header fields are allowed, except ModuleID.
  559. * @return void
  560. */
  561. function register_virtual_module($module_id, $module_data){
  562. $module_data = $this->normalize_module_header($module_data, $module_id);
  563. $module_data['virtual'] = true;
  564. $this->_virtual_modules[$module_id] = $module_data;
  565. }
  566. /**
  567. * Sort an array of modules so that all virtual modules are placed at its end.
  568. *
  569. * @param array $modules Input array, [module_id => module_data, ...].
  570. * @return array Sorted array.
  571. */
  572. function put_virtual_last($modules){
  573. uasort($modules, array(&$this, 'compare_virtual_flags'));
  574. return $modules;
  575. }
  576. /**
  577. * Callback function for sorting modules by the state of their 'virtual' flag.
  578. *
  579. * @param array $a Associative array of module A data
  580. * @param array $b Associative array of module B data
  581. * @return int
  582. */
  583. function compare_virtual_flags($a, $b){
  584. if ( empty($a['virtual']) ){
  585. return empty($b['virtual'])?0:-1;
  586. } else {
  587. return empty($b['virtual'])?1:0;
  588. }
  589. }
  590. /**
  591. * Validate active modules.
  592. *
  593. * Validates all active modules, deactivates invalid ones and returns
  594. * an array of deactivated modules.
  595. *
  596. * @return array
  597. */
  598. function validate_active_modules(){
  599. $active = $this->get_active_modules();
  600. if ( empty($active) ){
  601. return array();
  602. }
  603. $invalid = array();
  604. foreach($active as $module_id => $module_data){
  605. $rez = $this->validate_module($module_data);
  606. if ( is_wp_error($rez) ){
  607. $invalid[$module_id] = $rez;
  608. $this->deactivate($module_id);
  609. }
  610. }
  611. return $invalid;
  612. }
  613. /**
  614. * Validate module data.
  615. *
  616. * Checks that the module file exists or that the module
  617. * is a currently registered virtual module.
  618. *
  619. * @param array $module_data Associative array of module data.
  620. * @return bool|WP_Error True on success, an error object if the module fails validation
  621. */
  622. function validate_module($module_data){
  623. if ( empty($module_data['ModuleID']) ){
  624. return new WP_Error('invalid_cached_header', 'The cached module header is invalid');
  625. }
  626. if ( empty($module_data['virtual']) ){
  627. //Normal modules must have a valid filename
  628. if ( empty($module_data['file']) ){
  629. return new WP_Error('module_not_found', 'Invalid module file');
  630. }
  631. $filename = $this->module_dir . '/' . $module_data['file'];
  632. if ( !file_exists($filename) ){
  633. return new WP_Error('module_not_found', 'Module file not found');
  634. }
  635. //The module file header must be in the proper format. While $module_data comes
  636. //from cache and can be assumed to be correct, get_modules() will attempt to load
  637. //the current headers and only return modules with semi-valid headers.
  638. $installed = $this->get_modules();
  639. if ( !array_key_exists($module_data['ModuleID'], $installed) ){
  640. return new WP_Error('invalid_module_header', 'Invalid module header');
  641. }
  642. } else {
  643. //Virtual modules need to be currently registered
  644. if ( !array_key_exists($module_data['ModuleID'], $this->_virtual_modules) ){
  645. return new WP_Error('module_not_registered', 'The virtual module is not registered');
  646. }
  647. }
  648. return true;
  649. }
  650. /**
  651. * Add BLC-module specific headers to the list of allowed plugin headers. This
  652. * lets us use get_plugins() to retrieve the list of BLC modules.
  653. *
  654. * @param array $headers Currently known plugin headers.
  655. * @return array New plugin headers.
  656. */
  657. function inject_module_headers($headers){
  658. $module_headers = array(
  659. 'ModuleID',
  660. 'ModuleCategory',
  661. 'ModuleContext',
  662. 'ModuleLazyInit',
  663. 'ModuleClassName',
  664. 'ModulePriority',
  665. 'ModuleCheckerUrlPattern',
  666. 'ModuleHidden', //Don't show the module in the Settings page
  667. 'ModuleAlwaysActive', //Module can't be deactivated.
  668. 'ModuleRequiresPro', //Can only be activated in the Pro version
  669. );
  670. return array_merge($headers, $module_headers);
  671. }
  672. /**
  673. * Normalize a module header, using defaults where necessary.
  674. *
  675. * @param array $module_header Module header, as read from the module's .php file.
  676. * @param string $module_id Module ID.
  677. * @param string $module_filename Module filename. Optional.
  678. * @return array Normalized module header.
  679. */
  680. function normalize_module_header($module_header, $module_id, $module_filename = ''){
  681. //Default values for optional module header fields
  682. $defaults = array(
  683. 'ModuleContext' => 'all',
  684. 'ModuleCategory' => 'other',
  685. 'ModuleLazyInit' => 'false',
  686. 'ModulePriority' => '0',
  687. 'ModuleHidden' => 'false',
  688. 'ModuleAlwaysActive' => 'false',
  689. 'ModuleRequiresPro' => 'false',
  690. 'TextDomain' => 'broken-link-checker', //For translating module headers
  691. );
  692. $module_header['ModuleID'] = $module_id; //Just for consistency
  693. $module_header['file'] = $module_filename; //Used later to load the module
  694. //Apply defaults
  695. foreach($defaults as $field => $default_value){
  696. if ( empty($module_header[$field]) ){
  697. $module_header[$field] = $default_value;
  698. }
  699. }
  700. //Convert bool/int fields from strings to native datatypes
  701. $module_header['ModuleLazyInit'] = $this->str_to_bool($module_header['ModuleLazyInit']);
  702. $module_header['ModuleHidden'] = $this->str_to_bool($module_header['ModuleHidden']);
  703. $module_header['ModuleAlwaysActive'] = $this->str_to_bool($module_header['ModuleAlwaysActive']);
  704. $module_header['ModuleRequiresPro'] = $this->str_to_bool($module_header['ModuleRequiresPro']);
  705. $module_header['ModulePriority'] = intval($module_header['ModulePriority']);
  706. return $module_header;
  707. }
  708. /**
  709. * Converts the strings "true" and "false" to boolean TRUE and FALSE, respectively.
  710. * Any other string will yield FALSE.
  711. *
  712. * @param string $value "true" or "false", case-insensitive.
  713. * @return bool
  714. */
  715. function str_to_bool($value){
  716. $value = trim(strtolower($value));
  717. return $value == 'true';
  718. }
  719. /**
  720. * Generates a PHP script that calls the __() i18n function with
  721. * the name and description of each available module. The generated
  722. * script is used to make module headers show up in the .POT file.
  723. *
  724. * @access private
  725. *
  726. * @return string
  727. */
  728. function _build_header_translation_code(){
  729. $this->_module_cache = null; //Clear the cache
  730. $modules = $this->get_modules();
  731. $strings = array();
  732. foreach($modules as $module_id => $module_header){
  733. if ( $module_header['ModuleHidden'] || ($module_id == 'write-module-placeholders')) {
  734. continue;
  735. }
  736. if ( !empty($module_header['Name']) ){
  737. $strings[] = sprintf(
  738. '_x("%s", "module name", "broken-link-checker");',
  739. str_replace('"', '\"', $module_header['Name'])
  740. );
  741. }
  742. }
  743. return "<?php\n" . implode("\n", $strings) . "\n?>";
  744. }
  745. }
  746. ?>