/app/model/class-ms-model-upgrade.php

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