├── .ci-env.sh ├── .gitmodules ├── .travis.yml ├── lang ├── plugin-dependencies-nl_NL.mo ├── plugin-dependencies-nl_NL.po ├── plugin-dependencies-ro_RO.mo ├── plugin-dependencies-ro_RO.po └── plugin-dependencies.pot ├── plugin-dependencies.php ├── readme.md └── readme.txt /.ci-env.sh: -------------------------------------------------------------------------------- 1 | export PHPCS_GIT_TREE=phpcs-fixer 2 | export WPCS_GIT_TREE=develop 3 | export WPCS_STANDARD=WordPress-Extra 4 | export PHPCS_IGNORE='tests/*,dev-lib/*' 5 | export YUI_COMPRESSOR_CHECK=0 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dev-lib"] 2 | path = dev-lib 3 | url = https://github.com/xwp/wp-plugin-dev-lib.git 4 | branch = vip-themes 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: 2 | - php 3 | - node_js 4 | 5 | php: 6 | - 5.4 7 | - 5.5 8 | 9 | node_js: 10 | - 0.10 11 | 12 | env: 13 | - WP_VERSION=latest WP_MULTISITE=0 14 | - WP_VERSION=latest WP_MULTISITE=1 15 | - WP_VERSION=4.0 WP_MULTISITE=0 16 | - WP_VERSION=4.0 WP_MULTISITE=1 17 | 18 | before_script: 19 | - export DEV_LIB_PATH=dev-lib 20 | - if [ ! -e "$DEV_LIB_PATH" ] && [ -L .travis.yml ]; then export DEV_LIB_PATH=$( dirname $( readlink .travis.yml ) ); fi 21 | - source $DEV_LIB_PATH/travis.before_script.sh 22 | 23 | script: 24 | - $DEV_LIB_PATH/travis.script.sh 25 | 26 | after_script: 27 | - $DEV_LIB_PATH/travis.after_script.sh 28 | -------------------------------------------------------------------------------- /lang/plugin-dependencies-nl_NL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-plugin-dependencies/b4e6be79f5b04c9c8ea638fbc85e012b61e9c42e/lang/plugin-dependencies-nl_NL.mo -------------------------------------------------------------------------------- /lang/plugin-dependencies-nl_NL.po: -------------------------------------------------------------------------------- 1 | # Translation of the WordPress plugin Plugin Dependencies 1.0 by scribu. 2 | # Copyright (C) 2010 scribu 3 | # This file is distributed under the same license as the Plugin Dependencies package. 4 | # FIRST AUTHOR , 2010. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Plugin Dependencies\n" 9 | "Report-Msgid-Bugs-To: http://wordpress.org/tag/plugin-dependencies\n" 10 | "POT-Creation-Date: 2014-09-20 16:16+0100\n" 11 | "PO-Revision-Date: 2014-09-20 16:19+0100\n" 12 | "Last-Translator: EJ Reinders Folmer \n" 13 | "Language-Team: \n" 14 | "Language: nl_NL\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 19 | "X-Generator: Poedit 1.5.7\n" 20 | "X-Poedit-KeywordsList: __;_e;_n;_x;_ex;_nx;esc_html__;esc_html_e;esc_html_x;" 21 | "esc_attr__;esc_attr_e;esc_attr_x;_n_noop;_nx_noop\n" 22 | "X-Poedit-Basepath: .\n" 23 | "X-Poedit-SearchPath-0: .\n" 24 | "X-Poedit-SearchPath-1: ..\n" 25 | 26 | #: ../plugin-dependencies.php:569 27 | msgid "" 28 | "The following plugins have (also) been deactivated as a plugin they depend " 29 | "on has been deactivated:" 30 | msgstr "" 31 | "De volgende plugins zijn (ook) gedeactiveerd aangezien een plugin waar zij " 32 | "afhankelijk van zijn gedeactiveerd is:" 33 | 34 | #: ../plugin-dependencies.php:570 35 | msgid "" 36 | "The following plugins have been deactivated due to dependency conflicts:" 37 | msgstr "" 38 | "De volgende plugins zijn gedeactiveerd in verband met conflicterende " 39 | "afhankelijkheden:" 40 | 41 | #: ../plugin-dependencies.php:571 42 | msgid "" 43 | "One or more plugins were not activated as their dependencies were not met at " 44 | "the time of activation. If the dependencies have been met in the mean time, " 45 | "please try and activate them again:" 46 | msgstr "" 47 | "Eén of meer plugins zijn niet geactiveerd aangezien op het moment van " 48 | "activatie niet aan alle afhankelijkheden voldaan werd. Als de " 49 | "afhankelijkheden sindsdien afgedekt zijn, probeer ze dan opnieuw te " 50 | "activeren:" 51 | 52 | #: ../plugin-dependencies.php:572 53 | msgid "" 54 | "The plugin(s) which was just deactivated provided a dependency for other " 55 | "plugins. Dependent plugin(s) on the following sites in your network have " 56 | "been deactivated:" 57 | msgstr "" 58 | "De plugin die zojuist gedactiveerd is, leverde een afhankelijkheid aan " 59 | "andere plugins. Afhankelijke plugin(s) op de volgende sites in het network " 60 | "zijn gedeactiveerd:" 61 | 62 | #: ../plugin-dependencies.php:721 63 | msgid "Required plugins:" 64 | msgstr "Vereiste plugins:" 65 | 66 | #: ../plugin-dependencies.php:736 67 | msgid "Dependency: Unsatisfied" 68 | msgstr "Afhankelijkheid: Voldoet niet" 69 | 70 | #: ../plugin-dependencies.php:740 71 | msgid "Dependency: Network unsatisfied" 72 | msgstr "Afhankelijkheid: Netwerk voldoet niet" 73 | 74 | #: ../plugin-dependencies.php:744 75 | msgid "Dependency: Satisfied" 76 | msgstr "Afhankelijkheid: Voldoet" 77 | 78 | #: ../plugin-dependencies.php:759 ../plugin-dependencies.php:772 79 | #, php-format 80 | msgid "%s (%s)" 81 | msgstr "%s (%s)" 82 | 83 | #: ../plugin-dependencies.php:761 84 | msgid "network" 85 | msgstr "netwerk" 86 | 87 | #: ../plugin-dependencies.php:774 88 | msgid "must-use" 89 | msgstr "" 90 | 91 | #. Plugin Name of the plugin/theme 92 | msgid "Plugin Dependencies" 93 | msgstr "Plugin Dependencies" 94 | 95 | #. Plugin URI of the plugin/theme 96 | msgid "http://scribu.net/wordpress/plugin-dependencies" 97 | msgstr "http://scribu.net/wordpress/plugin-dependencies" 98 | 99 | #. Description of the plugin/theme 100 | msgid "" 101 | "Prevent activating plugins that don't have all their dependencies satisfied" 102 | msgstr "" 103 | "Voorkom het activeren van plugins als niet alle afhankelijkheden afgedekt " 104 | "zijn" 105 | 106 | #. Author of the plugin/theme 107 | msgid "scribu" 108 | msgstr "scribu" 109 | 110 | #. Author URI of the plugin/theme 111 | msgid "http://scribu.net/" 112 | msgstr "http://scribu.net/" 113 | 114 | #~ msgid "The following plugins have also been deactivated:" 115 | #~ msgstr "De volgende plugins zijn ook gedeactiveerd:" 116 | -------------------------------------------------------------------------------- /lang/plugin-dependencies-ro_RO.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-plugin-dependencies/b4e6be79f5b04c9c8ea638fbc85e012b61e9c42e/lang/plugin-dependencies-ro_RO.mo -------------------------------------------------------------------------------- /lang/plugin-dependencies-ro_RO.po: -------------------------------------------------------------------------------- 1 | # Translation of the WordPress plugin Plugin Dependencies 1.0 by scribu. 2 | # Copyright (C) 2010 scribu 3 | # This file is distributed under the same license as the Plugin Dependencies package. 4 | # FIRST AUTHOR , 2010. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Plugin Dependencies 1.0\n" 9 | "Report-Msgid-Bugs-To: http://wordpress.org/tag/plugin-dependencies\n" 10 | "POT-Creation-Date: 2010-10-24 22:10+0000\n" 11 | "PO-Revision-Date: 2010-10-25 01:12+0200\n" 12 | "Last-Translator: scribu \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=utf-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: plugin-dependencies.php:136 19 | msgid "The following plugins have also been deactivated:" 20 | msgstr "Următoarele module au fost de asemnea dezactivate:" 21 | 22 | #: plugin-dependencies.php:202 23 | msgid "Required plugins:" 24 | msgstr "Module necesare:" 25 | 26 | #. Plugin Name of the plugin/theme 27 | msgid "Plugin Dependencies" 28 | msgstr "" 29 | 30 | #. Plugin URI of the plugin/theme 31 | msgid "http://scribu.net/wordpress/plugin-dependencies" 32 | msgstr "" 33 | 34 | #. Description of the plugin/theme 35 | msgid "Prevent activating plugins that don't have all their dependencies satisfied" 36 | msgstr "" 37 | 38 | #. Author of the plugin/theme 39 | msgid "scribu" 40 | msgstr "" 41 | 42 | #. Author URI of the plugin/theme 43 | msgid "http://scribu.net/" 44 | msgstr "" 45 | 46 | -------------------------------------------------------------------------------- /lang/plugin-dependencies.pot: -------------------------------------------------------------------------------- 1 | # Translation of the WordPress plugin Plugin Dependencies 1.0 by scribu. 2 | # Copyright (C) 2010 scribu 3 | # This file is distributed under the same license as the Plugin Dependencies package. 4 | # FIRST AUTHOR , 2010. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: Plugin Dependencies\n" 9 | "Report-Msgid-Bugs-To: http://wordpress.org/tag/plugin-dependencies\n" 10 | "POT-Creation-Date: 2014-09-20 16:16+0100\n" 11 | "PO-Revision-Date: 2014-09-20 16:17+0100\n" 12 | "Last-Translator: EJ Reinders Folmer \n" 13 | "Language-Team: \n" 14 | "Language: English\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 19 | "X-Generator: Poedit 1.5.7\n" 20 | "X-Poedit-KeywordsList: __;_e;_n;_x;_ex;_nx;esc_html__;esc_html_e;esc_html_x;" 21 | "esc_attr__;esc_attr_e;esc_attr_x;_n_noop;_nx_noop\n" 22 | "X-Poedit-Basepath: .\n" 23 | "X-Poedit-SearchPath-0: .\n" 24 | "X-Poedit-SearchPath-1: ..\n" 25 | 26 | #: ../plugin-dependencies.php:569 27 | msgid "" 28 | "The following plugins have (also) been deactivated as a plugin they depend " 29 | "on has been deactivated:" 30 | msgstr "" 31 | 32 | #: ../plugin-dependencies.php:570 33 | msgid "" 34 | "The following plugins have been deactivated due to dependency conflicts:" 35 | msgstr "" 36 | 37 | #: ../plugin-dependencies.php:571 38 | msgid "" 39 | "One or more plugins were not activated as their dependencies were not met at " 40 | "the time of activation. If the dependencies have been met in the mean time, " 41 | "please try and activate them again:" 42 | msgstr "" 43 | 44 | #: ../plugin-dependencies.php:572 45 | msgid "" 46 | "The plugin(s) which was just deactivated provided a dependency for other " 47 | "plugins. Dependent plugin(s) on the following sites in your network have " 48 | "been deactivated:" 49 | msgstr "" 50 | 51 | #: ../plugin-dependencies.php:721 52 | msgid "Required plugins:" 53 | msgstr "" 54 | 55 | #: ../plugin-dependencies.php:736 56 | msgid "Dependency: Unsatisfied" 57 | msgstr "" 58 | 59 | #: ../plugin-dependencies.php:740 60 | msgid "Dependency: Network unsatisfied" 61 | msgstr "" 62 | 63 | #: ../plugin-dependencies.php:744 64 | msgid "Dependency: Satisfied" 65 | msgstr "" 66 | 67 | #: ../plugin-dependencies.php:759 ../plugin-dependencies.php:772 68 | #, php-format 69 | msgid "%s (%s)" 70 | msgstr "" 71 | 72 | #: ../plugin-dependencies.php:761 73 | msgid "network" 74 | msgstr "" 75 | 76 | #: ../plugin-dependencies.php:774 77 | msgid "must-use" 78 | msgstr "" 79 | 80 | #. Plugin Name of the plugin/theme 81 | msgid "Plugin Dependencies" 82 | msgstr "" 83 | 84 | #. Plugin URI of the plugin/theme 85 | msgid "http://scribu.net/wordpress/plugin-dependencies" 86 | msgstr "" 87 | 88 | #. Description of the plugin/theme 89 | msgid "" 90 | "Prevent activating plugins that don't have all their dependencies satisfied" 91 | msgstr "" 92 | 93 | #. Author of the plugin/theme 94 | msgid "scribu" 95 | msgstr "" 96 | 97 | #. Author URI of the plugin/theme 98 | msgid "http://scribu.net/" 99 | msgstr "" 100 | -------------------------------------------------------------------------------- /plugin-dependencies.php: -------------------------------------------------------------------------------- 1 | $plugin_data ) { 87 | $plugins_by_name[ $plugin_data['Name'] ] = $plugin; 88 | } 89 | 90 | foreach ( $all_plugins as $plugin => $plugin_data ) { 91 | self::$provides[ $plugin ] = array(); 92 | if ( ! empty( $plugin_data['Provides'] ) ) { 93 | self::$provides[ $plugin ] = self::parse_field( $plugin_data['Provides'] ); // returns array 94 | } 95 | self::$provides[ $plugin ][] = $plugin; 96 | 97 | $deps = array(); 98 | 99 | if ( ! empty( $plugin_data['Depends'] ) ) { 100 | foreach ( self::parse_field( $plugin_data['Depends'] ) as $dep ) { 101 | if ( isset( $plugins_by_name[ $dep ] ) ) { 102 | $dep = $plugins_by_name[ $dep ]; 103 | } 104 | 105 | $deps[] = $dep; 106 | } 107 | } 108 | 109 | self::$dependencies[ $plugin ] = $deps; 110 | } 111 | } 112 | 113 | private static function parse_field( $str ) { 114 | return array_filter( preg_split( '/,\s*/', $str ) ); 115 | } 116 | 117 | /** 118 | * Get a list of real or virtual dependencies for a plugin 119 | * 120 | * @param string $plugin_id A plugin basename 121 | * @return array List of dependencies 122 | */ 123 | public static function get_dependencies( $plugin_id ) { 124 | if ( ! isset( self::$dependencies ) ) { 125 | self::init(); 126 | } 127 | 128 | if ( ! isset( self::$dependencies[ $plugin_id ] ) ) { 129 | return array(); 130 | } else { 131 | return self::$dependencies[ $plugin_id ]; 132 | } 133 | } 134 | 135 | /** 136 | * Get a list of dependencies provided by a certain plugin 137 | * 138 | * @param string $plugin_id A plugin basename 139 | * @return array List of dependencies 140 | */ 141 | public static function get_provided( $plugin_id ) { 142 | if ( ! isset( self::$provides ) ) { 143 | self::init(); 144 | } 145 | 146 | if ( ! isset( self::$provides[ $plugin_id ] ) ) { 147 | return array(); 148 | } else { 149 | return self::$provides[ $plugin_id ]; 150 | } 151 | } 152 | 153 | /** 154 | * Get a list of plugins that provide a certain dependency 155 | * 156 | * @param string $dep Real or virtual dependency 157 | * @return array List of plugins 158 | */ 159 | public static function get_providers( $dep ) { 160 | if ( ! isset( self::$provides ) ) { 161 | self::init(); 162 | } 163 | 164 | $plugin_ids = array(); 165 | 166 | if ( isset( self::$provides[ $dep ] ) ) { 167 | $plugin_ids = array( $dep ); 168 | } else { 169 | // virtual dependency 170 | foreach ( self::$provides as $plugin => $provides ) { 171 | if ( in_array( $dep, $provides ) ) { 172 | $plugin_ids[] = $plugin; 173 | } 174 | } 175 | } 176 | 177 | return $plugin_ids; 178 | } 179 | 180 | 181 | /** 182 | * Check if the dependencies for activating a particular plugin are met 183 | * 184 | * @param string $plugin Name of the plugin currently being activated 185 | * @param bool $network_wide Whether this is a network or single site activation 186 | */ 187 | public static function check_activation( $plugin, $network_wide = false ) { 188 | if ( true === $network_wide ) { 189 | self::$active_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); 190 | } 191 | else { 192 | self::$active_plugins = get_option( 'active_plugins', array() ); 193 | 194 | if ( is_multisite() ) { 195 | self::$active_plugins = array_merge( self::$active_plugins, array_keys( get_site_option( 'active_sitewide_plugins', array() ) ) ); 196 | } 197 | } 198 | 199 | // Allow for plugins which are being activated in the same bulk activation action 200 | $bulk = array(); 201 | if ( ( isset( $_POST['action'] ) && 'activate-selected' === $_POST['action'] ) && ( isset( $_POST['checked'] ) && is_array( $_POST['checked'] ) ) ) { 202 | $bulk = $_POST['checked']; 203 | } 204 | 205 | self::$active_plugins = array_merge( self::$active_plugins, array_keys( (array) get_mu_plugins() ), $bulk ); 206 | 207 | $deps = self::get_dependencies( $plugin ); 208 | if ( count( $deps ) === count( array_intersect( self::$active_plugins, $deps ) ) ) { 209 | // Ok, all dependencies have been met 210 | return; 211 | } 212 | else { 213 | self::$blocked_activations[] = $plugin; 214 | self::set_transient( 'activate', self::$blocked_activations, $network_wide ); 215 | 216 | /* Prevent other activation hooks from running as the plugin will not be activated. 217 | Unfortunately we can't easily do this for the rest of the 'activate_plugins' hook nor 218 | for the 'activated_plugin' hook as these are not plugin specific and those actions 219 | should still run for non-blocked activations in a bulk activation run. 220 | So hooking into the last one to undo any additional activation actions run by running 221 | their corresponding deactivation hooks. 222 | */ 223 | remove_all_actions( 'activate_' . $plugin ); 224 | add_action( 'activated_plugin', array( __CLASS__, 'undo_activation_actions' ), 9999, 2 ); 225 | 226 | if ( $network_wide ) { 227 | add_filter( 'pre_update_site_option_active_sitewide_plugins', array( __CLASS__, 'prevent_activation' ), 10, 2 ); 228 | } else { 229 | add_filter( 'pre_update_option_active_plugins', array( __CLASS__, 'prevent_activation' ), 10, 2 ); 230 | } 231 | 232 | if ( ! empty( self::$blocked_activations ) && false === has_filter( 'pre_update_option_recently_activated', array( __CLASS__, 'override_recently_activated' ) ) ) { 233 | add_filter( 'pre_update_option_recently_activated', array( __CLASS__, 'override_recently_activated' ) ); 234 | } 235 | } 236 | } 237 | 238 | 239 | /** 240 | * Check for conflicting provider plugins which may need to be cascade deactivated. 241 | * 242 | * @param string $plugin Name of the plugin currently being activated 243 | * @param bool $network_wide Whether this is a network or single site activation 244 | */ 245 | public static function check_conflicting( $plugin, $network_wide = false ) { 246 | if ( ! isset( self::$blocked_activations ) || ! in_array( $plugin, self::$blocked_activations, true ) ) { 247 | self::execute_check( 'conflicting', $plugin, $network_wide ); 248 | } 249 | } 250 | 251 | 252 | /** 253 | * Check for depencing plugins which need to be cascade deactivated. 254 | * 255 | * @param string $plugin Name of the plugin currently being deactivated 256 | * @param bool $network_wide Whether this is a network or single site deactivation 257 | */ 258 | public static function check_cascade( $plugin, $network_wide = false ) { 259 | self::execute_check( 'cascade', $plugin, $network_wide ); 260 | } 261 | 262 | 263 | /** 264 | * Execute a dependency check 265 | * 266 | * @param string $type Either 'conflicting' or 'cascade' 267 | * @param string $plugin Name of the plugin currently being (de-)activated 268 | * @param bool $network_wide Whether the current call to the method is for a network-wide (de)activation 269 | */ 270 | protected static function execute_check( $type, $plugin, $network_wide = false ) { 271 | remove_action( 'deactivate_plugin', array( 'Plugin_Dependencies', 'check_cascade' ) ); 272 | Plugin_Dependencies::init(); 273 | self::$active_network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); 274 | self::check_dependencies( $type, $plugin, $network_wide, $network_wide ); 275 | add_action( 'deactivate_plugin', array( 'Plugin_Dependencies', 'check_cascade' ), 10, 2 ); 276 | } 277 | 278 | 279 | /** 280 | * Execute the actual dependency checks. 281 | * 282 | * @param string $type Either 'conflicting' or 'cascade' 283 | * @param string $plugin Name of the plugin currently being (de-)activated 284 | * @param bool $network_wide Whether the current call to the method is for a network-wide (de)activation 285 | * @param bool $original_network_wide Whether the originating call to this method was for a network-wide (de)activation 286 | */ 287 | private static function check_dependencies( $type, $plugin, $network_wide = false, $original_network_wide = false ) { 288 | 289 | if ( ! is_multisite() || false === $network_wide ) { 290 | self::$active_plugins = get_option( 'active_plugins', array() ); 291 | 292 | /* No need to execute check when a plugin is network deactivated, but still activated for 293 | the individual site */ 294 | if ( array() !== self::$active_plugins && ( false === $original_network_wide || ! in_array( $plugin, self::$active_plugins, true ) ) ) { 295 | 296 | if ( is_multisite() ) { 297 | self::$active_plugins = array_merge( self::$active_plugins, self::$active_network_plugins ); 298 | } 299 | 300 | $deactivated = call_user_func( array( __CLASS__, "deactivate_$type" ), (array) $plugin, false ); 301 | if ( $deactivated !== array() ) { 302 | self::set_transient( $type, $deactivated, false ); 303 | self::add_to_recently_deactivated( $deactivated ); 304 | self::$deactivated_on_sites[] = get_current_blog_id(); 305 | 306 | if ( false === $original_network_wide ) { 307 | add_filter( 'pre_update_option_active_plugins', array( __CLASS__, 'prevent_option_override' ) ); 308 | } 309 | } 310 | } 311 | } 312 | else { 313 | /* Multi-site network (de-)activation - check plugin dependencies for each blog */ 314 | self::check_dependencies_for_blogs( $type, $plugin ); 315 | 316 | /* And for the network */ 317 | self::$active_plugins = self::$active_network_plugins; 318 | $deactivated = call_user_func( array( __CLASS__, "deactivate_$type" ), (array) $plugin, $network_wide ); 319 | if ( $deactivated !== array() ) { 320 | self::set_transient( $type, $deactivated, true ); 321 | add_filter( 'pre_update_site_option_active_sitewide_plugins', array( __CLASS__, 'prevent_option_override_sitewide' ) ); 322 | } 323 | 324 | if ( isset( self::$deactivated_on_sites ) && self::$deactivated_on_sites !== array() ) { 325 | self::set_transient( 'network', self::$deactivated_on_sites, true ); 326 | } 327 | } 328 | self::$active_plugins = null; 329 | } 330 | 331 | 332 | /** 333 | * Walk through all blogs and execute the requested check for each. 334 | * 335 | * @param string $type Either 'conflicting' or 'cascade' 336 | * @param string $plugin Name of the plugin currently being (de-)activated 337 | */ 338 | public static function check_dependencies_for_blogs( $type, $plugin ) { 339 | global $wpdb; 340 | 341 | $original_blog_id = get_current_blog_id(); // alternatively use: $wpdb->blogid 342 | $all_blogs = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" ); 343 | 344 | if ( is_array( $all_blogs ) && $all_blogs !== array() ) { 345 | foreach ( $all_blogs as $blog_id ) { 346 | switch_to_blog( $blog_id ); 347 | self::check_dependencies( $type, $plugin, false, true ); 348 | } 349 | // Restore back to original blog 350 | switch_to_blog( $original_blog_id ); 351 | } 352 | } 353 | 354 | 355 | /** 356 | * Deactivate plugins that would provide the same dependencies as the ones in the list 357 | * 358 | * @param array $plugin_ids A list of plugin basenames 359 | * @param bool $network_deactivate Whether to network or single site deactivate any plugins which need deactivating 360 | * @return array List of deactivated plugins 361 | */ 362 | protected static function deactivate_conflicting( $to_activate, $network_deactivate = false ) { 363 | $deps = array(); 364 | foreach ( $to_activate as $plugin_id ) { 365 | $deps = array_merge( $deps, self::get_provided( $plugin_id ) ); 366 | } 367 | 368 | self::$deactivate_conflicting = array(); 369 | 370 | $to_check = array_diff( get_option( 'active_plugins', array() ), $to_activate ); // precaution 371 | 372 | foreach ( $to_check as $active_plugin ) { 373 | $common = array_intersect( $deps, self::get_provided( $active_plugin ) ); 374 | 375 | if ( ! empty( $common ) ) { 376 | self::$deactivate_conflicting[] = $active_plugin; 377 | } 378 | } 379 | 380 | // TODO: don't deactivate plugins that would still have all dependencies satisfied 381 | $deactivated = self::deactivate_cascade( self::$deactivate_conflicting, $network_deactivate ); 382 | 383 | deactivate_plugins( self::$deactivate_conflicting, false, $network_deactivate ); 384 | 385 | return array_merge( self::$deactivate_conflicting, $deactivated ); 386 | } 387 | 388 | 389 | /** 390 | * Deactivate plugins that would have unmet dependencies 391 | * 392 | * @param array $plugin_ids A list of plugin basenames 393 | * @param bool $network_deactivate Whether to network or single site deactivate any plugins which need deactivating 394 | * @return array List of deactivated plugins 395 | */ 396 | protected static function deactivate_cascade( $to_deactivate, $network_deactivate = false ) { 397 | if ( empty( $to_deactivate ) ) { 398 | return array(); 399 | } 400 | 401 | self::$deactivate_cascade = array(); 402 | 403 | self::_cascade( $to_deactivate, $network_deactivate ); 404 | 405 | // Do not notify about plugins which are requested to be deactivated anyway within the same bulk deactivation request 406 | $bulk = array(); 407 | if ( ( isset( $_POST['action2'] ) && 'deactivate-selected' === $_POST['action2'] ) && ( isset( $_POST['checked'] ) && is_array( $_POST['checked'] ) ) ) { 408 | $bulk = $_POST['checked']; 409 | } 410 | self::$deactivate_cascade = array_diff( self::$deactivate_cascade, $bulk ); 411 | 412 | return self::$deactivate_cascade; 413 | } 414 | 415 | private static function _cascade( $to_deactivate, $network_deactivate = false ) { 416 | $to_deactivate_deps = array(); 417 | foreach ( $to_deactivate as $plugin_id ) { 418 | $to_deactivate_deps = array_merge( $to_deactivate_deps, self::get_provided( $plugin_id ) ); 419 | } 420 | 421 | $found = array(); 422 | foreach ( self::$active_plugins as $dep ) { 423 | $matching_deps = array_intersect( $to_deactivate_deps, self::get_dependencies( $dep ) ); 424 | if ( ! empty( $matching_deps ) ) { 425 | $found[] = $dep; 426 | } 427 | } 428 | 429 | $found = array_diff( $found, self::$deactivate_cascade ); // prevent endless loop 430 | if ( empty( $found ) ) { 431 | return; 432 | } 433 | 434 | self::$deactivate_cascade = array_merge( self::$deactivate_cascade, $found ); 435 | 436 | self::_cascade( $found, $network_deactivate ); 437 | 438 | deactivate_plugins( $found, false, $network_deactivate ); 439 | } 440 | 441 | 442 | /** 443 | * Set a 'plugins deactivated' transient. 444 | * 445 | * @param string $type Either 'conflicting' or 'cascade' 446 | * @param array $deactivated Deactivated plugins 447 | * @param bool $network Whether to set the transient for the network or for an individual site 448 | */ 449 | protected static function set_transient( $type, $deactivated, $network = false ) { 450 | if ( true !== $network ) { 451 | $value = get_transient( "pd_deactivate_$type" ); 452 | if ( is_array( $value ) ) { 453 | $deactivated = array_merge( $value, $deactivated ); 454 | } 455 | set_transient( "pd_deactivate_$type", array_unique( $deactivated ) ); 456 | } 457 | else { 458 | $value = get_site_transient( "pd_deactivate_$type" ); 459 | if ( is_array( $value ) ) { 460 | $deactivated = array_merge( $value, $deactivated ); 461 | } 462 | set_site_transient( "pd_deactivate_$type", array_unique( $deactivated ) ); 463 | } 464 | } 465 | 466 | 467 | /** 468 | * Add deactivated plugins to the 'recently active' plugins list. 469 | * 470 | * @param array $deactivated Array of deactivated plugins 471 | */ 472 | protected static function add_to_recently_deactivated( $deactivated ) { 473 | $recent = array(); 474 | foreach ( $deactivated as $plugin ) { 475 | $recent[ $plugin ] = time(); 476 | } 477 | update_option( 'recently_activated', $recent + (array) get_option( 'recently_activated' ) ); 478 | } 479 | 480 | 481 | /** 482 | * Add blocked (bulk) plugin activations to the 'recently active' plugins list. 483 | */ 484 | public static function override_recently_activated( $new_value ) { 485 | remove_filter( current_filter(), array( __CLASS__, __FUNCTION__ ) ); 486 | 487 | $recent = array(); 488 | foreach ( self::$blocked_activations as $plugin ) { 489 | $recent[ $plugin ] = time(); 490 | } 491 | return array_merge( $new_value, $recent ); 492 | } 493 | 494 | 495 | /** 496 | * Prevent a plugin from being activated by overruling the new option value with the old. 497 | * Used for blocking plugin activations. 498 | */ 499 | public static function prevent_activation( $new_value, $old_value ) { 500 | remove_filter( current_filter(), array( __CLASS__, __FUNCTION__ ) ); 501 | return $old_value; 502 | } 503 | 504 | 505 | /** 506 | * Prevent the active plugins option being overwritten by the original (de)activate_plugins call as 507 | * that would undo the changes made by this plugin. 508 | */ 509 | public static function prevent_option_override( $new_value ) { 510 | remove_filter( current_filter(), array( __CLASS__, __FUNCTION__ ) ); 511 | return array_diff( $new_value, (array) self::$deactivate_cascade, (array) self::$deactivate_conflicting ); 512 | } 513 | 514 | 515 | /** 516 | * Prevent the sitewide active plugins option being overwritten by the original (de)activate_plugins call as 517 | * that would undo the changes made by this plugin. 518 | */ 519 | public static function prevent_option_override_sitewide( $new_value ) { 520 | remove_filter( current_filter(), array( __CLASS__, __FUNCTION__ ) ); 521 | 522 | $unset = array_merge( (array) self::$deactivate_cascade, (array) self::$deactivate_conflicting ); 523 | foreach ( $unset as $plugin ) { 524 | unset( $new_value[ $plugin ] ); 525 | } 526 | 527 | return $new_value; 528 | } 529 | 530 | 531 | /** 532 | * Undo any activation actions which may have run for blocked activation plugins 533 | * 534 | * @param string $plugin Name of the plugin which was blocked 535 | * @param bool $network_wide Whether the intended activation was for the network or single site 536 | */ 537 | public static function undo_activation_actions( $plugin, $network_wide ) { 538 | remove_action( current_filter(), array( __CLASS__, __FUNCTION__ ) ); 539 | do_action( 'deactivate_plugin', $plugin, $network_wide ); 540 | do_action( 'deactivate_' . $plugin, $network_wide ); 541 | do_action( 'deactivated_plugin', $plugin, $network_wide ); 542 | } 543 | } 544 | 545 | 546 | class Plugin_Dependencies_UI { 547 | 548 | private static $msg; 549 | 550 | /** 551 | * @var array $unsatisfied Checkbox ids for 'unsatisfied' plugins 552 | */ 553 | protected static $unsatisfied = array(); 554 | 555 | public static function init() { 556 | add_action( 'admin_print_styles', array( __CLASS__, 'admin_print_styles' ) ); 557 | add_action( 'admin_print_footer_scripts', array( __CLASS__, 'footer_script' ), 20 ); 558 | 559 | add_filter( 'plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 4 ); 560 | add_filter( 'network_admin_plugin_action_links', array( __CLASS__, 'plugin_action_links' ), 10, 4 ); 561 | 562 | Plugin_Dependencies::init(); 563 | } 564 | 565 | 566 | /** 567 | * Load text domain and set the message texts 568 | */ 569 | public static function load_textdomain() { 570 | load_plugin_textdomain( 'plugin-dependencies', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' ); 571 | 572 | self::$msg = array( 573 | array( 'cascade', __( 'The following plugins have (also) been deactivated as a plugin they depend on has been deactivated:', 'plugin-dependencies' ) ), 574 | array( 'conflicting', __( 'The following plugins have been deactivated due to dependency conflicts:', 'plugin-dependencies' ) ), 575 | array( 'activate', __( 'One or more plugins were not activated as their dependencies were not met at the time of activation. If the dependencies have been met in the mean time, please try and activate them again:', 'plugin-dependencies' ) ), 576 | array( 'network', __( 'The plugin(s) which was just deactivated provided a dependency for other plugins. Dependent plugin(s) on the following sites in your network have been deactivated:', 'plugin-dependencies' ) ), 577 | ); 578 | } 579 | 580 | 581 | /** 582 | * Show admin notices 583 | */ 584 | public static function admin_notices() { 585 | if ( current_user_can( 'publish_posts' ) ) { 586 | foreach ( self::$msg as $args ) { 587 | list( $type, $text ) = $args; 588 | 589 | if ( ! is_network_admin() ) { 590 | $deactivated = get_transient( "pd_deactivate_$type" ); 591 | delete_transient( "pd_deactivate_$type" ); 592 | } 593 | else { 594 | $deactivated = get_site_transient( "pd_deactivate_$type" ); 595 | delete_site_transient( "pd_deactivate_$type" ); 596 | } 597 | 598 | if ( empty( $deactivated ) ) { 599 | continue; 600 | } 601 | 602 | if ( 'network' !== $type ) { 603 | echo html( 604 | 'div', 605 | array( 'class' => 'updated' ), 606 | html( 'p', $text, self::generate_dep_list( $deactivated, $deactivated ) ) 607 | ); // xss ok 608 | } 609 | else { 610 | $dep_list = ''; 611 | $class = 'unsatisfied'; 612 | foreach ( $deactivated as $blog_id ) { 613 | $details = get_blog_details( $blog_id, false ); 614 | $dep_list .= html( 'li', compact( 'class' ), html( 'a', array( 'href' => get_admin_url( $blog_id, 'plugins.php?plugin_status=recently_activated' ) ), $details->blogname ) ); 615 | } 616 | 617 | echo html( 618 | 'div', 619 | array( 'class' => 'updated' ), 620 | html( 'p', $text, html( 'ul', array( 'class' => 'dep-list' ), $dep_list ) ) 621 | ); // xss ok 622 | } 623 | } 624 | } 625 | } 626 | 627 | public static function admin_print_styles() { 628 | ?> 629 | 636 | $data ) { 653 | $name = isset( $data['Name'] ) ? $data['Name'] : $file; 654 | $hash[ $name ] = sanitize_title( $name ); 655 | } 656 | 657 | $unsatisfied = '#' . implode( ', #', self::$unsatisfied ); 658 | ?> 659 | 677 | 'dep-action' ), __( 'Required plugins:', 'plugin-dependencies' ) ); 727 | $dep_list .= '
' . self::generate_dep_list( $deps, $unsatisfied, $unsatisfied_network ); 728 | $actions['deps'] = $dep_list; 729 | 730 | return $actions; 731 | } 732 | 733 | 734 | public static function generate_dep_list( $deps, $unsatisfied = array(), $unsatisfied_network = array() ) { 735 | $all_plugins = get_plugins(); 736 | $mu_plugins = get_mu_plugins(); 737 | 738 | $dep_list = ''; 739 | foreach ( $deps as $dep ) { 740 | $plugin_ids = Plugin_Dependencies::get_providers( $dep ); 741 | if ( in_array( $dep, $unsatisfied ) ) { 742 | $class = 'unsatisfied'; 743 | $title = __( 'Dependency: Unsatisfied', 'plugin-dependencies' ); 744 | } 745 | elseif ( in_array( $dep, $unsatisfied_network ) ) { 746 | $class = 'unsatisfied_network'; 747 | $title = __( 'Dependency: Network unsatisfied', 'plugin-dependencies' ); 748 | } 749 | else { 750 | $class = 'satisfied'; 751 | $title = __( 'Dependency: Satisfied', 'plugin-dependencies' ); 752 | } 753 | 754 | if ( empty( $plugin_ids ) ) { 755 | $name = html( 'span', esc_html( $dep ) ); 756 | } else { 757 | $list = array(); 758 | foreach ( $plugin_ids as $plugin_id ) { 759 | if ( isset( $all_plugins[ $plugin_id ]['Name'] ) ) { 760 | if ( is_network_admin() || ! is_plugin_active_for_network( $plugin_id ) ) { 761 | $name = $all_plugins[ $plugin_id ]['Name']; 762 | $url = self_admin_url( 'plugins.php' ) . '#' . sanitize_title( $name ); 763 | } 764 | else { 765 | $name = sprintf( 766 | __( '%s (%s)', 'plugin-dependencies' ), 767 | $all_plugins[ $plugin_id ]['Name'], 768 | __( 'network', 'plugin-dependencies' ) 769 | ); 770 | if ( current_user_can( 'manage_network_plugins' ) ) { 771 | $url = network_admin_url( 'plugins.php' ) . '#' . sanitize_title( $all_plugins[ $plugin_id ]['Name'] ); 772 | } 773 | else { 774 | $url = false; 775 | } 776 | } 777 | } elseif ( isset( $mu_plugins[ $plugin_id ]['Name'] ) ) { 778 | $name = sprintf( 779 | __( '%s (%s)', 'plugin-dependencies' ), 780 | $mu_plugins[ $plugin_id ]['Name'], 781 | __( 'must-use', 'plugin-dependencies' ) 782 | ); 783 | $url = add_query_arg( 'plugin_status', 'mustuse' ) . '#' . sanitize_title( $mu_plugins[ $plugin_id ]['Name'] ); 784 | } else { 785 | $name = $plugin_id; 786 | $url = '#' . sanitize_title( $name ); 787 | } 788 | 789 | if ( false !== $url ) { 790 | $list[] = html( 'a', array( 'href' => $url, 'title' => $title ), $name ); 791 | } 792 | else { 793 | $list[] = html( 'span', array( 'title' => $title ), $name ); 794 | } 795 | } 796 | $name = implode( ' or ', $list ); 797 | } 798 | 799 | $dep_list .= html( 'li', compact( 'class' ), $name ); 800 | } 801 | 802 | return html( 'ul', array( 'class' => 'dep-list' ), $dep_list ); 803 | } 804 | } 805 | 806 | 807 | if ( ! function_exists( 'html' ) ) : 808 | function html( $tag ) { 809 | $args = func_get_args(); 810 | 811 | $tag = array_shift( $args ); 812 | 813 | if ( is_array( $args[0] ) ) { 814 | $closing = $tag; 815 | $attributes = array_shift( $args ); 816 | foreach ( $attributes as $key => $value ) { 817 | if ( false === $value ) { 818 | continue; 819 | } 820 | 821 | if ( true === $value ) { 822 | $value = $key; 823 | } 824 | 825 | $tag .= ' ' . $key . '="' . esc_attr( $value ) . '"'; 826 | } 827 | } else { 828 | list( $closing ) = explode( ' ', $tag, 2 ); 829 | } 830 | 831 | if ( in_array( $closing, array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta' ) ) ) { 832 | return "<{$tag} />"; 833 | } 834 | 835 | $content = implode( '', $args ); 836 | 837 | return "<{$tag}>{$content}"; 838 | } 839 | endif; 840 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Plugin Dependencies 3 | 4 | Plugin dependency management 5 | 6 | **Contributors:** [scribu](http://profiles.wordpress.org/scribu), [xwp](http://profiles.wordpress.org/xwp), [kucrut](http://profiles.wordpress.org/kucrut), [jrf](http://profiles.wordpress.org/jrf) 7 | **Tags:** [plugin](http://wordpress.org/plugins/tags/plugin), [dependency](http://wordpress.org/plugins/tags/dependency) 8 | **Requires at least:** 3.1 9 | **Tested up to:** 4.0 10 | **Stable tag:** trunk (master) 11 | **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) 12 | 13 | [![Build Status](https://travis-ci.org/xwp/wp-plugin-dependencies.png?branch=master)](https://travis-ci.org/xwp/wp-plugin-dependencies) 14 | 15 | ## Description ## 16 | 17 | This meta-plugin allows regular plugins to specify other plugins that they depend upon. 18 | 19 | Example: 20 | 21 | ```php 22 | /* 23 | Plugin Name: BuddyPress Debug 24 | Depends: BuddyPress, Debug Bar 25 | */ 26 | ``` 27 | 28 | What this does: 29 | 30 | * Disables activation of *BuddyPress Debug* until both *BuddyPress* and *Debug Bar* are already activated. 31 | * When either *BuddyPress* or *Debug Bar* are deactivated, *BuddyPress Debug* will also be deactivated. 32 | 33 | 34 | > = Enriching dependency information = 35 | > 36 | > Unfortunately, very few plugins currently contain dependency information. If you'd like to enhance the information available to this plugin, you might want to install the [Known Plugin Dependencies](https://wordpress.org/plugins/known-plugin-dependencies/) plugin which acts as an add-on to this one. 37 | 38 | **Development of this plugin is done [on GitHub](https://github.com/xwp/wp-plugin-dependencies). Pull requests welcome. Please see [issues](https://github.com/xwp/wp-plugin-dependencies/issues) reported there before going to the plugin forum.** 39 | 40 | ## Frequently Asked Questions ## 41 | 42 | ### What happens if a user doesn't have Plugin Dependencies installed? ### 43 | Nothing. The *Depends:* header will simply be ignored. 44 | 45 | ### Can I have grand-child plugins? ### 46 | Yes, the dependency chain can go as deep as you want. 47 | 48 | ### Defining virtual packages ### 49 | Say you have some useful functions that you would like to package up as a library plugin: 50 | 51 | ```php 52 | /* 53 | Plugin Name: Facebook Lib 54 | Provides: lib-facebook 55 | */ 56 | ``` 57 | 58 | Now, dependant plugins can specify 'lib-facebook' as a dependency: 59 | 60 | ```php 61 | /* 62 | Plugin Name: Cool Facebook Plugin 63 | Depends: lib-facebook 64 | */ 65 | ``` 66 | 67 | Besides being more robust, the *Provides:* header allows multiple plugins to implement the same set of functionality and be used interchangeably. 68 | 69 | 70 | ## Screenshots ## 71 | 72 | 73 | ## Changelog ## 74 | 75 | ### 1.3 ### 76 | * Add Dependency Loader class. Props [kucrut](http://profiles.wordpress.org/kucrut/). 77 | 78 | * Make it work with bulk actions. Props [jrf](http://profiles.wordpress.org/jrf/). 79 | * Usability: Remove bulk action checkboxes for plugins with unsatisfied dependencies on single site plugins page within a network. 80 | 81 | * Guard dependencies even when a plugin is (de)activated outside of the plugins page context. Props [jrf](http://profiles.wordpress.org/jrf/). 82 | 83 | * Fix compatibility with multi-site. Props [jrf](http://profiles.wordpress.org/jrf/). 84 | * New: Show dependencies in the network admin plugins page. 85 | * Bug fix: network activated plugins were not recognized (at all) and deactivating one would throw PHP notices. 86 | * Bug fix: network activation action was not correctly unset if dependencies were not met (WP 3.4+). 87 | * Bug fix: network deactivation would only check dependencies for the network and the main site, not for the other sites in the network 88 | * Improved: logic for recognizing whether dependencies have been satisfied. 89 | * Usability: On single site plugin page in a multisite network: added a "network" textual indicator for dependencies which were met by a network activated plugin. 90 | * Usability: On single site plugin page in a multisite network: the required plugin names now only link to the plugin if the current user can activate that plugin. 91 | * Usability: Improved information to single site admins when dependent plugins have been deactivated because a required plugin has been network deactivated - show all deactivated plugins since last admin login, not just what happened in the last change round. 92 | * Usability: Notifications about deactivated plugins are now shown on any admin page which will help admins notice changes made by this plugin earlier in case of a network deactivation. 93 | 94 | * Clean up coding standards. Props [kucrut](http://profiles.wordpress.org/kucrut/), [jrf](http://profiles.wordpress.org/jrf/). 95 | * Improve style of plugin dependency notices. Props [jrf](http://profiles.wordpress.org/jrf/). 96 | * Usability: Add plugins deactivated by this plugin to the 'recently active' plugins list. Props [jrf](http://profiles.wordpress.org/jrf/). 97 | * Add Dutch translation. Props [jrf](http://profiles.wordpress.org/jrf/). 98 | 99 | ### 1.2.1 ### 100 | * fixed notices. props [cfoellmann](http://profiles.wordpress.org/cfoellmann) 101 | 102 | ### 1.2 ### 103 | * added ability to use plugin names as dependencies 104 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-2.html) 105 | 106 | ### 1.1 ### 107 | * added 'Provides:' header 108 | * replaced 'Dependencies:' with 'Depends:' 109 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-1.html) 110 | 111 | ### 1.0.1 ### 112 | * fixed critical bug when not running MultiSite 113 | * better network activation handling 114 | 115 | ### 1.0 ### 116 | * initial release 117 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-0.html) 118 | 119 | 120 | ## Upgrade Notice ## 121 | 122 | ### 1.3 ### 123 | * Upgrade highly recommended - Plugin now fully compatible with multisite and dependency management will now also work outside of the plugins page context, including for bulk actions. 124 | 125 | 126 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Plugin Dependencies === 2 | Contributors: scribu, xwp, kucrut, jrf 3 | Tags: plugin, dependency 4 | Requires at least: 3.1 5 | Tested up to: 4.0 6 | Stable tag: trunk 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | Plugin dependency management 11 | 12 | == Description == 13 | 14 | This meta-plugin allows regular plugins to specify other plugins that they depend upon. 15 | 16 | Example: 17 | 18 |
 19 | /*
 20 | Plugin Name: BuddyPress Debug
 21 | Depends: BuddyPress, Debug Bar
 22 | */
 23 | 
