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