/includes/addons.php

  1. <?php 
  2. /** 
  3. Some of the code in this library was borrowed from the TGM Updater class by Thomas Griffin. (https://github.com/thomasgriffin/TGM-Updater) 
  4. */ 
  5.  
  6. /** 
  7. * Setup plugins api filters 
  8. * 
  9. * @since 1.8.5 
  10. */ 
  11. function pmpro_setupAddonUpdateInfo() 
  12. add_filter('plugins_api', 'pmpro_plugins_api', 10, 3); 
  13. add_filter('pre_set_site_transient_update_plugins', 'pmpro_update_plugins_filter'); 
  14. add_filter('http_request_args', 'pmpro_http_request_args_for_addons', 10, 2); 
  15. add_action('update_option_pmpro_license_key', 'pmpro_reset_update_plugins_cache', 10, 2); 
  16. add_action('init', 'pmpro_setupAddonUpdateInfo'); 
  17.  
  18. /** 
  19. * Get addon information from PMPro server. 
  20. * 
  21. * @since 1.8.5 
  22. */ 
  23. function pmpro_getAddons() 
  24. //check if forcing a pull from the server 
  25. $addons = get_option("pmpro_addons", array()); 
  26. $addons_timestamp = get_option("pmpro_addons_timestamp", 0); 
  27.  
  28. //if no addons locally, we need to hit the server 
  29. if(empty($addons) || !empty($_REQUEST['force-check']) || current_time('timestamp') > $addons_timestamp+86400) 
  30. /** 
  31. * Filter to change the timeout for this wp_remote_get() request. 
  32. * 
  33. * @since 1.8.5.1 
  34. * 
  35. * @param int $timeout The number of seconds before the request times out 
  36. */ 
  37. $timeout = apply_filters("pmpro_get_addons_timeout", 5); 
  38.  
  39. //get em 
  40. $remote_addons = wp_remote_get(PMPRO_LICENSE_SERVER . "addons/", $timeout); 
  41.  
  42. //make sure we have at least an array to pass back 
  43. if(empty($addons)) 
  44. $addons = array(); 
  45.  
  46. //test response 
  47. if(is_wp_error($remote_addons)) { 
  48. //error 
  49. pmpro_setMessage("Could not connect to the PMPro License Server to update addon information. Try again later.", "error"); 
  50. elseif(!empty($remote_addons) && $remote_addons['response']['code'] == 200) 
  51. //update addons in cache 
  52. $addons = json_decode(wp_remote_retrieve_body($remote_addons), true); 
  53. delete_option('pmpro_addons'); 
  54. add_option("pmpro_addons", $addons, NULL, 'no'); 
  55.  
  56. //save timestamp of last update 
  57. delete_option('pmpro_addons_timestamp'); 
  58. add_option("pmpro_addons_timestamp", current_time('timestamp'), NULL, 'no');  
  59. }  
  60.  
  61. return $addons; 
  62.  
  63. /** 
  64. * Find a PMPro addon by slug. 
  65. * 
  66. * @since 1.8.5 
  67. * 
  68. * @param object $slug The identifying slug for the addon (typically the directory name) 
  69. * @return object $addon containing plugin information or false if not found 
  70. */ 
  71. function pmpro_getAddonBySlug($slug) 
  72. $addons = pmpro_getAddons(); 
  73.  
  74. if(empty($addons)) 
  75. return false; 
  76.  
  77. foreach($addons as $addon) 
  78. if($addon['Slug'] == $slug) 
  79. return $addon; 
  80.  
  81. return false; 
  82.  
  83. /** 
  84. * Infuse plugin update details when WordPress runs its update checker. 
  85. * 
  86. * @since 1.8.5 
  87. * 
  88. * @param object $value The WordPress update object. 
  89. * @return object $value Amended WordPress update object on success, default if object is empty. 
  90. */ 
  91. function pmpro_update_plugins_filter( $value ) { 
  92.  
  93. // If no update object exists, return early. 
  94. if ( empty( $value ) ) { 
  95. return $value; 
  96.  
  97. // get addon information 
  98. $addons = pmpro_getAddons(); 
  99.  
  100. // no addons? 
  101. if(empty($addons)) 
  102. return $value; 
  103.  
  104. //check addons 
  105. foreach($addons as $addon) 
  106. //skip wordpress.org plugins 
  107. if(empty($addon['License']) || $addon['License'] == 'wordpress') 
  108. continue; 
  109.  
  110. //get data for plugin 
  111. $plugin_file = $addon['Slug'] . '/' . $addon['Slug'] . '.php'; 
  112. $plugin_file_abs = WP_PLUGIN_DIR . "/" . $plugin_file;  
  113.  
  114. //couldn't find plugin, skip 
  115. if(!file_exists($plugin_file_abs)) 
  116. continue; 
  117. else 
  118. $plugin_data = get_plugin_data( $plugin_file_abs, false, true); 
  119.  
  120. //compare versions 
  121. if(!empty($addon['License']) && version_compare($plugin_data['Version'], $addon['Version'], '<')) 
  122. $value->response[$plugin_file] = pmpro_getPluginAPIObjectFromAddon($addon); 
  123. $value->response[$plugin_file]->new_version = $addon['Version'];  
  124.  
  125. // Return the update object. 
  126. return $value; 
  127.  
  128. /** 
  129. * Disables SSL verification to prevent download package failures. 
  130. * 
  131. * @since 1.8.5 
  132. * 
  133. * @param array $args Array of request args. 
  134. * @param string $url The URL to be pinged. 
  135. * @return array $args Amended array of request args. 
  136. */ 
  137. function pmpro_http_request_args_for_addons($args, $url)  
  138. // If this is an SSL request and we are performing an upgrade routine, disable SSL verification. 
  139. if(strpos($url, 'https://') !== false && strpos($url, PMPRO_LICENSE_SERVER) !== false && strpos($url, "download") !== false) 
  140. $args['sslverify'] = false; 
  141.  
  142. return $args; 
  143.  
  144. /** 
  145. * Setup plugin updaters 
  146. * 
  147. * @since 1.8.5 
  148. */ 
  149. function pmpro_plugins_api($api, $action = '', $args = null) 
  150. {  
  151. //Not even looking for plugin information? Or not given slug? 
  152. if('plugin_information' != $action || empty($args->slug)) 
  153. return $api; 
  154.  
  155. //get addon information 
  156. $addon = pmpro_getAddonBySlug($args->slug); 
  157.  
  158. //no addons? 
  159. if(empty($addon)) 
  160. return $api; 
  161.  
  162. //handled by wordpress.org? 
  163. if(empty($addon['License']) || $addon['License'] == 'wordpress') 
  164. return $api; 
  165.  
  166. // Create a new stdClass object and populate it with our plugin information. 
  167. $api = pmpro_getPluginAPIObjectFromAddon($addon); 
  168. return $api; 
  169.  
  170. /** 
  171. * Convert the format from the pmpro_getAddons function to that needed for plugins_api 
  172. * 
  173. * @since 1.8.5 
  174. */ 
  175. function pmpro_getPluginAPIObjectFromAddon($addon) 
  176. $api = new stdClass; 
  177.  
  178. if(empty($addon)) 
  179. return $api; 
  180.  
  181. $api->name = isset( $addon['Name'] ) ? $addon['Name'] : ''; 
  182. $api->slug = isset( $addon['Slug'] ) ? $addon['Slug'] : ''; 
  183. $api->plugin = isset( $addon['plugin'] ) ? $addon['plugin'] : ''; 
  184. $api->version = isset( $addon['Version'] ) ? $addon['Version'] : ''; 
  185. $api->author = isset( $addon['Author'] ) ? $addon['Author'] : ''; 
  186. $api->author_profile = isset( $addon['AuthorURI'] ) ? $addon['AuthorURI'] : ''; 
  187. $api->requires = isset( $addon['Requires'] ) ? $addon['Requires'] : ''; 
  188. $api->tested = isset( $addon['Tested'] ) ? $addon['Tested'] : ''; 
  189. $api->last_updated = isset( $addon['LastUpdated'] ) ? $addon['LastUpdated'] : ''; 
  190. $api->homepage = isset( $addon['URI'] ) ? $addon['URI'] : ''; 
  191. $api->sections['changelog'] = isset( $addon['Changelog'] ) ? $addon['Changelog'] : ''; 
  192. $api->download_link = isset( $addon['Download'] ) ? $addon['Download'] : ''; 
  193. $api->package = isset( $addon['Download'] ) ? $addon['Download'] : ''; 
  194.  
  195. //get license key if one is available 
  196. $key = get_option("pmpro_license_key", ""); 
  197. if(!empty($key) && !empty($api->download_link)) 
  198. $api->download_link = add_query_arg("key", $key, $api->download_link); 
  199. if(!empty($key) && !empty($api->package)) 
  200. $api->package = add_query_arg("key", $key, $api->package); 
  201. if(empty($api->upgrade_notice) && !pmpro_license_isValid()) 
  202. $api->upgrade_notice = __('Important: This plugin requires a valid PMPro Plus license key to update.', 'paid-memberships-pro'); 
  203.  
  204. return $api; 
  205.  
  206. /** 
  207. * Force update of plugin update data when the PMPro License key is updated 
  208. * 
  209. * @since 1.8 
  210. * 
  211. * @param array $args Array of request args. 
  212. * @param string $url The URL to be pinged. 
  213. * @return array $args Amended array of request args. 
  214. */ 
  215. function pmpro_reset_update_plugins_cache($old_value, $value)  
  216. delete_option('pmpro_addons_timestamp'); 
  217. delete_site_transient('update_themes');  
  218.  
  219. /** 
  220. * Detect when trying to update a PMPro Plus plugin without a valid license key. 
  221. * 
  222. * @since 1.9 
  223. */ 
  224. function pmpro_admin_init_updating_plugins() {  
  225. //if user can't edit plugins, then WP will catch this later 
  226. if ( ! current_user_can('update_plugins') ) 
  227. return; 
  228.  
  229. //updating one or more plugins via Dashboard -> Upgrade 
  230. if(basename($_SERVER['SCRIPT_NAME']) == 'update.php' && !empty($_REQUEST['action']) && $_REQUEST['action'] == 'update-selected' && !empty($_REQUEST['plugins'])) { 
  231. //figure out which plugin we are updating 
  232. $plugins = explode( ', ', stripslashes($_GET['plugins']) );  
  233. $plugins = array_map('urldecode', $plugins);  
  234.  
  235. //look for addons 
  236. $plus_addons = array(); 
  237. $plus_plugins = array(); 
  238. foreach($plugins as $plugin) {  
  239. $slug = str_replace('.php', '', basename($plugin)); 
  240. $addon = pmpro_getAddonBySlug($slug);  
  241. if(!empty($addon) && $addon['License'] == 'plus') { 
  242. $plus_addons[] = $addon['Name']; 
  243. $plus_plugins[] = $plugin; 
  244. }  
  245. unset($plugin); 
  246.  
  247. //if Plus addons found, check license key 
  248. if(!empty($plus_plugins) && !pmpro_license_isValid()) { 
  249. //show error 
  250. $msg = __('You must have a <a href="https://www.paidmembershipspro.com/pricing/?utm_source=wp-admin&utm_pluginlink=bulkupdate">valid PMPro Plus License Key</a> to update PMPro Plus add ons. The following plugins will not be updated:', 'paid-memberships-pro'); 
  251. echo '<div class="error"><p>' . $msg . ' <strong>' . implode(', ', $plus_addons) . '</strong></p></div>';  
  252.  
  253. //can exit out of this function now 
  254. return; 
  255.  
  256. //upgrading just one or plugin via an update.php link 
  257. if(basename($_SERVER['SCRIPT_NAME']) == 'update.php' && !empty($_REQUEST['action']) && $_REQUEST['action'] == 'upgrade-plugin' && !empty($_REQUEST['plugin'])) { 
  258. //figure out which plugin we are updating 
  259. $plugin = urldecode(trim($_REQUEST['plugin'])); 
  260.  
  261. $slug = str_replace('.php', '', basename($plugin)); 
  262. $addon = pmpro_getAddonBySlug($slug); 
  263. if(!empty($addon) && !pmpro_license_isValid()) { 
  264. require_once(ABSPATH . 'wp-admin/admin-header.php'); 
  265.  
  266. echo '<div class="wrap"><h2>' . __('Update Plugin') . '</h2>'; 
  267.  
  268. $msg = __('You must have a <a href="https://www.paidmembershipspro.com/pricing/?utm_source=wp-admin&utm_pluginlink=addon_update">valid PMPro Plus License Key</a> to update PMPro Plus add ons.', 'paid-memberships-pro'); 
  269. echo '<div class="error"><p>' . $msg . '</p></div>'; 
  270.  
  271. echo '<p><a href="' . admin_url('admin.php?page=pmpro-addons') . '" target="_parent">' . __('Return to the PMPro Add Ons page', 'paid-memberships-pro') . '</a></p>'; 
  272.  
  273. echo '</div>'; 
  274.  
  275. include(ABSPATH . 'wp-admin/admin-footer.php'); 
  276.  
  277. //can exit WP now 
  278. exit;  
  279. }  
  280.  
  281. //updating via AJAX on the plugins page 
  282. if(basename($_SERVER['SCRIPT_NAME']) == 'admin-ajax.php' && !empty($_REQUEST['action']) && $_REQUEST['action'] == 'update-plugin' && !empty($_REQUEST['plugin'])) { 
  283. //figure out which plugin we are updating 
  284. $plugin = urldecode(trim($_REQUEST['plugin'])); 
  285.  
  286. $slug = str_replace('.php', '', basename($plugin)); 
  287. $addon = pmpro_getAddonBySlug($slug); 
  288. if(!empty($addon) && !pmpro_license_isValid()) { 
  289. $msg = __('You must enter a valid PMPro Plus License Key under Settings > PMPro License to update this add on.', 'paid-memberships-pro'); 
  290. echo '<div class="error"><p>' . $msg . '</p></div>';  
  291.  
  292. //can exit WP now 
  293. exit; 
  294. }  
  295.  
  296. /** 
  297. TODO:  
  298. * Check for PMPro Plug plugins 
  299. * If a plus plugin is found, check the PMPro license key 
  300. * If the key is missing or invalid, throw an error 
  301. * Show appropriate footer and exit... maybe do something else to keep plugin update from happening 
  302. */ 
  303. add_action('admin_init', 'pmpro_admin_init_updating_plugins'); 
.