24 | 25 | What this does: 26 | 27 | * Disables activation of *BuddyPress Debug* until both *BuddyPress* and *Debug Bar* are already activated. 28 | * When either *BuddyPress* or *Debug Bar* are deactivated, *BuddyPress Debug* will also be deactivated. 29 | 30 | 31 | > = Enriching dependency information = 32 | > 33 | > Unfortunately, very few plugins currently contain dependency information. If you'd like to enhance the information available to this plugin, you might want to install the [Known Plugin Dependencies](https://wordpress.org/plugins/known-plugin-dependencies/) plugin which acts as an add-on to this one. 34 | 35 | **Development of this plugin is done [on GitHub](https://github.com/xwp/wp-plugin-dependencies). Pull requests welcome. Please see [issues](https://github.com/xwp/wp-plugin-dependencies/issues) reported there before going to the plugin forum.** 36 | 37 | == Frequently Asked Questions == 38 | 39 | = What happens if a user doesn't have Plugin Dependencies installed? = 40 | 41 | Nothing. The *Depends:* header will simply be ignored. 42 | 43 | = Can I have grand-child plugins? = 44 | 45 | Yes, the dependency chain can go as deep as you want. 46 | 47 | = Defining virtual packages = 48 | 49 | Say you have some useful functions that you would like to package up as a library plugin: 50 | 51 |
 52 | /*
 53 | Plugin Name: Facebook Lib
 54 | Provides: lib-facebook
 55 | */
 56 | 
