MS_Model_Upgrade

Upgrade DB model.

Defined (1)

The class is defined in the following location(s).

/app/model/class-ms-model-upgrade.php  
  1. class MS_Model_Upgrade extends MS_Model { 
  2.  
  3. /** 
  4. * Initialize upgrading check. 
  5. * @since 1.0.0 
  6. */ 
  7. public static function init() { 
  8. self::update(); 
  9.  
  10. MS_Factory::load( 'MS_Model_Upgrade' ); 
  11.  
  12. // This function is intended for development/testing only! 
  13. self::maybe_restore(); 
  14.  
  15. // This is a hidden feature available in the Settings > General page. 
  16. add_action( 'init', array( __CLASS__, 'maybe_reset' ) ); 
  17.  
  18. // This is a hidden feature available in Settings > General page. 
  19. add_action( 'init', array( __CLASS__, 'maybe_fix_stripe_subs' ) ); 
  20.  
  21. // Prevent WordPress from updating the Membership plugin when the 
  22. // WPMU DEV Dashboard is disabled. 
  23. if ( ! class_exists( 'WPMUDEV_Dashboard' ) ) { 
  24. add_filter( 
  25. 'plugins_api',  
  26. array( __CLASS__, 'no_dash_plugins_api' ),  
  27. 101, 3 
  28. ); 
  29. add_filter( 
  30. 'site_transient_update_plugins',  
  31. array( __CLASS__, 'no_dash_update_plugins' ) 
  32. ); 
  33.  
  34. do_action( 'ms_model_upgrade_init' ); 
  35.  
  36. /** 
  37. * Upgrade database. 
  38. * @since 1.0.0 
  39. * @param bool $force Also execute update logic when version did not change. 
  40. */ 
  41. public static function update( $force = false ) { 
  42. static $Done = false; 
  43.  
  44. if ( $Done && ! $force ) { return; } 
  45.  
  46. // Migration handler has its own valid_user check. 
  47. self::check_migration_handler(); 
  48.  
  49. // Updates are only triggered from Admin-Side by an Admin user. 
  50. if ( ! self::valid_user() ) { return; } 
  51.  
  52. // Check for correct network-wide protection setup. 
  53. self::check_settings(); 
  54.  
  55. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  56. $old_version = $settings->version; // Old: The version in DB. 
  57. $new_version = MS_Plugin::instance()->version; // New: Version in file. 
  58.  
  59. $is_new_setup = empty( $old_version ); 
  60.  
  61. // Compare current src version to DB version: 
  62. // We only do UP-grades but no DOWN-grades! 
  63. if ( $old_version ) { 
  64. $version_changed = version_compare( $old_version, $new_version, 'lt' ); 
  65. } else { 
  66. $version_changed = true; 
  67.  
  68. if ( $force || $version_changed ) { 
  69. $Done = true; 
  70. $msg = array(); 
  71.  
  72. /** 
  73. * ----- General update logic, executed on every update ------------ 
  74. */ 
  75.  
  76. do_action( 
  77. 'ms_model_upgrade_before_update',  
  78. $settings,  
  79. $old_version,  
  80. $new_version,  
  81. $force 
  82. ); 
  83.  
  84. // Prepare the Update message. 
  85. if ( ! $version_changed ) { 
  86. $msg[] = sprintf( 
  87. __( '<strong>Membership 2</strong> is set up for version %1$s!' , 'membership2' ),  
  88. $new_version 
  89. ); 
  90. } else { 
  91. $msg[] = sprintf( 
  92. __( '<strong>Membership 2</strong> was updated to version %1$s!' , 'membership2' ),  
  93. $new_version 
  94. ); 
  95.  
  96. // Every time the plugin is updated we clear the cache. 
  97. MS_Factory::clear(); 
  98.  
  99. // Create missing Membership pages. 
  100. $new_pages = MS_Model_Pages::create_missing_pages(); 
  101.  
  102. if ( ! empty( $new_pages ) ) { 
  103. $msg[] = sprintf( 
  104. __( 'New Membership pages created: "%1$s".', 'membership2' ),  
  105. implode( '", "', $new_pages ) 
  106. ); 
  107.  
  108. // Remove an old version of Protected Content 
  109. // TODO: REMOVE THIS BLOCK/FUNCTION END OF 2015 
  110. if ( $version_changed ) { 
  111. self::remove_old_copy(); 
  112.  
  113. // Note: We do not create menu items on upgrade! Users might have 
  114. // intentionally removed the items from the menu... 
  115.  
  116. /** 
  117. * ----- Version-Specific update logic ----------------------------- 
  118. */ 
  119.  
  120. // Upgrade from a 1.0.0.x version to 1.0.1.0 or higher 
  121. if ( version_compare( $old_version, '1.0.1.0', 'lt' ) ) { 
  122. self::_upgrade_1_0_1_0(); 
  123.  
  124. // Upgrade from 1.0.1.0 version to 1.0.1.1 or higher 
  125. if ( version_compare( $old_version, '1.0.1.1', 'lt' ) ) { 
  126. self::_upgrade_1_0_1_1(); 
  127.  
  128. // Upgrade from 1.0.1.x version to 1.0.2.0 or higher 
  129. if ( version_compare( $old_version, '1.0.2.0', 'lt' ) ) { 
  130. self::_upgrade_1_0_2_0(); 
  131.  
  132. // Upgrade from 1.0.2.x version to 1.0.2.4 or higher 
  133. if ( version_compare( $old_version, '1.0.2.4', 'lt' ) ) { 
  134. self::_upgrade_1_0_2_4(); 
  135.  
  136. /** 
  137. * ----- General update logic, executed on every update ------------ 
  138. */ 
  139.  
  140. $settings->version = $new_version; 
  141. $settings->save(); 
  142.  
  143. // Display a message after the page is reloaded. 
  144. if ( ! $is_new_setup ) { 
  145. lib3()->ui->admin_message( implode( '<br>', $msg ), '', '', 'ms-update' ); 
  146.  
  147. do_action( 
  148. 'ms_model_upgrade_after_update',  
  149. $settings,  
  150. $old_version,  
  151. $new_version,  
  152. $force 
  153. ); 
  154.  
  155. $addons = MS_Factory::load( 'MS_Model_Addon' ); 
  156. $addons->flush_list(); 
  157.  
  158. // This will reload the current page. 
  159. MS_Plugin::flush_rewrite_rules(); 
  160.  
  161. /** 
  162. * When WPMU DEV Dashboard is disabled this function will tell WordPress 
  163. * to not update Membership 2. 
  164. * PRO ONLY! 
  165. * @since 1.0.1.2 
  166. * @param mixed $res False: Update plugin / True: WP ignores plugin. 
  167. * @param string $action 
  168. * @param object $args 
  169. * @return mixed 
  170. */ 
  171. static public function no_dash_plugins_api( $res, $action, $args ) { 
  172. if ( ! empty( $args ) && is_object( $args ) ) { 
  173. if ( 'wpmudev_install-1003656' == $args->slug ) { 
  174. $res = true; 
  175. } elseif ( 'wpmudev_install-130' == $args->slug ) { 
  176. $res = true; 
  177. } elseif ( 'membership' == $args->slug ) { 
  178. $res = true; 
  179.  
  180. return $res; 
  181.  
  182. /** 
  183. * Filter the site transient value right before it is returned by the 
  184. * get_site_transient function. 
  185. * We mark the Membership2 plugin for "no update". 
  186. * PRO ONLY! 
  187. * @since 1.0.1.2 
  188. * @param object $data 
  189. * @return object 
  190. */ 
  191. static public function no_dash_update_plugins( $data ) { 
  192. if ( ! empty( $data ) && is_object( $data ) && ! empty( $data->response ) ) { 
  193. if ( isset( $data->response['membership/membership2.php'] ) ) { 
  194. $data->no_update['membership/membership2.php'] = $data->response['membership/membership2.php']; 
  195. unset( $data->response['membership/membership2.php'] ); 
  196.  
  197. return $data; 
  198.  
  199.  
  200. # ########################################################################## 
  201. # ########################################################################## 
  202.  
  203. /** 
  204. * Upgrade from any 1.0.0.x version to a higher version. 
  205. */ 
  206. static private function _upgrade_1_0_1_0() { 
  207. lib3()->updates->clear(); 
  208.  
  209. /** 
  210. * The "is_member" flag of users was not correctly saved when a 
  211. * subscription was added via the M2 > Members page. 
  212. * Fix this now. 
  213. */ 
  214. global $wpdb; 
  215. $sql = " 
  216. SELECT DISTINCT usr.user_id 
  217. FROM 
  218. {$wpdb->posts} post 
  219. LEFT JOIN {$wpdb->usermeta} usr 
  220. ON usr.user_id = post.post_author 
  221. AND usr.meta_key = 'ms_is_member' 
  222. WHERE 
  223. post_type = 'ms_relationship' 
  224. AND (usr.meta_value IS NULL OR usr.meta_value != 1); 
  225. "; 
  226. $result = $wpdb->get_col( $sql ); 
  227. foreach ( $result as $user_id ) { 
  228. lib3()->updates->add( 'update_user_meta', $user_id, 'ms_is_member', true ); 
  229.  
  230. // Execute all queued actions! 
  231. lib3()->updates->plugin( 'membership2' ); 
  232. lib3()->updates->execute(); 
  233.  
  234. /** 
  235. * Upgrade from 1.0.1.0 version to a higher version. 
  236. */ 
  237. static private function _upgrade_1_0_1_1() { 
  238. lib3()->updates->clear(); 
  239.  
  240. /** 
  241. * A bug in 1.0.1 created multiple copies of email templates. 
  242. * This update block will delete the duplicates again. 
  243. */ 
  244. global $wpdb; 
  245. $sql = " 
  246. SELECT ID 
  247. FROM {$wpdb->posts} 
  248. WHERE 
  249. post_type = 'ms_communication' 
  250. AND ID NOT IN ( 
  251. SELECT 
  252. MIN( p.ID ) ID 
  253. FROM 
  254. {$wpdb->posts} p 
  255. INNER JOIN {$wpdb->postmeta} m1 
  256. ON m1.post_id = p.ID AND m1.meta_key = 'type' 
  257. WHERE 
  258. p.post_type = 'ms_communication' 
  259. AND LENGTH( m1.meta_value ) > 0 
  260. GROUP BY 
  261. m1.meta_value,  
  262. p.post_parent 
  263. ); 
  264. "; 
  265. $ids = $wpdb->get_col( $sql ); 
  266.  
  267. foreach ( $ids as $id ) { 
  268. lib3()->updates->add( 'wp_delete_post', $id, true ); 
  269.  
  270. // Execute all queued actions! 
  271. lib3()->updates->plugin( 'membership2' ); 
  272. lib3()->updates->execute(); 
  273.  
  274. /** 
  275. * Upgrade from 1.0.1.1 version to a higher version. 
  276. */ 
  277. static private function _upgrade_1_0_2_0() { 
  278. lib3()->updates->clear(); 
  279.  
  280. /** 
  281. * Transaction logs are a bit messed up because some meta-keys have an 
  282. * underscore as prefix while other do not have it. This query removes 
  283. * the underscore from all transaction-log meta key names. 
  284. */ 
  285. global $wpdb; 
  286. $sql = " 
  287. UPDATE {$wpdb->postmeta} 
  288. INNER JOIN {$wpdb->posts} ON {$wpdb->postmeta}.post_id={$wpdb->posts}.ID 
  289. SET meta_key = SUBSTR(meta_key, 2) 
  290. WHERE 
  291. {$wpdb->posts}.post_type = 'ms_transaction_log' 
  292. AND SUBSTR({$wpdb->postmeta}.meta_key, 1, 1) = '_' 
  293. "; 
  294. $ids = $wpdb->query( $sql ); 
  295.  
  296. /** 
  297. * Upgrade from 1.0.2.x version to 1.0.2.4 version. 
  298. */ 
  299. static private function _upgrade_1_0_2_4() { 
  300. lib3()->updates->clear(); 
  301.  
  302. /** 
  303. * Transaction matching of M1 payments with M2 memberships has improved 
  304. * so a single M2 membership can be matched with multiple transaction 
  305. * types. 
  306. */ 
  307. $memberships = MS_Model_Membership::get_memberships(); 
  308. foreach ( $memberships as $item ) { 
  309. $source_id = $membership->source_id; 
  310. if ( empty( $source_id ) ) { continue; } 
  311.  
  312. $data = lib3()->array->get( 
  313. $membership->get_custom_data( 'matching' ) 
  314. ); 
  315.  
  316. if ( ! isset( $data['m1'] ) ) { $data['m1'] = array(); } 
  317. $data['m1'] = lib3()->array->get( $data['m1'] ); 
  318. $data['m1'][] = $source_id; 
  319. $membership->set_custom_data( 'matching', $data ); 
  320. $membership->save(); 
  321.  
  322. # ########################################################################## 
  323.  
  324. /** 
  325. * Used when upgrading from Membership to M2. If both Membership and 
  326. * Protected Content are installed when upgrading then the old 
  327. * "protected-content" folder may survive the upgrade and needs to be 
  328. * manually removed. 
  329. * @since 1.0.0 
  330. */ 
  331. static private function remove_old_copy() { 
  332. $new_dir = WP_PLUGIN_DIR . '/membership'; 
  333. $old_dir = WP_PLUGIN_DIR . '/protected-content'; 
  334. $old_plugins = array( 
  335. 'protected-content/protected-content.php',  
  336. 'membership/membershippremium.php',  
  337. ); 
  338. $new_plugin = plugin_basename( MS_Plugin::instance()->file ); 
  339.  
  340. // Make sure that the current plugin is the official M2 one. 
  341. if ( false === strpos( MS_Plugin::instance()->dir, $new_dir ) ) { 
  342. // Cancel: This plugin is not the official plugin (maybe a backup or beta version) 
  343.  
  344. if ( false !== strpos( MS_Plugin::instance()->dir, $old_dir ) ) { 
  345. lib3()->ui->admin_message( 
  346. __( '<b>Upgrade warning</b>:<br>The Membership 2 plugin is installed in an deprecated folder. Some users did report issues when the plugin is installed in this directory.<br>To fix this issue please follow these steps:<br><br>1. Delete* the old Membership Premium plugin if it is still installed.<br>2. Delete* the Membership 2 plugin.<br>3. Re-install Membership 2 from the WPMU Dashboard - your existing data is not affected by this.<br><br>*) <em>Only deactivating the plugins does not work, you have to delete them.</em>', 'membership2' ),  
  347. 'error' 
  348. ); 
  349.  
  350. return; 
  351.  
  352. // 1. See if there is a old copy of the plugin directory. Delete it. 
  353. if ( is_dir( $old_dir ) && is_file( $old_dir . '/protected-content.php' ) ) { 
  354. // Looks like the old version of this plugin is still installed. Remove it. 
  355. try { 
  356. unlink( $old_dir . '/protected-content.php' ); 
  357. array_map( 'unlink', glob( "$old_dir/*.*" ) ); 
  358. rmdir( $old_dir ); 
  359. } catch( Exception $e ) { 
  360. // Something went wrong when removing the old plugin. 
  361.   
  362. // 2. See if WordPress uses an old plugin in the DB. Update it. 
  363. if ( is_multisite() ) { 
  364. $global_plugins = (array) get_site_option( 'active_sitewide_plugins', array() ); 
  365. foreach ( $global_plugins as $key => $the_path ) { 
  366. if ( in_array( $the_path, $old_plugins ) ) { 
  367. $global_plugins[$key] = $new_plugin; 
  368. update_site_option( 'active_sitewide_plugins', $global_plugins ); 
  369.   
  370. $site_plugins = (array) get_option( 'active_plugins', array() ); 
  371. foreach ( $site_plugins as $key => $the_path ) { 
  372. if ( in_array( $the_path, $old_plugins ) ) { 
  373. $site_plugins[$key] = $new_plugin; 
  374. update_option( 'active_plugins', $site_plugins ); 
  375.   
  376. # ########################################################################## 
  377. # ########################################################################## 
  378.   
  379.   
  380. /** 
  381. * Completely whipe all Membership data from Database. 
  382. * Note: This function is not used currently... 
  383. * @since 1.0.0 
  384. */ 
  385. static private function cleanup_db() { 
  386. global $wpdb; 
  387. $sql = array(); 
  388. $trash_ids = array(); 
  389.  
  390. // Delete membership meta-data from users. 
  391. $users = MS_Model_Member::get_members(); 
  392. foreach ( $users as $user ) { 
  393. $user->delete_all_membership_usermeta(); 
  394. $user->save(); 
  395.  
  396. // Determine IDs of Membership Pages. 
  397. $page_types = MS_Model_Pages::get_page_types(); 
  398. foreach ( $page_types as $type => $name ) { 
  399. $page_id = MS_Model_Pages::get_setting( $type ); 
  400. $trash_ids[] = $page_id; 
  401.  
  402. /** 
  403. * Delete all plugin settings. 
  404. * Settings are saved by classes that extend MS_Model_option 
  405. */ 
  406. foreach ( MS_Model_Gateway::get_gateways() as $option ) { 
  407. $option->delete(); 
  408. MS_Factory::load( 'MS_Model_Addon' )->delete(); 
  409. MS_Factory::load( 'MS_Model_Pages' )->delete(); 
  410. MS_Factory::load( 'MS_Model_Settings' )->delete(); 
  411.  
  412. /** 
  413. * Delete transient data 
  414. * Transient data is saved by classed that extend MS_Model_Transient 
  415. */ 
  416. MS_Factory::load( 'MS_Model_Simulate' )->delete(); 
  417.  
  418. /** 
  419. * Delete all plugin content. 
  420. * Content is saved by classes that extend MS_Model_CustomPostType 
  421. */ 
  422. $ms_posttypes = array( 
  423. MS_Model_Communication::get_post_type(),  
  424. MS_Model_Event::get_post_type(),  
  425. MS_Model_Invoice::get_post_type(),  
  426. MS_Model_Transactionlog::get_post_type(),  
  427. MS_Model_Membership::get_post_type(),  
  428. MS_Model_Relationship::get_post_type(),  
  429. MS_Addon_Coupon_Model::get_post_type(),  
  430. MS_Addon_Invitation_Model::get_post_type(),  
  431. ); 
  432.  
  433. foreach ( $ms_posttypes as $type ) { 
  434. $sql[] = $wpdb->prepare( 
  435. "DELETE FROM $wpdb->posts WHERE post_type = %s;",  
  436. $type 
  437. ); 
  438.  
  439. // Remove orphaned post-metadata. 
  440. $sql[] = " 
  441. DELETE FROM $wpdb->postmeta 
  442. WHERE NOT EXISTS ( 
  443. SELECT 1 FROM $wpdb->posts tmp WHERE tmp.ID = post_id 
  444. ); 
  445. "; 
  446.  
  447. // Clear all WP transient cache. 
  448. $sql[] = " 
  449. DELETE FROM $wpdb->options 
  450. WHERE option_name LIKE '_transient_%'; 
  451. "; 
  452.  
  453. foreach ( $sql as $s ) { 
  454. $wpdb->query( $s ); 
  455.  
  456. // Move Membership pages to trash. 
  457. foreach ( $trash_ids as $id ) { 
  458. wp_delete_post( $id, true ); 
  459.  
  460. // Clear all data from WP Object cache. 
  461. wp_cache_flush(); 
  462.  
  463. // Redirect to the main page. 
  464. wp_safe_redirect( MS_Controller_Plugin::get_admin_url() ); 
  465. exit; 
  466.  
  467. /** 
  468. * Fix subscriptions with issue where Stripe payments would not go active. 
  469. * @since 1.0.3.4 
  470. */ 
  471. static private function fix_subs() { 
  472. /** 
  473. * Because the issue is only with recurring payments, we get all memberships 
  474. * with a price and recurring payment. 
  475. */ 
  476. $paid_memberships = MS_Model_Membership::get_memberships( array( 
  477. 'meta_query' => array( 
  478. array( 
  479. 'key' => 'price',  
  480. 'value' => 0,  
  481. 'compare' => '>',  
  482. ),  
  483. array( 
  484. 'key' => 'payment_type',  
  485. 'value' => 'recurring',  
  486. ),  
  487. ),  
  488. ) ); 
  489.  
  490. // Loop over the memberships. 
  491. foreach ( $paid_memberships as $membership ) { 
  492.  
  493. // Bug only applies to Stripe. 
  494. if ( ! $membership->can_use_gateway( 'stripeplan' ) ) { 
  495. return; 
  496.  
  497. // Get all the members in the selected membership. 
  498. $members = $membership->get_members( array( 
  499. 'status' => 'all',  
  500. ) ); 
  501.  
  502. // Loop through all the members. 
  503. foreach ( $members as $member ) { 
  504. $subscription = $member->get_subscription( $membership->id ); 
  505.  
  506. // Check if the bug is present. 
  507. if ( $subscription && ( $subscription->current_invoice_number < count( $subscription->get_invoices() ) ) ) { 
  508. $subscription->current_invoice_number = count( $subscription->get_invoices() ); 
  509. $subscription->save(); 
  510.  
  511.  
  512. return; 
  513.  
  514. /** 
  515. * Checks several settings to make sure that M2 is fully working. 
  516. * A) Makes sure that network-wide protection works by ensuring that the 
  517. * plugin is also network-activated. 
  518. * B) Checks if the permalink structure uses the post-name 
  519. * @since 1.0.0 
  520. */ 
  521. static private function check_settings() { 
  522. static $Setting_Check_Done = false; 
  523.  
  524. if ( ! $Setting_Check_Done ) { 
  525. $Setting_Check_Done = true; 
  526.  
  527. // A) Check plugin activation in network-wide mode. 
  528. if ( is_multisite() ) { 
  529.  
  530. if ( MS_Plugin::is_network_wide() ) { 
  531. // This function does not exist in network admin 
  532. if ( ! function_exists( 'is_plugin_active_for_network' ) ) { 
  533. require_once ABSPATH . '/wp-admin/includes/plugin.php'; 
  534.  
  535. if ( ! is_plugin_active_for_network( MS_PLUGIN ) ) { 
  536. activate_plugin( MS_PLUGIN, null, true ); 
  537. lib3()->ui->admin_message( 
  538. __( 'Info: Membership2 is not activated network-wide', 'membership2' ) 
  539. ); 
  540.  
  541. // B) Check the Permalink settings. 
  542. if ( false === strpos( get_option( 'permalink_structure' ), '%postname%' ) ) { 
  543. lib3()->ui->admin_message( 
  544. sprintf( 
  545. __( 'Your %sPermalink structure%s should include the %sPost name%s to ensure Membership 2 is working correctly.', 'membership2' ),  
  546. '<a href="' . admin_url( 'options-permalink.php' ) . '">',  
  547. '</a>',  
  548. '<strong>',  
  549. '</strong>' 
  550. ),  
  551. 'err' 
  552. ); 
  553.  
  554. return; 
  555.  
  556. /** 
  557. * This function checks if we arrive here after a migration, i.e. after the 
  558. * user updated Membership Premium or Protected Content to M2 
  559. * @since 1.0.0 
  560. */ 
  561. static private function check_migration_handler() { 
  562. $migrate = ''; 
  563. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  564.  
  565. // Check Migration from old Membership plugin. 
  566. $option_m1 = '_wpmudev_update_to_m2'; 
  567. $option_m1_free = '_wporg_update_to_m2'; 
  568. $from_m1 = get_site_option( $option_m1 ); 
  569. $from_m1_free = get_site_option( $option_m1_free ); 
  570.  
  571. if ( $from_m1 || $from_m1_free ) { 
  572. $migrate = 'm1'; 
  573.  
  574. delete_site_option( $option_m1 ); 
  575. delete_site_option( $option_m1_free ); 
  576. $settings->set_special_view( 'MS_View_MigrationM1' ); 
  577.  
  578. $view = $settings->get_special_view(); 
  579.  
  580. if ( $migrate || 'MS_View_MigrationM1' == $view ) { 
  581. if ( ! empty( $_REQUEST['skip_import'] ) ) { 
  582. $settings->reset_special_view(); 
  583. wp_safe_redirect( 
  584. esc_url_raw( remove_query_arg( array( 'skip_import' ) ) ) 
  585. ); 
  586. exit; 
  587. } else { 
  588. $settings->set_special_view( 'MS_View_MigrationM1' ); 
  589.  
  590. // Complete the migration when the import is done. 
  591. add_action( 
  592. 'ms_import_action_done',  
  593. array( 'MS_Model_Settings', 'reset_special_view' ) 
  594. ); 
  595.  
  596. /** 
  597. * Returns a secure token to trigger advanced admin actions like db-reset 
  598. * or restoring a snapshot. 
  599. * - Only one token is valid at any given time. 
  600. * - Each token has a timeout of max. 120 seconds. 
  601. * - Each token can be used once only. 
  602. * @since 1.0.0 
  603. * @internal 
  604. * @param string $action Like a nonce, this is the action to execute. 
  605. * @return array Intended usage: add_query_param( $token, $url ) 
  606. */ 
  607. static public function get_token( $action ) { 
  608. if ( ! is_user_logged_in() ) { return array(); } 
  609. if ( ! is_admin() ) { return array(); } 
  610.  
  611. $one_time_key = uniqid(); 
  612. MS_Factory::set_transient( 'ms_one_time_key-' . $action, $one_time_key, 120 ); 
  613.  
  614. // Token is valid for 86 seconds because of usage of date('B') 
  615. $plain = $action . '-' . date( 'B' ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  616. $token = array( 'ms_token' => wp_create_nonce( $plain ) ); 
  617. return $token; 
  618.  
  619. /** 
  620. * Verfies the admin token in the $_GET collection 
  621. * $_GET['ms_token'] must match the current ms_token 
  622. * $_POST['confirm'] must have value 'yes' 
  623. * @since 1.0.0 
  624. * @internal 
  625. * @param string $action Like a nonce, this is the action to execute. 
  626. * @return bool 
  627. */ 
  628. static private function verify_token( $action ) { 
  629. if ( ! self::valid_user() ) { return false; } 
  630.  
  631. if ( empty( $_GET['ms_token'] ) ) { return false; } 
  632. $get_token = $_GET['ms_token']; 
  633.  
  634. if ( empty( $_POST['confirm'] ) ) { return false; } 
  635. if ( 'yes' != $_POST['confirm'] ) { return false; } 
  636.  
  637. $one_time_key = MS_Factory::get_transient( 'ms_one_time_key-' . $action ); 
  638. MS_Factory::delete_transient( 'ms_one_time_key-' . $action ); 
  639. if ( empty( $one_time_key ) ) { return false; } 
  640.  
  641. // We verify the current and the previous beat 
  642. $plain_token_1 = $action . '-' . date( 'B' ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  643. $plain_token_2 = $action . '-' . ( date( 'B' ) - 1 ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  644.  
  645. if ( wp_verify_nonce( $get_token, $plain_token_1 ) ) { return true; } 
  646. if ( wp_verify_nonce( $get_token, $plain_token_2 ) ) { return true; } 
  647.  
  648. return false; 
  649.  
  650. /** 
  651. * Verifies the following conditions: 
  652. * - Current user is logged in and has admin permissions 
  653. * - The request is an wp-admin request 
  654. * - The request is not an Ajax call 
  655. * @since 1.0.0 
  656. * @return bool True if all conditions are true 
  657. */ 
  658. static private function valid_user() { 
  659. /** 
  660. * Determine user_id from request cookies. 
  661. * @see wp-includes/pluggable.php wp_currentuserinfo() 
  662. */ 
  663. $user_id = apply_filters( 'determine_current_user', false ); 
  664.  
  665. if ( ! $user_id ) { return false; } 
  666. if ( ! is_admin() ) { return false; } 
  667. if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return false; } 
  668. if ( defined( 'DOING_CRON' ) && DOING_CRON ) { return false; } 
  669. /** 
  670. * BuddyPress notices hack 
  671. */ 
  672. $bp = function_exists( 'buddypress' ) ? buddypress() : null; 
  673. if( null != $bp ) 
  674. if ( ! did_action( 'init' ) ) { return false; } 
  675. if ( ! current_user_can( 'manage_options' ) ) { return false; } 
  676.  
  677. return true; 
  678.  
  679. /** 
  680. * Checks if valid reset-instructions are present. If yes, then wipe the 
  681. * plugin settings. 
  682. * @since 1.0.0 
  683. */ 
  684. static public function maybe_reset() { 
  685. static $Reset_Done = false; 
  686.  
  687. if ( ! $Reset_Done ) { 
  688. $Reset_Done = true; 
  689. if ( ! self::verify_token( 'reset' ) ) { return false; } 
  690.  
  691. self::cleanup_db(); 
  692. $msg = __( 'Membership 2 successfully reset!', 'membership2' ); 
  693. lib3()->ui->admin_message( $msg ); 
  694.  
  695. wp_safe_redirect( MS_Controller_Plugin::get_admin_url( 'MENU_SLUG' ) ); 
  696. exit; 
  697.  
  698. /** 
  699. * Checks if valid fixsub-instructions are present. If yes, then fix the 
  700. * plugin subscriptions. 
  701. * @since 1.0.3.4 
  702. */ 
  703. static public function maybe_fix_stripe_subs() { 
  704. static $Fix_Done = false; 
  705.  
  706. if ( ! $Fix_Done ) { 
  707. $Fix_Done = true; 
  708. if ( ! self::verify_token( 'fixsub' ) ) { return false; } 
  709.  
  710. self::fix_subs(); 
  711. $msg = __( 'Membership 2 subscriptions fixed!', 'membership2' ); 
  712. lib3()->ui->admin_message( $msg ); 
  713.  
  714. wp_safe_redirect( MS_Controller_Plugin::get_admin_url( 'MENU_SLUG' ) ); 
  715. exit; 
  716.  
  717. /** 
  718. * Checks if valid restore-options are specified. If they are, the snapshot 
  719. * will be restored. 
  720. * @since 1.0.0 
  721. * @internal This function is intended for development/testing only! 
  722. */ 
  723. static private function maybe_restore() { 
  724. static $Restore_Done = false; 
  725.  
  726. if ( ! $Restore_Done ) { 
  727. $Restore_Done = true; 
  728. if ( empty( $_POST['restore_snapshot'] ) ) { return false; } 
  729. $snapshot = $_POST['restore_snapshot']; 
  730.  
  731. if ( ! self::verify_token( 'restore' ) ) { return false; } 
  732.  
  733. lib3()->updates->plugin( 'membership2' ); 
  734. if ( lib3()->updates->restore( $snapshot ) ) { 
  735. printf( 
  736. '<p>' . 
  737. __( 'The Membership2 Snapshot "%s" was restored!', 'membership2' ) . 
  738. '</p>',  
  739. $snapshot 
  740. ); 
  741.  
  742. printf( 
  743. '<p><b>' . 
  744. __( 'To prevent auto-updating the DB again we stop here!', 'membership2' ) . 
  745. '</b></p>' 
  746. ); 
  747.  
  748. printf( 
  749. '<p>' . 
  750. __( 'You now have the option to <br />(A) downgrade the plugin to an earlier version via FTP or <br />(B) to %sre-run the upgrade process%s.', 'membership2' ) . 
  751. '</p>',  
  752. '<a href="' . MS_Controller_Plugin::get_admin_url( 'MENU_SLUG' ) . '">',  
  753. '</a>' 
  754. ); 
  755.  
  756. wp_die( '', 'Snapshot Restored' ); 
  757.  
  758. };