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. // Prevent WordPress from updating the Membership plugin when the 
  19. // WPMU DEV Dashboard is disabled. 
  20.  
  21. // PRO ONLY! 
  22.  
  23. do_action( 'ms_model_upgrade_init' ); 
  24.  
  25. /** 
  26. * Upgrade database. 
  27. * @since 1.0.0 
  28. * @param bool $force Also execute update logic when version did not change. 
  29. */ 
  30. public static function update( $force = false ) { 
  31. static $Done = false; 
  32.  
  33. if ( $Done && ! $force ) { return; } 
  34.  
  35. // Migration handler has its own valid_user check. 
  36. self::check_migration_handler(); 
  37.  
  38. // Updates are only triggered from Admin-Side by an Admin user. 
  39. if ( ! self::valid_user() ) { return; } 
  40.  
  41. // Check for correct network-wide protection setup. 
  42. self::check_settings(); 
  43.  
  44. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  45. $old_version = $settings->version; // Old: The version in DB. 
  46. $new_version = MS_Plugin::instance()->version; // New: Version in file. 
  47.  
  48. $is_new_setup = empty( $old_version ); 
  49.  
  50. // Compare current src version to DB version: 
  51. // We only do UP-grades but no DOWN-grades! 
  52. if ( $old_version ) { 
  53. $version_changed = version_compare( $old_version, $new_version, 'lt' ); 
  54. } else { 
  55. $version_changed = true; 
  56.  
  57. if ( $force || $version_changed ) { 
  58. $Done = true; 
  59. $msg = array(); 
  60.  
  61. /** 
  62. * ----- General update logic, executed on every update ------------ 
  63. */ 
  64.  
  65. do_action( 
  66. 'ms_model_upgrade_before_update',  
  67. $settings,  
  68. $old_version,  
  69. $new_version,  
  70. $force 
  71. ); 
  72.  
  73. // Prepare the Update message. 
  74. if ( ! $version_changed ) { 
  75. $msg[] = sprintf( 
  76. __( '<strong>Membership 2</strong> is set up for version %1$s!' , 'membership2' ),  
  77. $new_version 
  78. ); 
  79. } else { 
  80. $msg[] = sprintf( 
  81. __( '<strong>Membership 2</strong> was updated to version %1$s!' , 'membership2' ),  
  82. $new_version 
  83. ); 
  84.  
  85. // Every time the plugin is updated we clear the cache. 
  86. MS_Factory::clear(); 
  87.  
  88. // Create missing Membership pages. 
  89. $new_pages = MS_Model_Pages::create_missing_pages(); 
  90.  
  91. if ( ! empty( $new_pages ) ) { 
  92. $msg[] = sprintf( 
  93. __( 'New Membership pages created: "%1$s".', 'membership2' ),  
  94. implode( '", "', $new_pages ) 
  95. ); 
  96.  
  97. // Remove an old version of Protected Content 
  98. // TODO: REMOVE THIS BLOCK/FUNCTION END OF 2015 
  99. if ( $version_changed ) { 
  100. self::remove_old_copy(); 
  101.  
  102. // Note: We do not create menu items on upgrade! Users might have 
  103. // intentionally removed the items from the menu... 
  104.  
  105. /** 
  106. * ----- Version-Specific update logic ----------------------------- 
  107. */ 
  108.  
  109. // Upgrade from a 1.0.0.x version to 1.0.1.0 or higher 
  110. if ( version_compare( $old_version, '4.0.0.4', 'lt' ) ) { 
  111. self::_upgrade_1_0_1_0(); 
  112.  
  113. // Upgrade from 1.0.1.0 version to 1.0.1.1 or higher 
  114. // ONLY RELEVANT FOR PRO VERSION! 
  115.  
  116. // Upgrade from 1.0.1.x version to 1.0.2.0 or higher 
  117. if ( version_compare( $old_version, '4.0.0.6', 'lt' ) ) { 
  118. self::_upgrade_1_0_2_0(); 
  119.  
  120. /** 
  121. * ----- General update logic, executed on every update ------------ 
  122. */ 
  123.  
  124. $settings->version = $new_version; 
  125. $settings->save(); 
  126.  
  127. // Display a message after the page is reloaded. 
  128. if ( ! $is_new_setup ) { 
  129. lib3()->ui->admin_message( implode( '<br>', $msg ), '', '', 'ms-update' ); 
  130.  
  131. do_action( 
  132. 'ms_model_upgrade_after_update',  
  133. $settings,  
  134. $old_version,  
  135. $new_version,  
  136. $force 
  137. ); 
  138.  
  139. $addons = MS_Factory::load( 'MS_Model_Addon' ); 
  140. $addons->flush_list(); 
  141.  
  142. // This will reload the current page. 
  143. MS_Plugin::flush_rewrite_rules(); 
  144.  
  145. /** 
  146. * PRO ONLY! 
  147. * @since 1.0.1.2 
  148. * @param mixed $res False: Update plugin / True: WP ignores plugin. 
  149. * @param string $action 
  150. * @param object $args 
  151. * @return mixed 
  152. */ 
  153. static public function no_dash_plugins_api( $res, $action, $args ) { 
  154. // PRO ONLY! 
  155.  
  156. /** 
  157. * PRO ONLY! 
  158. * @since 1.0.1.2 
  159. * @param object $data 
  160. * @return object 
  161. */ 
  162. static public function no_dash_update_plugins( $data ) { 
  163. // PRO ONLY! 
  164.  
  165.  
  166. # ########################################################################## 
  167. # ########################################################################## 
  168.  
  169. /** 
  170. * Upgrade from any 1.0.0.x version to a higher version. 
  171. */ 
  172. static private function _upgrade_1_0_1_0() { 
  173. lib3()->updates->clear(); 
  174.  
  175. /** 
  176. * The "is_member" flag of users was not correctly saved when a 
  177. * subscription was added via the M2 > Members page. 
  178. * Fix this now. 
  179. */ 
  180. global $wpdb; 
  181. $sql = " 
  182. SELECT DISTINCT usr.user_id 
  183. FROM 
  184. {$wpdb->posts} post 
  185. LEFT JOIN {$wpdb->usermeta} usr 
  186. ON usr.user_id = post.post_author 
  187. AND usr.meta_key = 'ms_is_member' 
  188. WHERE 
  189. post_type = 'ms_relationship' 
  190. AND (usr.meta_value IS NULL OR usr.meta_value != 1); 
  191. "; 
  192. $result = $wpdb->get_col( $sql ); 
  193. foreach ( $result as $user_id ) { 
  194. lib3()->updates->add( 'update_user_meta', $user_id, 'ms_is_member', true ); 
  195.  
  196. // Execute all queued actions! 
  197. lib3()->updates->plugin( 'membership2' ); 
  198. lib3()->updates->execute(); 
  199.  
  200. /** 
  201. * Upgrade from 1.0.1.0 version to a higher version. 
  202. * ONLY RELEVANT FOR PRO VERSION! 
  203. */ 
  204. static private function _upgrade_1_0_1_1() { 
  205.  
  206. /** 
  207. * Upgrade from 1.0.1.1 version to a higher version. 
  208. */ 
  209. static private function _upgrade_1_0_2_0() { 
  210. lib3()->updates->clear(); 
  211.  
  212. /** 
  213. * Transaction logs are a bit messed up because some meta-keys have an 
  214. * underscore as prefix while other do not have it. This query removes 
  215. * the underscore from all transaction-log meta key names. 
  216. */ 
  217. global $wpdb; 
  218. $sql = " 
  219. UPDATE {$wpdb->postmeta} 
  220. INNER JOIN {$wpdb->posts} ON {$wpdb->postmeta}.post_id={$wpdb->posts}.ID 
  221. SET meta_key = SUBSTR(meta_key, 2) 
  222. WHERE 
  223. {$wpdb->posts}.post_type = 'ms_transaction_log' 
  224. AND SUBSTR({$wpdb->postmeta}.meta_key, 1, 1) = '_' 
  225. "; 
  226. $ids = $wpdb->query( $sql ); 
  227.  
  228. # ########################################################################## 
  229.  
  230. /** 
  231. * Used when upgrading from Membership to M2. If both Membership and 
  232. * Protected Content are installed when upgrading then the old 
  233. * "protected-content" folder may survive the upgrade and needs to be 
  234. * manually removed. 
  235. * @since 1.0.0 
  236. */ 
  237. static private function remove_old_copy() { 
  238. $new_dir = WP_PLUGIN_DIR . '/membership'; 
  239. // ONLY RELEVANT IN PRO VERSION! 
  240. return; 
  241.  
  242. # ########################################################################## 
  243. # ########################################################################## 
  244.  
  245.  
  246. /** 
  247. * Completely whipe all Membership data from Database. 
  248. * Note: This function is not used currently... 
  249. * @since 1.0.0 
  250. */ 
  251. static private function cleanup_db() { 
  252. global $wpdb; 
  253. $sql = array(); 
  254. $trash_ids = array(); 
  255.  
  256. // Delete membership meta-data from users. 
  257. $users = MS_Model_Member::get_members(); 
  258. foreach ( $users as $user ) { 
  259. $user->delete_all_membership_usermeta(); 
  260. $user->save(); 
  261.  
  262. // Determine IDs of Membership Pages. 
  263. $page_types = MS_Model_Pages::get_page_types(); 
  264. foreach ( $page_types as $type => $name ) { 
  265. $page_id = MS_Model_Pages::get_setting( $type ); 
  266. $trash_ids[] = $page_id; 
  267.  
  268. /** 
  269. * Delete all plugin settings. 
  270. * Settings are saved by classes that extend MS_Model_option 
  271. */ 
  272. foreach ( MS_Model_Gateway::get_gateways() as $option ) { 
  273. $option->delete(); 
  274. MS_Factory::load( 'MS_Model_Addon' )->delete(); 
  275. MS_Factory::load( 'MS_Model_Pages' )->delete(); 
  276. MS_Factory::load( 'MS_Model_Settings' )->delete(); 
  277.  
  278. /** 
  279. * Delete transient data 
  280. * Transient data is saved by classed that extend MS_Model_Transient 
  281. */ 
  282. MS_Factory::load( 'MS_Model_Simulate' )->delete(); 
  283.  
  284. /** 
  285. * Delete all plugin content. 
  286. * Content is saved by classes that extend MS_Model_CustomPostType 
  287. */ 
  288. $ms_posttypes = array( 
  289. MS_Model_Communication::get_post_type(),  
  290. MS_Model_Event::get_post_type(),  
  291. MS_Model_Invoice::get_post_type(),  
  292. MS_Model_Transactionlog::get_post_type(),  
  293. MS_Model_Membership::get_post_type(),  
  294. MS_Model_Relationship::get_post_type(),  
  295. // MS_Addon_Coupon_Model::get_post_type(), ONLY PRO 
  296. // MS_Addon_Invitation_Model::get_post_type(), ONLY PRO 
  297. ); 
  298.  
  299. foreach ( $ms_posttypes as $type ) { 
  300. $sql[] = $wpdb->prepare( 
  301. "DELETE FROM $wpdb->posts WHERE post_type = %s;",  
  302. $type 
  303. ); 
  304.  
  305. // Remove orphaned post-metadata. 
  306. $sql[] = " 
  307. DELETE FROM $wpdb->postmeta 
  308. WHERE NOT EXISTS ( 
  309. SELECT 1 FROM $wpdb->posts tmp WHERE tmp.ID = post_id 
  310. ); 
  311. "; 
  312.  
  313. // Clear all WP transient cache. 
  314. $sql[] = " 
  315. DELETE FROM $wpdb->options 
  316. WHERE option_name LIKE '_transient_%'; 
  317. "; 
  318.  
  319. foreach ( $sql as $s ) { 
  320. $wpdb->query( $s ); 
  321.  
  322. // Move Membership pages to trash. 
  323. foreach ( $trash_ids as $id ) { 
  324. wp_delete_post( $id, true ); 
  325.  
  326. // Clear all data from WP Object cache. 
  327. wp_cache_flush(); 
  328.  
  329. // Redirect to the main page. 
  330. wp_safe_redirect( MS_Controller_Plugin::get_admin_url() ); 
  331. exit; 
  332.  
  333. /** 
  334. * Checks several settings to make sure that M2 is fully working. 
  335. * A) Makes sure that network-wide protection works by ensuring that the 
  336. * plugin is also network-activated. 
  337. * B) Checks if the permalink structure uses the post-name 
  338. * @since 1.0.0 
  339. */ 
  340. static private function check_settings() { 
  341. static $Setting_Check_Done = false; 
  342.  
  343. if ( ! $Setting_Check_Done ) { 
  344. $Setting_Check_Done = true; 
  345.  
  346. // A) ONLY RELEVANT IN PRO VERSION! 
  347.  
  348. // B) Check the Permalink settings. 
  349. if ( false === strpos( get_option( 'permalink_structure' ), '%postname%' ) ) { 
  350. lib3()->ui->admin_message( 
  351. sprintf( 
  352. __( 'Your %sPermalink structure%s should include the %sPost name%s to ensure Membership 2 is working correctly.', 'membership2' ),  
  353. '<a href="' . admin_url( 'options-permalink.php' ) . '">',  
  354. '</a>',  
  355. '<strong>',  
  356. '</strong>' 
  357. ),  
  358. 'err' 
  359. ); 
  360.  
  361. return; 
  362.  
  363. /** 
  364. * This function checks if we arrive here after a migration, i.e. after the 
  365. * user updated Membership Premium or Protected Content to M2 
  366. * @since 1.0.0 
  367. */ 
  368. static private function check_migration_handler() { 
  369. $migrate = ''; 
  370. $settings = MS_Factory::load( 'MS_Model_Settings' ); 
  371.  
  372. // Check Migration from old Membership plugin. 
  373. $option_m1 = '_wpmudev_update_to_m2'; 
  374. $option_m1_free = '_wporg_update_to_m2'; 
  375. $from_m1 = get_site_option( $option_m1 ); 
  376. $from_m1_free = get_site_option( $option_m1_free ); 
  377.  
  378. if ( $from_m1 || $from_m1_free ) { 
  379. $migrate = 'm1'; 
  380.  
  381. delete_site_option( $option_m1 ); 
  382. delete_site_option( $option_m1_free ); 
  383. $settings->set_special_view( 'MS_View_MigrationM1' ); 
  384.  
  385. $view = $settings->get_special_view(); 
  386.  
  387. if ( $migrate || 'MS_View_MigrationM1' == $view ) { 
  388. if ( ! empty( $_REQUEST['skip_import'] ) ) { 
  389. $settings->reset_special_view(); 
  390. wp_safe_redirect( 
  391. esc_url_raw( remove_query_arg( array( 'skip_import' ) ) ) 
  392. ); 
  393. exit; 
  394. } else { 
  395. $settings->set_special_view( 'MS_View_MigrationM1' ); 
  396.  
  397. // Complete the migration when the import is done. 
  398. add_action( 
  399. 'ms_import_action_done',  
  400. array( 'MS_Model_Settings', 'reset_special_view' ) 
  401. ); 
  402.  
  403. /** 
  404. * Returns a secure token to trigger advanced admin actions like db-reset 
  405. * or restoring a snapshot. 
  406. * - Only one token is valid at any given time. 
  407. * - Each token has a timeout of max. 120 seconds. 
  408. * - Each token can be used once only. 
  409. * @since 1.0.0 
  410. * @internal 
  411. * @param string $action Like a nonce, this is the action to execute. 
  412. * @return array Intended usage: add_query_param( $token, $url ) 
  413. */ 
  414. static public function get_token( $action ) { 
  415. if ( ! is_user_logged_in() ) { return array(); } 
  416. if ( ! is_admin() ) { return array(); } 
  417.  
  418. $one_time_key = uniqid(); 
  419. MS_Factory::set_transient( 'ms_one_time_key-' . $action, $one_time_key, 120 ); 
  420.  
  421. // Token is valid for 86 seconds because of usage of date('B') 
  422. $plain = $action . '-' . date( 'B' ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  423. $token = array( 'ms_token' => wp_create_nonce( $plain ) ); 
  424. return $token; 
  425.  
  426. /** 
  427. * Verfies the admin token in the $_GET collection 
  428. * $_GET['ms_token'] must match the current ms_token 
  429. * $_POST['confirm'] must have value 'yes' 
  430. * @since 1.0.0 
  431. * @internal 
  432. * @param string $action Like a nonce, this is the action to execute. 
  433. * @return bool 
  434. */ 
  435. static private function verify_token( $action ) { 
  436. if ( ! self::valid_user() ) { return false; } 
  437.  
  438. if ( empty( $_GET['ms_token'] ) ) { return false; } 
  439. $get_token = $_GET['ms_token']; 
  440.  
  441. if ( empty( $_POST['confirm'] ) ) { return false; } 
  442. if ( 'yes' != $_POST['confirm'] ) { return false; } 
  443.  
  444. $one_time_key = MS_Factory::get_transient( 'ms_one_time_key-' . $action ); 
  445. MS_Factory::delete_transient( 'ms_one_time_key-' . $action ); 
  446. if ( empty( $one_time_key ) ) { return false; } 
  447.  
  448. // We verify the current and the previous beat 
  449. $plain_token_1 = $action . '-' . date( 'B' ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  450. $plain_token_2 = $action . '-' . ( date( 'B' ) - 1 ) . ':' . get_current_user_id() . '-' . $one_time_key; 
  451.  
  452. if ( wp_verify_nonce( $get_token, $plain_token_1 ) ) { return true; } 
  453. if ( wp_verify_nonce( $get_token, $plain_token_2 ) ) { return true; } 
  454.  
  455. return false; 
  456.  
  457. /** 
  458. * Verifies the following conditions: 
  459. * - Current user is logged in and has admin permissions 
  460. * - The request is an wp-admin request 
  461. * - The request is not an Ajax call 
  462. * @since 1.0.0 
  463. * @return bool True if all conditions are true 
  464. */ 
  465. static private function valid_user() { 
  466. /** 
  467. * Determine user_id from request cookies. 
  468. * @see wp-includes/pluggable.php wp_currentuserinfo() 
  469. */ 
  470. $user_id = apply_filters( 'determine_current_user', false ); 
  471.  
  472. if ( ! $user_id ) { return false; } 
  473. if ( ! is_admin() ) { return false; } 
  474. if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { return false; } 
  475. if ( defined( 'DOING_CRON' ) && DOING_CRON ) { return false; } 
  476. if ( ! current_user_can( 'manage_options' ) ) { return false; } 
  477.  
  478. return true; 
  479.  
  480. /** 
  481. * Checks if valid reset-instructions are present. If yes, then whipe the 
  482. * plugin settings. 
  483. * @since 1.0.0 
  484. */ 
  485. static public function maybe_reset() { 
  486. static $Reset_Done = false; 
  487.  
  488. if ( ! $Reset_Done ) { 
  489. $Reset_Done = true; 
  490. if ( ! self::verify_token( 'reset' ) ) { return false; } 
  491.  
  492. self::cleanup_db(); 
  493. $msg = __( 'Membership 2 successfully reset!', 'membership2' ); 
  494. lib3()->ui->admin_message( $msg ); 
  495.  
  496. wp_safe_redirect( MS_Controller_Plugin::get_admin_url( 'MENU_SLUG' ) ); 
  497. exit; 
  498.  
  499. /** 
  500. * Checks if valid restore-options are specified. If they are, the snapshot 
  501. * will be restored. 
  502. * @since 1.0.0 
  503. * @internal This function is intended for development/testing only! 
  504. */ 
  505. static private function maybe_restore() { 
  506. static $Restore_Done = false; 
  507.  
  508. if ( ! $Restore_Done ) { 
  509. $Restore_Done = true; 
  510. if ( empty( $_POST['restore_snapshot'] ) ) { return false; } 
  511. $snapshot = $_POST['restore_snapshot']; 
  512.  
  513. if ( ! self::verify_token( 'restore' ) ) { return false; } 
  514.  
  515. lib3()->updates->plugin( 'membership2' ); 
  516. if ( lib3()->updates->restore( $snapshot ) ) { 
  517. printf( 
  518. '<p>' . 
  519. __( 'The Membership2 Snapshot "%s" was restored!', 'membership2' ) . 
  520. '</p>',  
  521. $snapshot 
  522. ); 
  523.  
  524. printf( 
  525. '<p><b>' . 
  526. __( 'To prevent auto-updating the DB again we stop here!', 'membership2' ) . 
  527. '</b></p>' 
  528. ); 
  529.  
  530. printf( 
  531. '<p>' . 
  532. __( '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' ) . 
  533. '</p>',  
  534. '<a href="' . MS_Controller_Plugin::get_admin_url( 'MENU_SLUG' ) . '">',  
  535. '</a>' 
  536. ); 
  537.  
  538. wp_die( '', 'Snapshot Restored' ); 
  539.  
  540. };