57 | 58 | Now, dependant plugins can specify 'lib-facebook' as a dependency: 59 | 60 |
 61 | /*
 62 | Plugin Name: Cool Facebook Plugin
 63 | Depends: lib-facebook
 64 | */
 65 | 
66 | 67 | Besides being more robust, the *Provides:* header allows multiple plugins to implement the same set of functionality and be used interchangeably. 68 | 69 | == Screenshots == 70 | 71 | 1. Activation prevention 72 | 1. Cascade deactivation 73 | 74 | == Changelog == 75 | 76 | = 1.3 = 77 | * Add Dependency Loader class. Props [kucrut](http://profiles.wordpress.org/kucrut/). 78 | 79 | * Make it work with bulk actions. Props [jrf](http://profiles.wordpress.org/jrf/). 80 | * Usability: Remove bulk action checkboxes for plugins with unsatisfied dependencies on single site plugins page within a network. 81 | 82 | * Guard dependencies even when a plugin is (de)activated outside of the plugins page context. Props [jrf](http://profiles.wordpress.org/jrf/). 83 | 84 | * Fix compatibility with multi-site. Props [jrf](http://profiles.wordpress.org/jrf/). 85 | * New: Show dependencies in the network admin plugins page. 86 | * Bug fix: network activated plugins were not recognized (at all) and deactivating one would throw PHP notices. 87 | * Bug fix: network activation action was not correctly unset if dependencies were not met (WP 3.4+). 88 | * Bug fix: network deactivation would only check dependencies for the network and the main site, not for the other sites in the network 89 | * Improved: logic for recognizing whether dependencies have been satisfied. 90 | * Usability: On single site plugin page in a multisite network: added a "network" textual indicator for dependencies which were met by a network activated plugin. 91 | * Usability: On single site plugin page in a multisite network: the required plugin names now only link to the plugin if the current user can activate that plugin. 92 | * Usability: Improved information to single site admins when dependent plugins have been deactivated because a required plugin has been network deactivated - show all deactivated plugins since last admin login, not just what happened in the last change round. 93 | * Usability: Notifications about deactivated plugins are now shown on any admin page which will help admins notice changes made by this plugin earlier in case of a network deactivation. 94 | 95 | * Clean up coding standards. Props [kucrut](http://profiles.wordpress.org/kucrut/), [jrf](http://profiles.wordpress.org/jrf/). 96 | * Improve style of plugin dependency notices. Props [jrf](http://profiles.wordpress.org/jrf/). 97 | * Usability: Add plugins deactivated by this plugin to the 'recently active' plugins list. Props [jrf](http://profiles.wordpress.org/jrf/). 98 | * Add Dutch translation. Props [jrf](http://profiles.wordpress.org/jrf/). 99 | 100 | 101 | = 1.2.1 = 102 | * fixed notices. props [cfoellmann](http://profiles.wordpress.org/cfoellmann) 103 | 104 | = 1.2 = 105 | * added ability to use plugin names as dependencies 106 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-2.html) 107 | 108 | = 1.1 = 109 | * added 'Provides:' header 110 | * replaced 'Dependencies:' with 'Depends:' 111 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-1.html) 112 | 113 | = 1.0.1 = 114 | * fixed critical bug when not running MultiSite 115 | * better network activation handling 116 | 117 | = 1.0 = 118 | * initial release 119 | * [more info](http://scribu.net/wordpress/plugin-dependencies/pd-1-0.html) 120 | 121 | == Upgrade Notice == 122 | 123 | = 1.3 = 124 | * Upgrade highly recommended - Plugin now fully compatible with multisite and dependency management will now also work outside of the plugins page context, including for bulk actions. --------------------------------------------------------------------------------