class-fl-builder-update.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <?php
  2. /**
  3. * Helper class for builder updates.
  4. *
  5. * @since 1.2.8
  6. */
  7. final class FLBuilderUpdate {
  8. /**
  9. * Initialize hooks.
  10. *
  11. * @since 1.8
  12. * @return void
  13. */
  14. static public function init() {
  15. add_action( 'init', __CLASS__ . '::maybe_run', 11 );
  16. }
  17. /**
  18. * Checks to see if an update should be run. If it should,
  19. * the appropriate update method is run and the version
  20. * number is updated in the database.
  21. *
  22. * @since 1.2.8
  23. * @return void
  24. */
  25. static public function maybe_run() {
  26. // Make sure the user is logged in.
  27. if ( ! is_user_logged_in() ) {
  28. return;
  29. }
  30. // Don't update for dev copies.
  31. if ( FL_BUILDER_VERSION == '{FL_BUILDER_VERSION}' ) {
  32. return;
  33. }
  34. // Get the saved version.
  35. $saved_version = get_site_option( '_fl_builder_version' );
  36. // No saved version number. This must be a fresh install.
  37. if ( ! $saved_version ) {
  38. update_site_option( '_fl_builder_version', FL_BUILDER_VERSION );
  39. return;
  40. } elseif ( ! version_compare( $saved_version, FL_BUILDER_VERSION, '=' ) ) {
  41. if ( is_multisite() ) {
  42. self::run_multisite( $saved_version );
  43. } else {
  44. self::run( $saved_version );
  45. }
  46. update_site_option( '_fl_builder_version', FL_BUILDER_VERSION );
  47. update_site_option( '_fl_builder_update_info', array(
  48. 'from' => $saved_version,
  49. 'to' => FL_BUILDER_VERSION,
  50. ) );
  51. }
  52. }
  53. /**
  54. * Runs the update for a specific version.
  55. *
  56. * @since 1.2.8
  57. * @access private
  58. * @return void
  59. */
  60. static private function run( $saved_version ) {
  61. // Update to 1.2.8 or greater.
  62. if ( version_compare( $saved_version, '1.2.8', '<' ) ) {
  63. self::v_1_2_8();
  64. }
  65. // Update to 1.4.6 or greater.
  66. if ( version_compare( $saved_version, '1.4.6', '<' ) ) {
  67. self::v_1_4_6();
  68. }
  69. // Update to 1.6.3 or greater.
  70. if ( version_compare( $saved_version, '1.6.3', '<' ) ) {
  71. self::v_1_6_3();
  72. }
  73. // Update to 1.10 or greater.
  74. if ( version_compare( $saved_version, '1.10', '<' ) ) {
  75. self::v_1_10();
  76. }
  77. // Clear all asset cache.
  78. FLBuilderModel::delete_asset_cache_for_all_posts();
  79. // Flush the rewrite rules.
  80. flush_rewrite_rules();
  81. }
  82. /**
  83. * Runs the update for all sites on a network install.
  84. *
  85. * @since 1.2.8
  86. * @access private
  87. * @return void
  88. */
  89. static private function run_multisite( $saved_version ) {
  90. global $blog_id;
  91. global $wpdb;
  92. // Network update to 1.10 or greater.
  93. if ( version_compare( $saved_version, '1.10', '<' ) ) {
  94. self::v_1_10( true );
  95. }
  96. // Save the original blog id.
  97. $original_blog_id = $blog_id;
  98. // Get all blog ids.
  99. $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
  100. // Loop through the blog ids and run the update.
  101. foreach ( $blog_ids as $id ) {
  102. switch_to_blog( $id );
  103. self::run( $saved_version );
  104. }
  105. // Revert to the original blog.
  106. switch_to_blog( $original_blog_id );
  107. }
  108. /**
  109. * Check for the fl_builder_nodes table that existed before 1.2.8.
  110. *
  111. * @since 1.2.8
  112. * @access private
  113. * @return bool
  114. */
  115. static private function pre_1_2_8_table_exists() {
  116. global $wpdb;
  117. $table = $wpdb->prefix . 'fl_builder_nodes';
  118. $results = $wpdb->get_results( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
  119. return count( $results ) > 0;
  120. }
  121. /**
  122. * Check to see if the fl_builder_nodes table that existed before 1.2.8
  123. * is empty or not.
  124. *
  125. * @since 1.2.8
  126. * @access private
  127. * @return bool
  128. */
  129. static private function pre_1_2_8_table_is_empty() {
  130. global $wpdb;
  131. if ( self::pre_1_2_8_table_exists() ) {
  132. $table = $wpdb->prefix . 'fl_builder_nodes';
  133. $nodes = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %s', $table ) );
  134. return count( $nodes ) === 0;
  135. }
  136. return true;
  137. }
  138. /**
  139. * Saves a backup of the pre 1.2.8 database table.
  140. *
  141. * @since 1.2.8
  142. * @access private
  143. * @return void
  144. */
  145. static private function pre_1_2_8_backup() {
  146. global $wpdb;
  147. if ( self::pre_1_2_8_table_exists() ) {
  148. $cache_dir = FLBuilderModel::get_cache_dir();
  149. $table = $wpdb->prefix . 'fl_builder_nodes';
  150. // Get the data to backup.
  151. $nodes = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %s', $table ) );
  152. $meta = $wpdb->get_results( "SELECT * FROM {$wpdb->postmeta} WHERE meta_key = '_fl_builder_layout'" );
  153. // Build the export object.
  154. $data = new StdClass();
  155. $data->version = FL_BUILDER_VERSION;
  156. $data->nodes = $nodes;
  157. $data->meta = $meta;
  158. // Save the backup.
  159. fl_builder_filesystem()->file_put_contents( $cache_dir['path'] . 'backup.dat', serialize( $data ) );
  160. }
  161. }
  162. /**
  163. * Restores a site to pre 1.2.8.
  164. *
  165. * @since 1.2.8
  166. * @access private
  167. * @return void
  168. */
  169. static private function pre_1_2_8_restore() {
  170. global $wpdb;
  171. if ( ! self::pre_1_2_8_table_exists() || self::pre_1_2_8_table_is_empty() ) {
  172. $cache_dir = FLBuilderModel::get_cache_dir();
  173. $backup_path = $cache_dir['path'] . 'backup.dat';
  174. // Install the database.
  175. FLBuilderModel::install_database();
  176. // Check for the backup file.
  177. if ( file_exists( $backup_path ) ) {
  178. // Get the backup data.
  179. $backup = unserialize( file_get_contents( $backup_path ) );
  180. // Check for the correct backup data.
  181. if ( ! isset( $backup->nodes ) || ! isset( $backup->meta ) ) {
  182. return;
  183. }
  184. // Restore the nodes.
  185. foreach ( $backup->nodes as $node ) {
  186. $wpdb->insert("{$wpdb->prefix}fl_builder_nodes",
  187. array(
  188. 'node' => $node->node,
  189. 'type' => $node->type,
  190. 'layout' => $node->layout,
  191. 'parent' => $node->parent,
  192. 'position' => $node->position,
  193. 'settings' => $node->settings,
  194. 'status' => $node->status,
  195. ),
  196. array( '%s', '%s', '%s', '%s', '%d', '%s', '%s' )
  197. );
  198. }
  199. // Restore the meta.
  200. foreach ( $backup->meta as $meta ) {
  201. update_post_meta( $meta->post_id, '_fl_builder_layout', $meta->meta_value );
  202. }
  203. }
  204. }
  205. }
  206. /**
  207. * Update to version 1.2.8 or later.
  208. *
  209. * @since 1.2.8
  210. * @access private
  211. * @return void
  212. */
  213. static private function v_1_2_8() {
  214. global $wpdb;
  215. if ( self::pre_1_2_8_table_exists() ) {
  216. $table = $wpdb->prefix . 'fl_builder_nodes';
  217. $metas = $wpdb->get_results( "SELECT * FROM {$wpdb->postmeta} WHERE meta_key = '_fl_builder_layout'" );
  218. $cache_dir = FLBuilderModel::get_cache_dir();
  219. // Loop through the layout ids for each post.
  220. foreach ( $metas as $meta ) {
  221. // Get the old layout nodes from the database.
  222. $published = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM %s WHERE layout = %s AND status = 'published'", $table, $meta->meta_value ) );
  223. $draft = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM %s WHERE layout = %s AND status = 'draft'", $table, $meta->meta_value ) );
  224. // Convert the old nodes to new ones.
  225. $published = self::v_1_2_8_convert_nodes( $published );
  226. $draft = self::v_1_2_8_convert_nodes( $draft );
  227. // Add the new layout post meta.
  228. update_post_meta( $meta->post_id, '_fl_builder_data', $published );
  229. update_post_meta( $meta->post_id, '_fl_builder_draft', $draft );
  230. }
  231. // Backup the old builder table.
  232. self::pre_1_2_8_backup();
  233. // Drop the old builder table.
  234. if ( file_exists( $cache_dir['path'] . 'backup.dat' ) ) {
  235. $wpdb->query( "DROP TABLE {$wpdb->prefix}fl_builder_nodes" );
  236. }
  237. // Delete old post meta.
  238. delete_post_meta_by_key( '_fl_builder_layout' );
  239. delete_post_meta_by_key( '_fl_builder_layout_export' );
  240. delete_post_meta_by_key( '_fl_builder_css' );
  241. delete_post_meta_by_key( '_fl_builder_css-draft' );
  242. delete_post_meta_by_key( '_fl_builder_js' );
  243. delete_post_meta_by_key( '_fl_builder_js-draft' );
  244. // Convert global settings.
  245. self::v_1_2_8_convert_global_settings();
  246. // Delete all asset cache.
  247. $css = glob( $cache_dir['path'] . '*.css' );
  248. $js = glob( $cache_dir['path'] . '*.js' );
  249. if ( is_array( $css ) ) {
  250. array_map( array( fl_builder_filesystem(), 'unlink' ), $css );
  251. }
  252. if ( is_array( $js ) ) {
  253. array_map( array( fl_builder_filesystem(), 'unlink' ), $js );
  254. }
  255. }
  256. }
  257. /**
  258. * Convert the global settings for 1.2.8 or later.
  259. *
  260. * @since 1.2.8
  261. * @access private
  262. * @return void
  263. */
  264. static private function v_1_2_8_convert_global_settings() {
  265. $settings = get_option( '_fl_builder_settings' );
  266. if ( $settings && is_string( $settings ) ) {
  267. update_option( '_fl_builder_settings', json_decode( $settings ) );
  268. }
  269. }
  270. /**
  271. * Convert the nodes for 1.2.8 or earlier.
  272. *
  273. * @since 1.2.8
  274. * @access private
  275. * @param array $nodes An array of node data.
  276. * @return array
  277. */
  278. static private function v_1_2_8_convert_nodes( $nodes ) {
  279. $new_nodes = array();
  280. foreach ( $nodes as $node ) {
  281. unset( $node->id );
  282. unset( $node->layout );
  283. unset( $node->status );
  284. if ( 'row' == $node->type ) {
  285. $node->parent = null;
  286. }
  287. $node->settings = self::v_1_2_8_json_decode_settings( $node->settings );
  288. $new_nodes[ $node->node ] = $node;
  289. }
  290. return $new_nodes;
  291. }
  292. /**
  293. * Convert a JSON encoded settings string for 1.2.8 or earlier.
  294. *
  295. * @since 1.2.8
  296. * @access private
  297. * @param object $settings The settings object.
  298. * @return object
  299. */
  300. static private function v_1_2_8_json_decode_settings( $settings ) {
  301. if ( ! $settings || empty( $settings ) ) {
  302. return null;
  303. }
  304. $settings = json_decode( $settings );
  305. foreach ( $settings as $key => $val ) {
  306. if ( is_string( $val ) ) {
  307. $decoded = json_decode( $val );
  308. if ( is_object( $decoded ) || is_array( $decoded ) ) {
  309. $settings->{$key} = $decoded;
  310. }
  311. } elseif ( is_array( $val ) ) {
  312. foreach ( $val as $sub_key => $sub_val ) {
  313. if ( is_string( $sub_val ) ) {
  314. $decoded = json_decode( $sub_val );
  315. if ( is_object( $decoded ) || is_array( $decoded ) ) {
  316. $settings->{$key}[ $sub_key ] = $decoded;
  317. }
  318. }
  319. }
  320. }
  321. }
  322. return $settings;
  323. }
  324. /**
  325. * Update to version 1.4.6 or later.
  326. *
  327. * @since 1.4.6
  328. * @access private
  329. * @return void
  330. */
  331. static private function v_1_4_6() {
  332. // Remove the old fl-builder uploads folder.
  333. $upload_dir = wp_upload_dir( null, false );
  334. $path = trailingslashit( $upload_dir['basedir'] ) . 'fl-builder';
  335. if ( file_exists( $path ) ) {
  336. fl_builder_filesystem()->rmdir( $path, true );
  337. }
  338. }
  339. /**
  340. * Update to version 1.6.3 or later.
  341. *
  342. * @since 1.6.3
  343. * @access private
  344. * @return void
  345. */
  346. static private function v_1_6_3() {
  347. $posts = get_posts( array(
  348. 'post_type' => 'fl-builder-template',
  349. 'posts_per_page' => '-1',
  350. ) );
  351. foreach ( $posts as $post ) {
  352. $type = wp_get_post_terms( $post->ID, 'fl-builder-template-type' );
  353. if ( 0 === count( $type ) ) {
  354. wp_set_post_terms( $post->ID, 'layout', 'fl-builder-template-type' );
  355. }
  356. }
  357. }
  358. /**
  359. * Update to version 1.10 or later.
  360. *
  361. * @since 1.10
  362. * @access private
  363. * @return void
  364. */
  365. static private function v_1_10( $network = false ) {
  366. if ( ! function_exists( 'get_editable_roles' ) ) {
  367. require_once( ABSPATH . 'wp-admin/includes/user.php' );
  368. }
  369. $roles = get_editable_roles();
  370. $user_access = array();
  371. $unrestricted = self::v_1_10_convert_cap_to_roles( '_fl_builder_editing_capability', $roles, $network );
  372. $global_templates = self::v_1_10_convert_cap_to_roles( '_fl_builder_global_templates_editing_capability', $roles, $network );
  373. $builder_admin = self::v_1_10_convert_option_to_roles( '_fl_builder_user_templates_admin', $roles, $network );
  374. $template_exporter = self::v_1_10_convert_option_to_roles( '_fl_builder_template_data_exporter', $roles, $network );
  375. if ( ! empty( $unrestricted ) ) {
  376. $user_access['unrestricted_editing'] = $unrestricted;
  377. }
  378. if ( ! empty( $global_templates ) ) {
  379. $user_access['global_node_editing'] = $global_templates;
  380. }
  381. if ( ! empty( $builder_admin ) ) {
  382. $user_access['builder_admin'] = $builder_admin;
  383. }
  384. if ( ! empty( $template_exporter ) ) {
  385. $user_access['template_data_exporter'] = $template_exporter;
  386. }
  387. if ( ! empty( $user_access ) ) {
  388. if ( $network ) {
  389. update_site_option( '_fl_builder_user_access', $user_access );
  390. } else {
  391. update_option( '_fl_builder_user_access', $user_access );
  392. }
  393. }
  394. }
  395. /**
  396. * Convert an old editing capability to a role settings.
  397. *
  398. * @since 1.10
  399. * @access private
  400. * @return array
  401. */
  402. static private function v_1_10_convert_cap_to_roles( $key, $roles, $network = false ) {
  403. $option = $network ? get_site_option( $key ) : get_option( $key );
  404. $data = array();
  405. if ( ! empty( $option ) ) {
  406. if ( $network ) {
  407. delete_site_option( $key );
  408. } else {
  409. delete_option( $key );
  410. }
  411. $option = explode( ',', $option );
  412. foreach ( $roles as $role_key => $role_data ) {
  413. if ( ! isset( $role_data['capabilities']['edit_posts'] ) ) {
  414. continue;
  415. }
  416. $data[ $role_key ] = false;
  417. foreach ( $option as $cap ) {
  418. if ( isset( $role_data['capabilities'][ trim( $cap ) ] ) ) {
  419. $data[ $role_key ] = true;
  420. break;
  421. }
  422. }
  423. }
  424. }
  425. return $data;
  426. }
  427. /**
  428. * Convert old options to user access roles.
  429. *
  430. * @since 1.10
  431. * @access private
  432. * @return array
  433. */
  434. static private function v_1_10_convert_option_to_roles( $key, $roles, $network = false ) {
  435. $option = $network ? get_site_option( $key ) : get_option( $key );
  436. $enabled = ! empty( $option ) && $option;
  437. $data = array();
  438. if ( ! empty( $option ) ) {
  439. if ( $network ) {
  440. delete_site_option( $key );
  441. } else {
  442. delete_option( $key );
  443. }
  444. foreach ( $roles as $role_key => $role_data ) {
  445. if ( ! isset( $role_data['capabilities']['edit_posts'] ) ) {
  446. continue;
  447. }
  448. $data[ $role_key ] = $enabled;
  449. }
  450. }
  451. return $data;
  452. }
  453. }
  454. FLBuilderUpdate::init();