/bp-forums/bbpress/bb-admin/includes/functions.bb-plugin.php

  1. <?php 
  2.  
  3. function bb_get_plugins_callback( $type = 'normal', $path, $filename ) 
  4. if ( '.php' != substr( $filename, -4 ) ) { 
  5. return false; 
  6.  
  7. $data = array( 'autoload' => 0 ); 
  8.  
  9. if ( $has_underscore = '_' === substr( $filename, 0, 1 ) ) { 
  10. switch ( $type ) { 
  11. case 'all': 
  12. case 'autoload': 
  13. $data['autoload'] = 1; 
  14. break; 
  15. case 'normal': 
  16. default: 
  17. return false; 
  18. break; 
  19. } elseif ( 'autoload' === $type ) { 
  20. return false; 
  21.  
  22. if ( $_data = bb_get_plugin_data( $path ) ) { 
  23. return array_merge( $_data , $data ); 
  24.  
  25. return false; 
  26.  
  27. function bb_get_plugins( $location = 'all', $type = 'normal', $status = 'all' ) 
  28. static $plugin_cache = array(); 
  29.  
  30. if ( !in_array( $type, array( 'all', 'autoload', 'normal' ) ) ) { 
  31. $type = 'normal'; 
  32.  
  33. if ( 'autoload' === $type || !in_array( $status, array( 'all', 'active', 'inactive' ) ) ) { 
  34. $status = 'all'; 
  35.  
  36. if ( isset( $plugin_cache[$location][$type][$status] ) ) { 
  37. return $plugin_cache[$location][$type][$status]; 
  38.  
  39. global $bb; 
  40. $directories = array(); 
  41. if ( 'all' === $location ) { 
  42. foreach ( $bb->plugin_locations as $_data ) { 
  43. $directories[] = $_data['dir']; 
  44. } elseif ( isset( $bb->plugin_locations[$location]['dir'] ) ) { 
  45. $directories[] = $bb->plugin_locations[$location]['dir']; 
  46.  
  47. require_once( BB_PATH . BB_INC . 'class.bb-dir-map.php' ); 
  48.  
  49. $plugin_arrays = array(); 
  50. foreach ( $directories as $directory ) { 
  51. $dir_map = new BB_Dir_Map( 
  52. $directory,  
  53. array( 
  54. 'callback' => 'bb_get_plugins_callback',  
  55. 'callback_args' => array( $type ),  
  56. 'recurse' => 1 
  57. ); 
  58. $dir_plugins = $dir_map->get_results(); 
  59. $dir_plugins = is_wp_error( $dir_plugins ) ? array() : $dir_plugins; 
  60. $plugin_arrays[] = $dir_plugins; 
  61. unset($dir_map, $dir_plugins); 
  62.  
  63. $plugins = array(); 
  64. foreach ($plugin_arrays as $plugin_array) { 
  65. $plugins = array_merge($plugins, $plugin_array); 
  66.  
  67. $active_plugins = (array) bb_get_option( 'active_plugins' ); 
  68.  
  69. $adjusted_plugins = array(); 
  70. foreach ($plugins as $plugin => $plugin_data) { 
  71. $_id = $plugin_data['location'] . '#' . $plugin; 
  72. $plugin_data['active'] = 0; 
  73. if ( 'autoload' === $type || in_array( $_id, $active_plugins ) ) { 
  74. $plugin_data['active'] = 1; 
  75. if ( 
  76. 'active' === $status && $plugin_data['active'] || 
  77. 'inactive' === $status && !$plugin_data['active'] || 
  78. 'all' === $status 
  79. ) { 
  80. $adjusted_plugins[$_id] = $plugin_data; 
  81.  
  82. uasort( $adjusted_plugins, 'bb_plugins_sort' ); 
  83.  
  84. $plugin_cache[$location][$type][$status] = $adjusted_plugins; 
  85.  
  86. return $adjusted_plugins; 
  87.  
  88. function bb_plugins_sort( $a, $b ) 
  89. return strnatcasecmp( $a['name'], $b['name'] ); 
  90.  
  91. function bb_get_plugin_counts() 
  92. $all_plugins = bb_get_plugins( 'all', 'all' ); 
  93. $active_plugins = (array) bb_get_option( 'active_plugins' ); 
  94. $counts = array( 
  95. 'plugin_count_all' => count( $all_plugins ),  
  96. 'plugin_count_active' => count( $active_plugins ),  
  97. 'plugin_count_inactive' => 0,  
  98. 'plugin_count_autoload' => 0 
  99. ); 
  100. foreach ( $all_plugins as $id => $all_plugin ) { 
  101. if ( $all_plugin['autoload'] ) { 
  102. $counts['plugin_count_autoload']++; 
  103. } elseif ( !in_array( $id, $active_plugins ) ) { 
  104. $counts['plugin_count_inactive']++; 
  105. return $counts; 
  106.  
  107. /** 
  108. * Parse the plugin contents to retrieve plugin's metadata. 
  109. * 
  110. * The metadata of the plugin's data searches for the following in the plugin's 
  111. * header. All plugin data must be on its own line. For plugin description, it 
  112. * must not have any newlines or only parts of the description will be displayed 
  113. * and the same goes for the plugin data. The below is formatted for printing. 
  114. * 
  115. * <code> 
  116. * /** 
  117. * Plugin Name: Name of Plugin 
  118. * Plugin URI: Link to plugin information 
  119. * Description: Plugin Description 
  120. * Author: Plugin author's name 
  121. * Author URI: Link to the author's web site 
  122. * Version: Must be set 
  123. * Requires at least: Optional. Minimum bbPress version this plugin requires 
  124. * Tested up to: Optional. Maximum bbPress version this plugin has been tested with 
  125. * Text Domain: Optional. Unique identifier, should be same as the one used in 
  126. * bb_load_plugin_textdomain() 
  127. * Domain Path: Optional. Only useful if the translations are located in a 
  128. * folder above the plugin's base path. For example, if .mo files are 
  129. * located in the locale folder then Domain Path will be "/locale/" and 
  130. * must have the first slash. Defaults to the base folder the plugin is 
  131. * located in. 
  132. * * / # You must remove the space to close comment (the space is here only for documentation purposes). 
  133. * </code> 
  134. * 
  135. * Plugin data returned array contains the following: 
  136. * 'location' - Location of plugin file 
  137. * 'name' - Name of the plugin, must be unique. 
  138. * 'uri' - Plugin's web site. 
  139. * 'plugin_link' - Title of plugin linked to plugin's web site. 
  140. * 'description' - Description of what the plugin does and/or notes 
  141. * from the author. 
  142. * 'author' - The author's name 
  143. * 'author_uri' - The author's web site address. 
  144. * 'author_link' - The author's name linked to the author's web site. 
  145. * 'version' - The plugin version number. 
  146. * 'requires' - Minimum bbPress version plugin requires 
  147. * 'tested' - Maximum bbPress version plugin has been tested with 
  148. * 'text_domain' - Plugin's text domain for localization. 
  149. * 'domain_path' - Plugin's relative directory path to .mo files. 
  150. * 
  151. * Some users have issues with opening large files and manipulating the contents 
  152. * for want is usually the first 1kiB or 2kiB. This function stops pulling in 
  153. * the plugin contents when it has all of the required plugin data. 
  154. * 
  155. * The first 8kiB of the file will be pulled in and if the plugin data is not 
  156. * within that first 8kiB, then the plugin author should correct their plugin 
  157. * and move the plugin data headers to the top. 
  158. * 
  159. * The plugin file is assumed to have permissions to allow for scripts to read 
  160. * the file. This is not checked however and the file is only opened for 
  161. * reading. 
  162. * 
  163. * @link http://trac.wordpress.org/ticket/5651 Previous Optimizations. 
  164. * @link http://trac.wordpress.org/ticket/7372 Further and better Optimizations. 
  165. * @since 1.5.0 
  166. * 
  167. * @param string $plugin_file Path to the plugin file 
  168. * @param bool $markup If the returned data should have HTML markup applied 
  169. * @param bool $translate If the returned data should be translated 
  170. * @return array See above for description. 
  171. */ 
  172. function bb_get_plugin_data( $plugin_file, $markup = true, $translate = true ) { 
  173. global $bb; 
  174.  
  175. if ( preg_match( '/^([a-z0-9_-]+)#((?:[a-z0-9\/\\_-]+.)+)(php)$/i', $plugin_file, $_matches ) ) { 
  176. $plugin_file = $bb->plugin_locations[$_matches[1]]['dir'] . $_matches[2] . $_matches[3]; 
  177.  
  178. $_directory = $bb->plugin_locations[$_matches[1]]['dir']; 
  179. $_plugin = $_matches[2] . $_matches[3]; 
  180.  
  181. if ( !$_plugin ) { 
  182. // Not likely 
  183. return false; 
  184.  
  185. if ( validate_file( $_plugin ) ) { 
  186. // $plugin has .., :, etc. 
  187. return false; 
  188.  
  189. $plugin_file = $_directory . $_plugin; 
  190. unset( $_matches, $_directory, $_plugin ); 
  191.  
  192. if ( !file_exists( $plugin_file ) ) { 
  193. // The plugin isn't there 
  194. return false; 
  195.  
  196. // We don't need to write to the file, so just open for reading. 
  197. $fp = fopen($plugin_file, 'r'); 
  198.  
  199. // Pull only the first 8kiB of the file in. 
  200. $plugin_code = fread( $fp, 8192 ); 
  201.  
  202. // PHP will close file handle, but we are good citizens. 
  203. fclose($fp); 
  204.  
  205. // Grab just the first commented area from the file 
  206. if ( !preg_match( '|/\*(.*?Plugin Name:.*?)\*/|ims', $plugin_code, $plugin_block ) ) 
  207. return false; 
  208. $plugin_data = trim( $plugin_block[1] ); 
  209.  
  210. preg_match( '|Plugin Name:(.*)$|mi', $plugin_data, $name ); 
  211. preg_match( '|Plugin URI:(.*)$|mi', $plugin_data, $uri ); 
  212. preg_match( '|Version:(.*)|i', $plugin_data, $version ); 
  213. preg_match( '|Description:(.*)$|mi', $plugin_data, $description ); 
  214. preg_match( '|Author:(.*)$|mi', $plugin_data, $author ); 
  215. preg_match( '|Author URI:(.*)$|mi', $plugin_data, $author_uri ); 
  216. preg_match( '|Text Domain:(.*)$|mi', $plugin_data, $text_domain ); 
  217. preg_match( '|Domain Path:(.*)$|mi', $plugin_data, $domain_path ); 
  218. preg_match( '|Requires at least:(.*)$|mi', $plugin_data, $requires ); 
  219. preg_match( '|Tested up to:(.*)$|mi', $plugin_data, $tested ); 
  220.  
  221. // Normalise the path to the plugin 
  222. $plugin_file = str_replace( '\\', '/', $plugin_file ); 
  223.  
  224. foreach ( $bb->plugin_locations as $_name => $_data ) { 
  225. $_directory = str_replace( '\\', '/', $_data['dir'] ); 
  226. if ( 0 === strpos( $plugin_file, $_directory ) ) { 
  227. $location = array( 1 => $_name ); 
  228. break; 
  229.  
  230. $plugins_allowedtags = array('a' => array('href' => array(), 'title' => array()), 'abbr' => array('title' => array()), 'acronym' => array('title' => array()), 'code' => array(), 'em' => array(), 'strong' => array()); 
  231.  
  232. $fields = array( 
  233. 'location' => '',  
  234. 'name' => 'html',  
  235. 'uri' => 'url',  
  236. 'version' => 'text',  
  237. 'description' => 'html',  
  238. 'author' => 'html',  
  239. 'author_uri' => 'url',  
  240. 'text_domain' => '',  
  241. 'domain_path' => '',  
  242. 'requires' => 'text',  
  243. 'tested' => 'text',  
  244. ); 
  245. foreach ( $fields as $field => $san ) { 
  246. if ( !empty( ${$field} ) ) { 
  247. ${$field} = trim(${$field}[1]); 
  248. switch ( $san ) { 
  249. case 'html' : 
  250. ${$field} = bb_filter_kses( ${$field} ); 
  251. break; 
  252. case 'text' : 
  253. ${$field} = esc_html( ${$field} ); 
  254. break; 
  255. case 'url' : 
  256. ${$field} = esc_url( ${$field} ); 
  257. break; 
  258. } else { 
  259. ${$field} = ''; 
  260.  
  261. $plugin_data = compact( array_keys( $fields ) ); 
  262.  
  263. if ( $translate ) 
  264. $plugin_data = _bb_get_plugin_data_translate( $plugin_data, $plugin_file ); 
  265.  
  266. if ( $markup ) 
  267. $plugin_data['description'] = bb_autop( preg_replace( '/[\r\n]+/', "\n", trim( $plugin_data['description'] ) ) ); 
  268.  
  269. $plugin_data['plugin_link'] = ( $plugin_data['uri'] ) ? 
  270. "<a href='{$plugin_data['uri']}' title='" . esc_attr__( 'Visit plugin site' ) . "'>{$plugin_data['name']}</a>" : 
  271. $plugin_data['name']; 
  272. $plugin_data['author_link'] = ( $plugin_data['author'] && $plugin_data['author_uri'] ) ? 
  273. "<a href='{$plugin_data['author_uri']}' title='" . esc_attr__( 'Visit author homepage' ) . "'>{$plugin_data['author']}</a>" : 
  274. $plugin_data['author']; 
  275.  
  276. return $plugin_data; 
  277.  
  278. function _bb_get_plugin_data_translate( $plugin_data, $plugin_file ) { 
  279. //Translate fields 
  280. if( !empty($plugin_data['text_domain']) ) { 
  281. if( ! empty( $plugin_data['domain_path'] ) ) 
  282. bb_load_plugin_textdomain($plugin_data['text_domain'], dirname($plugin_file). $plugin_data['domain_path']); 
  283. else 
  284. bb_load_plugin_textdomain($plugin_data['text_domain'], dirname($plugin_file)); 
  285.  
  286. foreach ( array('name', 'plugin_url', 'description', 'author', 'author_uri', 'version') as $field ) 
  287. $plugin_data[$field] = translate($plugin_data[$field], $plugin_data['text_domain']); 
  288.  
  289. return $plugin_data; 
  290.  
  291. /** 
  292. * Attempts activation of plugin in a "sandbox" and redirects on success. 
  293. * 
  294. * A plugin that is already activated will not attempt to be activated again. 
  295. * 
  296. * The way it works is by setting the redirection to the error before trying to 
  297. * include the plugin file. If the plugin fails, then the redirection will not 
  298. * be overwritten with the success message. Also, the options will not be 
  299. * updated and the activation hook will not be called on plugin error. 
  300. * 
  301. * It should be noted that in no way the below code will actually prevent errors 
  302. * within the file. The code should not be used elsewhere to replicate the 
  303. * "sandbox", which uses redirection to work. 
  304. * 
  305. * If any errors are found or text is outputted, then it will be captured to 
  306. * ensure that the success redirection will update the error redirection. 
  307. * 
  308. * @since 1.0 
  309. * 
  310. * @param string $plugin Plugin path to main plugin file with plugin data. 
  311. * @param string $redirect Optional. URL to redirect to. 
  312. * @return WP_Error|null WP_Error on invalid file or null on success. 
  313. */ 
  314. function bb_activate_plugin( $plugin, $redirect = '' ) { 
  315. $active_plugins = (array) bb_get_option( 'active_plugins' ); 
  316. $plugin = bb_plugin_basename( trim( $plugin ) ); 
  317.  
  318. $valid_path = bb_validate_plugin( $plugin ); 
  319. if ( is_wp_error( $valid_path ) ) 
  320. return $valid_path; 
  321.  
  322. if ( in_array( $plugin, $active_plugins ) ) { 
  323. return false; 
  324.  
  325. if ( !empty( $redirect ) ) { 
  326. // We'll override this later if the plugin can be included without fatal error 
  327. wp_redirect( add_query_arg( '_scrape_nonce', bb_create_nonce( 'scrape-plugin_' . $plugin ), $redirect ) );  
  328.  
  329. ob_start(); 
  330. @include( $valid_path ); 
  331. // Add to the active plugins array 
  332. $active_plugins[] = $plugin; 
  333. ksort( $active_plugins ); 
  334. bb_update_option( 'active_plugins', $active_plugins ); 
  335. do_action( 'bb_activate_plugin_' . $plugin ); 
  336. ob_end_clean(); 
  337.  
  338. return $valid_path; 
  339.  
  340. /** 
  341. * Deactivate a single plugin or multiple plugins. 
  342. * 
  343. * The deactivation hook is disabled by the plugin upgrader by using the $silent 
  344. * parameter. 
  345. * 
  346. * @since unknown 
  347. * 
  348. * @param string|array $plugins Single plugin or list of plugins to deactivate. 
  349. * @param bool $silent Optional, default is false. Prevent calling deactivate hook. 
  350. */ 
  351. function bb_deactivate_plugins( $plugins, $silent = false ) { 
  352. $active_plugins = (array) bb_get_option( 'active_plugins' ); 
  353.  
  354. if ( !is_array( $plugins ) ) { 
  355. $plugins = array( $plugins ); 
  356.  
  357. foreach ( $plugins as $plugin ) { 
  358. $plugin = bb_plugin_basename( trim( $plugin ) ); 
  359. if ( !in_array( $plugin, $active_plugins ) ) { 
  360. continue; 
  361. // Remove the deactivated plugin 
  362. array_splice( $active_plugins, array_search( $plugin, $active_plugins ), 1 ); 
  363. if ( !$silent ) { 
  364. do_action( 'bb_deactivate_plugin_' . $plugin ); 
  365.  
  366. bb_update_option( 'active_plugins', $active_plugins ); 
  367.  
  368. /** 
  369. * Validate the plugin path. 
  370. * 
  371. * Checks that the file exists and is valid file. 
  372. * 
  373. * @since 1.0 
  374. * @uses validate_file() to check the passed plugin identifier isn't malformed 
  375. * @uses bb_get_plugin_path() to get the full path of the plugin 
  376. * @uses bb_get_plugins() to get the plugins that actually exist 
  377. * 
  378. * @param string $plugin Plugin Path 
  379. * @param string $location The location of plugin, one of 'user', 'core' or 'all' 
  380. * @param string $type The type of plugin, one of 'all', 'autoload' or 'normal' 
  381. * @return WP_Error|int 0 on success, WP_Error on failure. 
  382. */ 
  383. function bb_validate_plugin( $plugin, $location = 'all', $type = 'all' ) { 
  384. if ( validate_file( trim( $plugin ) ) ) { 
  385. return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) ); 
  386. $path = bb_get_plugin_path( trim( $plugin ) ); 
  387. if ( !file_exists( $path ) ) { 
  388. return new WP_Error( 'plugin_not_found', __( 'Plugin file does not exist.' ) ); 
  389. if ( !in_array( trim( $plugin ), array_keys( bb_get_plugins( $location, $type ) ) ) ) { 
  390. return new WP_Error( 'plugin_not_available', __( 'That type of plugin is not available in the specified location.' ) ); 
  391.  
  392. return $path; 
.