upgrades.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <?php
  2. /**
  3. * acf_has_upgrade
  4. *
  5. * Returns true if this site has an upgrade avaialble.
  6. *
  7. * @date 24/8/18
  8. * @since 5.7.4
  9. *
  10. * @param void
  11. * @return bool
  12. */
  13. function acf_has_upgrade() {
  14. $db_version = acf_get_db_version();
  15. if ( $db_version && acf_version_compare( $db_version, '<', ACF_UPGRADE_VERSION ) ) {
  16. return true;
  17. }
  18. if ( $db_version !== ACF_VERSION ) {
  19. acf_update_db_version( ACF_VERSION );
  20. }
  21. return false;
  22. }
  23. /**
  24. * Runs upgrade routines if this site has an upgrade available.
  25. *
  26. * @date 24/8/18
  27. * @since 5.7.4
  28. */
  29. function acf_upgrade_all() {
  30. // Increase time limit if possible.
  31. if ( function_exists( 'set_time_limit' ) ) {
  32. set_time_limit( 600 );
  33. }
  34. // start timer
  35. timer_start();
  36. // log
  37. acf_dev_log( 'ACF Upgrade Begin.' );
  38. // vars
  39. $db_version = acf_get_db_version();
  40. // 5.0.0
  41. if ( acf_version_compare( $db_version, '<', '5.0.0' ) ) {
  42. acf_upgrade_500();
  43. }
  44. // 5.5.0
  45. if ( acf_version_compare( $db_version, '<', '5.5.0' ) ) {
  46. acf_upgrade_550();
  47. }
  48. /**
  49. * When adding new upgrade routines here, increment the ACF_UPGRADE_VERSION
  50. * constant in `acf.php` to the new highest upgrade version.
  51. */
  52. // upgrade DB version once all updates are complete
  53. acf_update_db_version( ACF_VERSION );
  54. if ( is_multisite() ) {
  55. // Clears the network upgrade notification banner after site upgrades.
  56. delete_site_transient( 'acf_network_upgrade_needed_' . ACF_UPGRADE_VERSION );
  57. }
  58. // log
  59. global $wpdb;
  60. acf_dev_log( 'ACF Upgrade Complete.', $wpdb->num_queries, timer_stop( 0 ) );
  61. }
  62. /**
  63. * acf_get_db_version
  64. *
  65. * Returns the ACF DB version.
  66. *
  67. * @date 10/09/2016
  68. * @since 5.4.0
  69. *
  70. * @param void
  71. * @return string
  72. */
  73. function acf_get_db_version() {
  74. return get_option( 'acf_version' );
  75. }
  76. /*
  77. * acf_update_db_version
  78. *
  79. * Updates the ACF DB version.
  80. *
  81. * @date 10/09/2016
  82. * @since 5.4.0
  83. *
  84. * @param string $version The new version.
  85. * @return void
  86. */
  87. function acf_update_db_version( $version = '' ) {
  88. update_option( 'acf_version', $version );
  89. }
  90. /**
  91. * acf_upgrade_500
  92. *
  93. * Version 5 introduces new post types for field groups and fields.
  94. *
  95. * @date 23/8/18
  96. * @since 5.7.4
  97. *
  98. * @param void
  99. * @return void
  100. */
  101. function acf_upgrade_500() {
  102. // log
  103. acf_dev_log( 'ACF Upgrade 5.0.0.' );
  104. // action
  105. do_action( 'acf/upgrade_500' );
  106. // do tasks
  107. acf_upgrade_500_field_groups();
  108. // update version
  109. acf_update_db_version( '5.0.0' );
  110. }
  111. /**
  112. * acf_upgrade_500_field_groups
  113. *
  114. * Upgrades all ACF4 field groups to ACF5
  115. *
  116. * @date 23/8/18
  117. * @since 5.7.4
  118. *
  119. * @param void
  120. * @return void
  121. */
  122. function acf_upgrade_500_field_groups() {
  123. // log
  124. acf_dev_log( 'ACF Upgrade 5.0.0 Field Groups.' );
  125. // get old field groups
  126. $ofgs = get_posts(
  127. array(
  128. 'numberposts' => -1,
  129. 'post_type' => 'acf',
  130. 'orderby' => 'menu_order title',
  131. 'order' => 'asc',
  132. 'suppress_filters' => true,
  133. )
  134. );
  135. // loop
  136. if ( $ofgs ) {
  137. foreach ( $ofgs as $ofg ) {
  138. acf_upgrade_500_field_group( $ofg );
  139. }
  140. }
  141. }
  142. /**
  143. * acf_upgrade_500_field_group
  144. *
  145. * Upgrades a ACF4 field group to ACF5
  146. *
  147. * @date 23/8/18
  148. * @since 5.7.4
  149. *
  150. * @param object $ofg The old field group post object.
  151. * @return array $nfg The new field group array.
  152. */
  153. function acf_upgrade_500_field_group( $ofg ) {
  154. // log
  155. acf_dev_log( 'ACF Upgrade 5.0.0 Field Group.', $ofg );
  156. // vars
  157. $nfg = array(
  158. 'ID' => 0,
  159. 'title' => $ofg->post_title,
  160. 'menu_order' => $ofg->menu_order,
  161. );
  162. // construct the location rules
  163. $rules = get_post_meta( $ofg->ID, 'rule', false );
  164. $anyorall = get_post_meta( $ofg->ID, 'allorany', true );
  165. if ( is_array( $rules ) ) {
  166. // if field group was duplicated, rules may be a serialized string!
  167. $rules = array_map( 'maybe_unserialize', $rules );
  168. // convert rules to groups
  169. $nfg['location'] = acf_convert_rules_to_groups( $rules, $anyorall );
  170. }
  171. // settings
  172. if ( $position = get_post_meta( $ofg->ID, 'position', true ) ) {
  173. $nfg['position'] = $position;
  174. }
  175. if ( $layout = get_post_meta( $ofg->ID, 'layout', true ) ) {
  176. $nfg['layout'] = $layout;
  177. }
  178. if ( $hide_on_screen = get_post_meta( $ofg->ID, 'hide_on_screen', true ) ) {
  179. $nfg['hide_on_screen'] = maybe_unserialize( $hide_on_screen );
  180. }
  181. // save field group
  182. // acf_upgrade_field_group will call the acf_get_valid_field_group function and apply 'compatibility' changes
  183. $nfg = acf_update_field_group( $nfg );
  184. // log
  185. acf_dev_log( '> Complete.', $nfg );
  186. // action for 3rd party
  187. do_action( 'acf/upgrade_500_field_group', $nfg, $ofg );
  188. // upgrade fields
  189. acf_upgrade_500_fields( $ofg, $nfg );
  190. // trash?
  191. if ( $ofg->post_status == 'trash' ) {
  192. acf_trash_field_group( $nfg['ID'] );
  193. }
  194. // return
  195. return $nfg;
  196. }
  197. /**
  198. * acf_upgrade_500_fields
  199. *
  200. * Upgrades all ACF4 fields to ACF5 from a specific field group
  201. *
  202. * @date 23/8/18
  203. * @since 5.7.4
  204. *
  205. * @param object $ofg The old field group post object.
  206. * @param array $nfg The new field group array.
  207. * @return void
  208. */
  209. function acf_upgrade_500_fields( $ofg, $nfg ) {
  210. // log
  211. acf_dev_log( 'ACF Upgrade 5.0.0 Fields.' );
  212. // global
  213. global $wpdb;
  214. // get field from postmeta
  215. $rows = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND meta_key LIKE %s", $ofg->ID, 'field_%' ), ARRAY_A );
  216. // check
  217. if ( $rows ) {
  218. // vars
  219. $checked = array();
  220. // loop
  221. foreach ( $rows as $row ) {
  222. // vars
  223. $field = $row['meta_value'];
  224. $field = maybe_unserialize( $field );
  225. $field = maybe_unserialize( $field ); // run again for WPML
  226. // bail early if key already migrated (potential duplicates in DB)
  227. if ( isset( $checked[ $field['key'] ] ) ) {
  228. continue;
  229. }
  230. $checked[ $field['key'] ] = 1;
  231. // add parent
  232. $field['parent'] = $nfg['ID'];
  233. // migrate field
  234. $field = acf_upgrade_500_field( $field );
  235. }
  236. }
  237. }
  238. /**
  239. * acf_upgrade_500_field
  240. *
  241. * Upgrades a ACF4 field to ACF5
  242. *
  243. * @date 23/8/18
  244. * @since 5.7.4
  245. *
  246. * @param array $field The old field.
  247. * @return array $field The new field.
  248. */
  249. function acf_upgrade_500_field( $field ) {
  250. // log
  251. acf_dev_log( 'ACF Upgrade 5.0.0 Field.', $field );
  252. // order_no is now menu_order
  253. $field['menu_order'] = acf_extract_var( $field, 'order_no', 0 );
  254. // correct very old field keys (field2 => field_2)
  255. if ( substr( $field['key'], 0, 6 ) !== 'field_' ) {
  256. $field['key'] = 'field_' . str_replace( 'field', '', $field['key'] );
  257. }
  258. // extract sub fields
  259. $sub_fields = array();
  260. if ( $field['type'] == 'repeater' ) {
  261. // loop over sub fields
  262. if ( ! empty( $field['sub_fields'] ) ) {
  263. foreach ( $field['sub_fields'] as $sub_field ) {
  264. $sub_fields[] = $sub_field;
  265. }
  266. }
  267. // remove sub fields from field
  268. unset( $field['sub_fields'] );
  269. } elseif ( $field['type'] == 'flexible_content' ) {
  270. // loop over layouts
  271. if ( is_array( $field['layouts'] ) ) {
  272. foreach ( $field['layouts'] as $i => $layout ) {
  273. // generate key
  274. $layout['key'] = uniqid( 'layout_' );
  275. // loop over sub fields
  276. if ( ! empty( $layout['sub_fields'] ) ) {
  277. foreach ( $layout['sub_fields'] as $sub_field ) {
  278. $sub_field['parent_layout'] = $layout['key'];
  279. $sub_fields[] = $sub_field;
  280. }
  281. }
  282. // remove sub fields from layout
  283. unset( $layout['sub_fields'] );
  284. // update
  285. $field['layouts'][ $i ] = $layout;
  286. }
  287. }
  288. }
  289. // save field
  290. $field = acf_update_field( $field );
  291. // log
  292. acf_dev_log( '> Complete.', $field );
  293. // sub fields
  294. if ( $sub_fields ) {
  295. foreach ( $sub_fields as $sub_field ) {
  296. $sub_field['parent'] = $field['ID'];
  297. acf_upgrade_500_field( $sub_field );
  298. }
  299. }
  300. // action for 3rd party
  301. do_action( 'acf/update_500_field', $field );
  302. // return
  303. return $field;
  304. }
  305. /**
  306. * acf_upgrade_550
  307. *
  308. * Version 5.5 adds support for the wp_termmeta table added in WP 4.4.
  309. *
  310. * @date 23/8/18
  311. * @since 5.7.4
  312. *
  313. * @param void
  314. * @return void
  315. */
  316. function acf_upgrade_550() {
  317. // log
  318. acf_dev_log( 'ACF Upgrade 5.5.0.' );
  319. // action
  320. do_action( 'acf/upgrade_550' );
  321. // do tasks
  322. acf_upgrade_550_termmeta();
  323. // update version
  324. acf_update_db_version( '5.5.0' );
  325. }
  326. /**
  327. * acf_upgrade_550_termmeta
  328. *
  329. * Upgrades all ACF4 termmeta saved in wp_options to the wp_termmeta table.
  330. *
  331. * @date 23/8/18
  332. * @since 5.7.4
  333. *
  334. * @param void
  335. * @return void
  336. */
  337. function acf_upgrade_550_termmeta() {
  338. // log
  339. acf_dev_log( 'ACF Upgrade 5.5.0 Termmeta.' );
  340. // bail early if no wp_termmeta table
  341. if ( get_option( 'db_version' ) < 34370 ) {
  342. return;
  343. }
  344. // get all taxonomies
  345. $taxonomies = get_taxonomies( false, 'objects' );
  346. // loop
  347. if ( $taxonomies ) {
  348. foreach ( $taxonomies as $taxonomy ) {
  349. acf_upgrade_550_taxonomy( $taxonomy->name );
  350. }
  351. }
  352. // action for 3rd party
  353. do_action( 'acf/upgrade_550_termmeta' );
  354. }
  355. /*
  356. * acf_wp_upgrade_550_termmeta
  357. *
  358. * When the database is updated to support term meta, migrate ACF term meta data across.
  359. *
  360. * @date 23/8/18
  361. * @since 5.7.4
  362. *
  363. * @param string $wp_db_version The new $wp_db_version.
  364. * @param string $wp_current_db_version The old (current) $wp_db_version.
  365. * @return void
  366. */
  367. function acf_wp_upgrade_550_termmeta( $wp_db_version, $wp_current_db_version ) {
  368. if ( $wp_db_version >= 34370 && $wp_current_db_version < 34370 ) {
  369. if ( acf_version_compare( acf_get_db_version(), '>', '5.5.0' ) ) {
  370. acf_upgrade_550_termmeta();
  371. }
  372. }
  373. }
  374. add_action( 'wp_upgrade', 'acf_wp_upgrade_550_termmeta', 10, 2 );
  375. /**
  376. * acf_upgrade_550_taxonomy
  377. *
  378. * Upgrades all ACF4 termmeta for a specific taxonomy.
  379. *
  380. * @date 24/8/18
  381. * @since 5.7.4
  382. *
  383. * @param string $taxonomy The taxonomy name.
  384. * @return void
  385. */
  386. function acf_upgrade_550_taxonomy( $taxonomy ) {
  387. // log
  388. acf_dev_log( 'ACF Upgrade 5.5.0 Taxonomy.', $taxonomy );
  389. // global
  390. global $wpdb;
  391. // vars
  392. $search = $taxonomy . '_%';
  393. $_search = '_' . $search;
  394. // escape '_'
  395. // http://stackoverflow.com/questions/2300285/how-do-i-escape-in-sql-server
  396. $search = str_replace( '_', '\_', $search );
  397. $_search = str_replace( '_', '\_', $_search );
  398. // search
  399. // results show faster query times using 2 LIKE vs 2 wildcards
  400. $rows = $wpdb->get_results(
  401. $wpdb->prepare(
  402. "SELECT *
  403. FROM $wpdb->options
  404. WHERE option_name LIKE %s
  405. OR option_name LIKE %s",
  406. $search,
  407. $_search
  408. ),
  409. ARRAY_A
  410. );
  411. // loop
  412. if ( $rows ) {
  413. foreach ( $rows as $row ) {
  414. /*
  415. Use regex to find "(_)taxonomy_(term_id)_(field_name)" and populate $matches:
  416. Array
  417. (
  418. [0] => _category_3_color
  419. [1] => _
  420. [2] => 3
  421. [3] => color
  422. )
  423. */
  424. if ( ! preg_match( "/^(_?){$taxonomy}_(\d+)_(.+)/", $row['option_name'], $matches ) ) {
  425. continue;
  426. }
  427. // vars
  428. $term_id = $matches[2];
  429. $meta_key = $matches[1] . $matches[3];
  430. $meta_value = $row['option_value'];
  431. // update
  432. // memory usage reduced by 50% by using a manual insert vs update_metadata() function.
  433. // update_metadata( 'term', $term_id, $meta_name, $meta_value );
  434. $wpdb->insert(
  435. $wpdb->termmeta,
  436. array(
  437. 'term_id' => $term_id,
  438. 'meta_key' => $meta_key,
  439. 'meta_value' => $meta_value,
  440. )
  441. );
  442. // log
  443. acf_dev_log( 'ACF Upgrade 5.5.0 Term.', $term_id, $meta_key );
  444. // action
  445. do_action( 'acf/upgrade_550_taxonomy_term', $term_id );
  446. }
  447. }
  448. // action for 3rd party
  449. do_action( 'acf/upgrade_550_taxonomy', $taxonomy );
  450. }