class-wc-plugin-updates.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <?php
  2. /**
  3. * Class for displaying plugin warning notifications and determining 3rd party plugin compatibility.
  4. *
  5. * @package WooCommerce/Admin
  6. * @version 3.2.0
  7. */
  8. if ( ! defined( 'ABSPATH' ) ) {
  9. exit;
  10. }
  11. /**
  12. * WC_Admin_Plugin_Updates Class.
  13. */
  14. class WC_Plugin_Updates {
  15. /**
  16. * This is the header used by extensions to show requirements.
  17. *
  18. * @var string
  19. */
  20. const VERSION_REQUIRED_HEADER = 'WC requires at least';
  21. /**
  22. * This is the header used by extensions to show testing.
  23. *
  24. * @var string
  25. */
  26. const VERSION_TESTED_HEADER = 'WC tested up to';
  27. /**
  28. * The version for the update to WooCommerce.
  29. *
  30. * @var string
  31. */
  32. protected $new_version = '';
  33. /**
  34. * Array of plugins lacking testing with the major version.
  35. *
  36. * @var array
  37. */
  38. protected $major_untested_plugins = array();
  39. /**
  40. * Array of plugins lacking testing with the minor version.
  41. *
  42. * @var array
  43. */
  44. protected $minor_untested_plugins = array();
  45. /**
  46. * Common JS for initializing and managing thickbox-based modals.
  47. */
  48. protected function generic_modal_js() {
  49. ?>
  50. <script>
  51. ( function( $ ) {
  52. // Initialize thickbox.
  53. tb_init( '.wc-thickbox' );
  54. var old_tb_position = false;
  55. // Make the WC thickboxes look good when opened.
  56. $( '.wc-thickbox' ).on( 'click', function( evt ) {
  57. var $overlay = $( '#TB_overlay' );
  58. if ( ! $overlay.length ) {
  59. $( 'body' ).append( '<div id="TB_overlay"></div><div id="TB_window" class="wc_untested_extensions_modal_container"></div>' );
  60. } else {
  61. $( '#TB_window' ).removeClass( 'thickbox-loading' ).addClass( 'wc_untested_extensions_modal_container' );
  62. }
  63. // WP overrides the tb_position function. We need to use a different tb_position function than that one.
  64. // This is based on the original tb_position.
  65. if ( ! old_tb_position ) {
  66. old_tb_position = tb_position;
  67. }
  68. tb_position = function() {
  69. $( '#TB_window' ).css( { marginLeft: '-' + parseInt( ( TB_WIDTH / 2 ), 10 ) + 'px', width: TB_WIDTH + 'px' } );
  70. $( '#TB_window' ).css( { marginTop: '-' + parseInt( ( TB_HEIGHT / 2 ), 10 ) + 'px' } );
  71. };
  72. });
  73. // Reset tb_position to WP default when modal is closed.
  74. $( 'body' ).on( 'thickbox:removed', function() {
  75. if ( old_tb_position ) {
  76. tb_position = old_tb_position;
  77. }
  78. });
  79. })( jQuery );
  80. </script>
  81. <?php
  82. }
  83. /*
  84. |--------------------------------------------------------------------------
  85. | Message Helpers
  86. |--------------------------------------------------------------------------
  87. |
  88. | Methods for getting messages.
  89. */
  90. /**
  91. * Get the inline warning notice for minor version updates.
  92. *
  93. * @return string
  94. */
  95. protected function get_extensions_inline_warning_minor() {
  96. $upgrade_type = 'minor';
  97. $plugins = ! empty( $this->major_untested_plugins ) ? array_diff_key( $this->minor_untested_plugins, $this->major_untested_plugins ) : $this->minor_untested_plugins;
  98. $version_parts = explode( '.', $this->new_version );
  99. $new_version = $version_parts[0] . '.' . $version_parts[1];
  100. if ( empty( $plugins ) ) {
  101. return;
  102. }
  103. /* translators: %s: version number */
  104. $message = sprintf( __( "<strong>Heads up!</strong> The versions of the following plugins you're running haven't been tested with the latest version of WooCommerce (%s).", 'woocommerce' ), $new_version );
  105. ob_start();
  106. include 'views/html-notice-untested-extensions-inline.php';
  107. return ob_get_clean();
  108. }
  109. /**
  110. * Get the inline warning notice for major version updates.
  111. *
  112. * @return string
  113. */
  114. protected function get_extensions_inline_warning_major() {
  115. $upgrade_type = 'major';
  116. $plugins = $this->major_untested_plugins;
  117. $version_parts = explode( '.', $this->new_version );
  118. $new_version = $version_parts[0] . '.0';
  119. if ( empty( $plugins ) ) {
  120. return;
  121. }
  122. /* translators: %s: version number */
  123. $message = sprintf( __( "<strong>Heads up!</strong> The versions of the following plugins you're running haven't been tested with WooCommerce %s. Please update them or confirm compatibility before updating WooCommerce, or you may experience issues:", 'woocommerce' ), $new_version );
  124. ob_start();
  125. include 'views/html-notice-untested-extensions-inline.php';
  126. return ob_get_clean();
  127. }
  128. /**
  129. * Get the warning notice for the modal window.
  130. *
  131. * @return string
  132. */
  133. protected function get_extensions_modal_warning() {
  134. $version_parts = explode( '.', $this->new_version );
  135. $new_version = $version_parts[0] . '.0';
  136. $plugins = $this->major_untested_plugins;
  137. ob_start();
  138. include 'views/html-notice-untested-extensions-modal.php';
  139. return ob_get_clean();
  140. }
  141. /*
  142. |--------------------------------------------------------------------------
  143. | Data Helpers
  144. |--------------------------------------------------------------------------
  145. |
  146. | Methods for getting & manipulating data.
  147. */
  148. /**
  149. * Get active plugins that have a tested version lower than the input version.
  150. *
  151. * @param string $new_version WooCommerce version to test against.
  152. * @param string $release 'major' or 'minor'.
  153. * @return array of plugin info arrays
  154. */
  155. public function get_untested_plugins( $new_version, $release ) {
  156. $extensions = array_merge( $this->get_plugins_with_header( self::VERSION_TESTED_HEADER ), $this->get_plugins_for_woocommerce() );
  157. $untested = array();
  158. $new_version_parts = explode( '.', $new_version );
  159. $version = $new_version_parts[0];
  160. if ( 'minor' === $release ) {
  161. $version .= '.' . $new_version_parts[1];
  162. }
  163. if ( 'major' === $release ) {
  164. $current_version_parts = explode( '.', WC_VERSION );
  165. // If user has already moved to the major version, we don't need to flag up anything.
  166. if ( version_compare( $current_version_parts[0] . '.' . $current_version_parts[1], $new_version_parts[0] . '.0', '>=' ) ) {
  167. return array();
  168. }
  169. }
  170. foreach ( $extensions as $file => $plugin ) {
  171. if ( ! empty( $plugin[ self::VERSION_TESTED_HEADER ] ) ) {
  172. $plugin_version_parts = explode( '.', $plugin[ self::VERSION_TESTED_HEADER ] );
  173. if ( ! is_numeric( $plugin_version_parts[0] )
  174. || ( 'minor' === $release && ! isset( $plugin_version_parts[1] ) )
  175. || ( 'minor' === $release && ! is_numeric( $plugin_version_parts[1] ) )
  176. ) {
  177. continue;
  178. }
  179. $plugin_version = $plugin_version_parts[0];
  180. if ( 'minor' === $release ) {
  181. $plugin_version .= '.' . $plugin_version_parts[1];
  182. }
  183. if ( version_compare( $plugin_version, $version, '<' ) ) {
  184. $untested[ $file ] = $plugin;
  185. }
  186. } else {
  187. $plugin[ self::VERSION_TESTED_HEADER ] = __( 'unknown', 'woocommerce' );
  188. $untested[ $file ] = $plugin;
  189. }
  190. }
  191. return $untested;
  192. }
  193. /**
  194. * Get plugins that have a valid value for a specific header.
  195. *
  196. * @param string $header Plugin header to search for.
  197. * @return array Array of plugins that contain the searched header.
  198. */
  199. protected function get_plugins_with_header( $header ) {
  200. $plugins = get_plugins();
  201. $matches = array();
  202. foreach ( $plugins as $file => $plugin ) {
  203. if ( ! empty( $plugin[ $header ] ) ) {
  204. $matches[ $file ] = $plugin;
  205. }
  206. }
  207. return apply_filters( 'woocommerce_get_plugins_with_header', $matches, $header, $plugins );
  208. }
  209. /**
  210. * Get plugins which "maybe" are for WooCommerce.
  211. *
  212. * @return array of plugin info arrays
  213. */
  214. protected function get_plugins_for_woocommerce() {
  215. $plugins = get_plugins();
  216. $matches = array();
  217. foreach ( $plugins as $file => $plugin ) {
  218. if ( 'WooCommerce' !== $plugin['Name'] && ( stristr( $plugin['Name'], 'woocommerce' ) || stristr( $plugin['Description'], 'woocommerce' ) ) ) {
  219. $matches[ $file ] = $plugin;
  220. }
  221. }
  222. return apply_filters( 'woocommerce_get_plugins_for_woocommerce', $matches, $plugins );
  223. }
  224. }