Нема описа

admin-field-groups.php 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  1. <?php
  2. if ( ! defined( 'ABSPATH' ) ) {
  3. exit; // Exit if accessed directly
  4. }
  5. if ( ! class_exists( 'ACF_Admin_Field_Groups' ) ) :
  6. class ACF_Admin_Field_Groups {
  7. /**
  8. * Array of field groups availbale for sync.
  9. *
  10. * @since 5.9.0
  11. * @var array
  12. */
  13. public $sync = array();
  14. /**
  15. * The current view (post_status).
  16. *
  17. * @since 5.9.0
  18. * @var string
  19. */
  20. public $view = '';
  21. /**
  22. * Constructor.
  23. *
  24. * @date 5/03/2014
  25. * @since 5.0.0
  26. *
  27. * @param void
  28. * @return void
  29. */
  30. public function __construct() {
  31. // Add hooks.
  32. add_action( 'load-edit.php', array( $this, 'handle_redirection' ) );
  33. add_action( 'current_screen', array( $this, 'current_screen' ) );
  34. // Handle post status change events.
  35. add_action( 'trashed_post', array( $this, 'trashed_post' ) );
  36. add_action( 'untrashed_post', array( $this, 'untrashed_post' ) );
  37. add_action( 'deleted_post', array( $this, 'deleted_post' ) );
  38. }
  39. /**
  40. * Returns the Field Groups admin URL.
  41. *
  42. * @date 27/3/20
  43. * @since 5.9.0
  44. *
  45. * @param string $params Extra URL params.
  46. * @return string
  47. */
  48. public function get_admin_url( $params = '' ) {
  49. return admin_url( "edit.php?post_type=acf-field-group{$params}" );
  50. }
  51. /**
  52. * Returns the Field Groups admin URL taking into account the current view.
  53. *
  54. * @date 27/3/20
  55. * @since 5.9.0
  56. *
  57. * @param string $params Extra URL params.
  58. * @return string
  59. */
  60. public function get_current_admin_url( $params = '' ) {
  61. return $this->get_admin_url( ( $this->view ? '&post_status=' . $this->view : '' ) . $params );
  62. }
  63. /**
  64. * Redirects users from ACF 4.0 admin page.
  65. *
  66. * @date 17/9/18
  67. * @since 5.7.6
  68. *
  69. * @param void
  70. * @return void
  71. */
  72. public function handle_redirection() {
  73. if ( isset( $_GET['post_type'] ) && $_GET['post_type'] === 'acf' ) {
  74. wp_redirect( $this->get_admin_url() );
  75. exit;
  76. }
  77. }
  78. /**
  79. * Constructor for the Field Groups admin page.
  80. *
  81. * @date 21/07/2014
  82. * @since 5.0.0
  83. *
  84. * @param void
  85. * @return void
  86. */
  87. public function current_screen() {
  88. // Bail early if not Field Groups admin page.
  89. if ( ! acf_is_screen( 'edit-acf-field-group' ) ) {
  90. return;
  91. }
  92. // Get the current view.
  93. $this->view = isset( $_GET['post_status'] ) ? sanitize_text_field( $_GET['post_status'] ) : '';
  94. // Setup and check for custom actions..
  95. $this->setup_sync();
  96. $this->check_sync();
  97. $this->check_duplicate();
  98. // Modify publish post status text and order.
  99. global $wp_post_statuses;
  100. $wp_post_statuses['publish']->label_count = _n_noop( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', 'acf' );
  101. $wp_post_statuses['trash'] = acf_extract_var( $wp_post_statuses, 'trash' );
  102. // Add hooks.
  103. add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
  104. add_action( 'admin_body_class', array( $this, 'admin_body_class' ) );
  105. add_filter( 'views_edit-acf-field-group', array( $this, 'admin_table_views' ), 10, 1 );
  106. add_filter( 'manage_acf-field-group_posts_columns', array( $this, 'admin_table_columns' ), 10, 1 );
  107. add_action( 'manage_acf-field-group_posts_custom_column', array( $this, 'admin_table_columns_html' ), 10, 2 );
  108. add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
  109. add_filter( 'bulk_actions-edit-acf-field-group', array( $this, 'admin_table_bulk_actions' ), 10, 1 );
  110. add_action( 'admin_footer', array( $this, 'admin_footer' ), 1 );
  111. if ( $this->view !== 'trash' ) {
  112. add_filter( 'page_row_actions', array( $this, 'page_row_actions' ), 10, 2 );
  113. }
  114. // Add hooks for "sync" view.
  115. if ( $this->view === 'sync' ) {
  116. add_action( 'admin_footer', array( $this, 'admin_footer__sync' ), 1 );
  117. }
  118. }
  119. /**
  120. * Sets up the field groups ready for sync.
  121. *
  122. * @date 17/4/20
  123. * @since 5.9.0
  124. *
  125. * @param void
  126. * @return void
  127. */
  128. public function setup_sync() {
  129. // Review local json field groups.
  130. if ( acf_get_local_json_files() ) {
  131. // Get all groups in a single cached query to check if sync is available.
  132. $all_field_groups = acf_get_field_groups();
  133. foreach ( $all_field_groups as $field_group ) {
  134. // Extract vars.
  135. $local = acf_maybe_get( $field_group, 'local' );
  136. $modified = acf_maybe_get( $field_group, 'modified' );
  137. $private = acf_maybe_get( $field_group, 'private' );
  138. // Ignore if is private.
  139. if ( $private ) {
  140. continue;
  141. // Ignore not local "json".
  142. } elseif ( $local !== 'json' ) {
  143. continue;
  144. // Append to sync if not yet in database.
  145. } elseif ( ! $field_group['ID'] ) {
  146. $this->sync[ $field_group['key'] ] = $field_group;
  147. // Append to sync if "json" modified time is newer than database.
  148. } elseif ( $modified && $modified > get_post_modified_time( 'U', true, $field_group['ID'] ) ) {
  149. $this->sync[ $field_group['key'] ] = $field_group;
  150. }
  151. }
  152. }
  153. }
  154. /**
  155. * Enqueues admin scripts.
  156. *
  157. * @date 18/4/20
  158. * @since 5.9.0
  159. *
  160. * @param void
  161. * @return void
  162. */
  163. public function admin_enqueue_scripts() {
  164. acf_enqueue_script( 'acf' );
  165. // Localize text.
  166. acf_localize_text(
  167. array(
  168. 'Review local JSON changes' => __( 'Review local JSON changes', 'acf' ),
  169. 'Loading diff' => __( 'Loading diff', 'acf' ),
  170. 'Sync changes' => __( 'Sync changes', 'acf' ),
  171. )
  172. );
  173. }
  174. /**
  175. * Modifies the admin body class.
  176. *
  177. * @date 18/4/20
  178. * @since 5.9.0
  179. *
  180. * @param string $classes Space-separated list of CSS classes.
  181. * @return string
  182. */
  183. public function admin_body_class( $classes ) {
  184. $classes .= ' acf-admin-field-groups';
  185. if ( $this->view ) {
  186. $classes .= " view-{$this->view}";
  187. }
  188. return $classes;
  189. }
  190. /**
  191. * returns the disabled post state HTML.
  192. *
  193. * @date 17/4/20
  194. * @since 5.9.0
  195. *
  196. * @param void
  197. * @return string
  198. */
  199. public function get_disabled_post_state() {
  200. return '<span class="dashicons dashicons-hidden"></span> ' . _x( 'Disabled', 'post status', 'acf' );
  201. }
  202. /**
  203. * Adds the "disabled" post state for the admin table title.
  204. *
  205. * @date 1/4/20
  206. * @since 5.9.0
  207. *
  208. * @param array $post_states An array of post display states.
  209. * @param WP_Post $post The current post object.
  210. * @return array
  211. */
  212. public function display_post_states( $post_states, $post ) {
  213. if ( $post->post_status === 'acf-disabled' ) {
  214. $post_states['acf-disabled'] = $this->get_disabled_post_state();
  215. }
  216. return $post_states;
  217. }
  218. /**
  219. * Customizes the admin table columns.
  220. *
  221. * @date 1/4/20
  222. * @since 5.9.0
  223. *
  224. * @param array $columns The columns array.
  225. * @return array
  226. */
  227. public function admin_table_columns( $_columns ) {
  228. $columns = array(
  229. 'cb' => $_columns['cb'],
  230. 'title' => $_columns['title'],
  231. 'acf-description' => __( 'Description', 'acf' ),
  232. 'acf-key' => __( 'Key', 'acf' ),
  233. 'acf-location' => __( 'Location', 'acf' ),
  234. 'acf-count' => __( 'Fields', 'acf' ),
  235. );
  236. if ( acf_get_local_json_files() ) {
  237. $columns['acf-json'] = __( 'Local JSON', 'acf' );
  238. }
  239. return $columns;
  240. }
  241. /**
  242. * Renders the admin table column HTML
  243. *
  244. * @date 1/4/20
  245. * @since 5.9.0
  246. *
  247. * @param string $column_name The name of the column to display.
  248. * @param int $post_id The current post ID.
  249. * @return void
  250. */
  251. public function admin_table_columns_html( $column_name, $post_id ) {
  252. $field_group = acf_get_field_group( $post_id );
  253. if ( $field_group ) {
  254. $this->render_admin_table_column( $column_name, $field_group );
  255. }
  256. }
  257. /**
  258. * Renders a specific admin table column.
  259. *
  260. * @date 17/4/20
  261. * @since 5.9.0
  262. *
  263. * @param string $column_name The name of the column to display.
  264. * @param array $field_group The field group.
  265. * @return void
  266. */
  267. public function render_admin_table_column( $column_name, $field_group ) {
  268. switch ( $column_name ) {
  269. // Key.
  270. case 'acf-key':
  271. echo esc_html( $field_group['key'] );
  272. break;
  273. // Description.
  274. case 'acf-description':
  275. if ( $field_group['description'] ) {
  276. echo '<span class="acf-description">' . acf_esc_html( $field_group['description'] ) . '</span>';
  277. }
  278. break;
  279. // Location.
  280. case 'acf-location':
  281. $this->render_admin_table_column_locations( $field_group );
  282. break;
  283. // Count.
  284. case 'acf-count':
  285. echo esc_html( acf_get_field_count( $field_group ) );
  286. break;
  287. // Local JSON.
  288. case 'acf-json':
  289. $this->render_admin_table_column_local_status( $field_group );
  290. break;
  291. }
  292. }
  293. /**
  294. * Displays a visual representation of the field group's locations.
  295. *
  296. * @date 1/4/20
  297. * @since 5.9.0
  298. *
  299. * @param array $field_group The field group.
  300. * @return void
  301. */
  302. public function render_admin_table_column_locations( $field_group ) {
  303. $objects = array();
  304. // Loop over location rules and determine connected object types.
  305. if ( $field_group['location'] ) {
  306. foreach ( $field_group['location'] as $i => $rules ) {
  307. // Determine object types for each rule.
  308. foreach ( $rules as $j => $rule ) {
  309. // Get location type and subtype for the current rule.
  310. $location = acf_get_location_rule( $rule['param'] );
  311. $location_object_type = '';
  312. $location_object_subtype = '';
  313. if ( $location ) {
  314. $location_object_type = $location->get_object_type( $rule );
  315. $location_object_subtype = $location->get_object_subtype( $rule );
  316. }
  317. $rules[ $j ]['object_type'] = $location_object_type;
  318. $rules[ $j ]['object_subtype'] = $location_object_subtype;
  319. }
  320. // Now that each $rule conains object type data...
  321. $object_types = array_column( $rules, 'object_type' );
  322. $object_types = array_filter( $object_types );
  323. $object_types = array_values( $object_types );
  324. if ( $object_types ) {
  325. $object_type = $object_types[0];
  326. } else {
  327. continue;
  328. }
  329. $object_subtypes = array_column( $rules, 'object_subtype' );
  330. $object_subtypes = array_filter( $object_subtypes );
  331. $object_subtypes = array_values( $object_subtypes );
  332. $object_subtypes = array_map( 'acf_array', $object_subtypes );
  333. if ( count( $object_subtypes ) > 1 ) {
  334. $object_subtypes = call_user_func_array( 'array_intersect', $object_subtypes );
  335. $object_subtypes = array_values( $object_subtypes );
  336. } elseif ( $object_subtypes ) {
  337. $object_subtypes = $object_subtypes[0];
  338. } else {
  339. $object_subtypes = array( '' );
  340. }
  341. // Append to objects.
  342. foreach ( $object_subtypes as $object_subtype ) {
  343. $object = acf_get_object_type( $object_type, $object_subtype );
  344. if ( $object ) {
  345. $objects[ $object->name ] = $object;
  346. }
  347. }
  348. }
  349. }
  350. // Reset keys.
  351. $objects = array_values( $objects );
  352. // Display.
  353. $html = '';
  354. if ( $objects ) {
  355. $limit = 3;
  356. $total = count( $objects );
  357. // Icon.
  358. $html .= '<span class="dashicons ' . $objects[0]->icon . ( $total > 1 ? ' acf-multi-dashicon' : '' ) . '"></span> ';
  359. // Labels.
  360. $labels = array_column( $objects, 'label' );
  361. $labels = array_slice( $labels, 0, 3 );
  362. $html .= implode( ', ', $labels );
  363. // More.
  364. if ( $total > $limit ) {
  365. $html .= ', ...';
  366. }
  367. } else {
  368. $html = '<span class="dashicons dashicons-businesswoman"></span> ' . __( 'Various', 'acf' );
  369. }
  370. // Filter.
  371. echo acf_esc_html( $html );
  372. }
  373. /**
  374. * Returns a human readable file location.
  375. *
  376. * @date 17/4/20
  377. * @since 5.9.0
  378. *
  379. * @param string $file The full file path.
  380. * @return string
  381. */
  382. public function get_human_readable_file_location( $file ) {
  383. // Generate friendly file path.
  384. $theme_path = get_stylesheet_directory();
  385. if ( strpos( $file, $theme_path ) !== false ) {
  386. $rel_file = str_replace( $theme_path, '', $file );
  387. $located = sprintf( __( 'Located in theme: %s', 'acf' ), $rel_file );
  388. } elseif ( strpos( $file, WP_PLUGIN_DIR ) !== false ) {
  389. $rel_file = str_replace( WP_PLUGIN_DIR, '', $file );
  390. $located = sprintf( __( 'Located in plugin: %s', 'acf' ), $rel_file );
  391. } else {
  392. $rel_file = str_replace( ABSPATH, '', $file );
  393. $located = sprintf( __( 'Located in: %s', 'acf' ), $rel_file );
  394. }
  395. return $located;
  396. }
  397. /**
  398. * Displays the local JSON status of a field group.
  399. *
  400. * @date 14/4/20
  401. * @since 5.9.0
  402. *
  403. * @param type $var Description. Default.
  404. * @return type Description.
  405. */
  406. public function render_admin_table_column_local_status( $field_group ) {
  407. $json = acf_get_local_json_files();
  408. if ( isset( $json[ $field_group['key'] ] ) ) {
  409. $file = $json[ $field_group['key'] ];
  410. if ( isset( $this->sync[ $field_group['key'] ] ) ) {
  411. $url = $this->get_admin_url( '&acfsync=' . $field_group['key'] . '&_wpnonce=' . wp_create_nonce( 'bulk-posts' ) );
  412. echo '<strong>' . __( 'Sync available', 'acf' ) . '</strong>';
  413. if ( $field_group['ID'] ) {
  414. echo '<div class="row-actions">
  415. <span class="sync"><a href="' . esc_url( $url ) . '">' . __( 'Sync', 'acf' ) . '</a> | </span>
  416. <span class="review"><a href="#" data-event="review-sync" data-id="' . esc_attr( $field_group['ID'] ) . '" data-href="' . esc_url( $url ) . '">' . __( 'Review changes', 'acf' ) . '</a></span>
  417. </div>';
  418. } else {
  419. echo '<div class="row-actions">
  420. <span class="sync"><a href="' . esc_url( $url ) . '">' . __( 'Import', 'acf' ) . '</a></span>
  421. </div>';
  422. }
  423. } else {
  424. echo __( 'Saved', 'acf' );
  425. }
  426. } else {
  427. echo '<span class="acf-secondary-text">' . __( 'Awaiting save', 'acf' ) . '</span>';
  428. }
  429. }
  430. /**
  431. * Customizes the page row actions visible on hover.
  432. *
  433. * @date 14/4/20
  434. * @since 5.9.0
  435. *
  436. * @param array $actions The array of actions HTML.
  437. * @param WP_Post $post The post.
  438. * @return array
  439. */
  440. public function page_row_actions( $actions, $post ) {
  441. // Remove "Quick Edit" action.
  442. unset( $actions['inline'], $actions['inline hide-if-no-js'] );
  443. // Append "Duplicate" action.
  444. $duplicate_action_url = $this->get_admin_url( '&acfduplicate=' . $post->ID . '&_wpnonce=' . wp_create_nonce( 'bulk-posts' ) );
  445. $actions['acfduplicate'] = '<a href="' . esc_url( $duplicate_action_url ) . '" aria-label="' . esc_attr__( 'Duplicate this item', 'acf' ) . '">' . __( 'Duplicate', 'acf' ) . '</a>';
  446. // Return actions in custom order.
  447. $order = array( 'edit', 'acfduplicate', 'trash' );
  448. return array_merge( array_flip( $order ), $actions );
  449. }
  450. /**
  451. * Modifies the admin table bulk actions dropdown.
  452. *
  453. * @date 15/4/20
  454. * @since 5.9.0
  455. *
  456. * @param array $actions The actions array.
  457. * @return array
  458. */
  459. public function admin_table_bulk_actions( $actions ) {
  460. // Add "duplicate" action.
  461. if ( $this->view !== 'sync' ) {
  462. $actions['acfduplicate'] = __( 'Duplicate', 'acf' );
  463. }
  464. // Add "Sync" action.
  465. if ( $this->sync ) {
  466. if ( $this->view === 'sync' ) {
  467. $actions = array();
  468. }
  469. $actions['acfsync'] = __( 'Sync changes', 'acf' );
  470. }
  471. return $actions;
  472. }
  473. /**
  474. * Checks for the custom "duplicate" action.
  475. *
  476. * @date 15/4/20
  477. * @since 5.9.0
  478. *
  479. * @param void
  480. * @return void
  481. */
  482. public function check_duplicate() {
  483. // Display notice on success redirect.
  484. if ( isset( $_GET['acfduplicatecomplete'] ) ) {
  485. $ids = array_map( 'intval', explode( ',', $_GET['acfduplicatecomplete'] ) );
  486. // Generate text.
  487. $text = sprintf(
  488. _n( 'Field group duplicated.', '%s field groups duplicated.', count( $ids ), 'acf' ),
  489. count( $ids )
  490. );
  491. // Append links to text.
  492. $links = array();
  493. foreach ( $ids as $id ) {
  494. $links[] = '<a href="' . get_edit_post_link( $id ) . '">' . get_the_title( $id ) . '</a>';
  495. }
  496. $text .= ' ' . implode( ', ', $links );
  497. // Add notice.
  498. acf_add_admin_notice( $text, 'success' );
  499. return;
  500. }
  501. // Find items to duplicate.
  502. $ids = array();
  503. if ( isset( $_GET['acfduplicate'] ) ) {
  504. $ids[] = intval( $_GET['acfduplicate'] );
  505. } elseif ( isset( $_GET['post'], $_GET['action2'] ) && $_GET['action2'] === 'acfduplicate' ) {
  506. $ids = array_map( 'intval', $_GET['post'] );
  507. }
  508. if ( $ids ) {
  509. check_admin_referer( 'bulk-posts' );
  510. // Duplicate field groups and generate array of new IDs.
  511. $new_ids = array();
  512. foreach ( $ids as $id ) {
  513. $field_group = acf_duplicate_field_group( $id );
  514. $new_ids[] = $field_group['ID'];
  515. }
  516. // Redirect.
  517. wp_redirect( $this->get_admin_url( '&acfduplicatecomplete=' . implode( ',', $new_ids ) ) );
  518. exit;
  519. }
  520. }
  521. /**
  522. * Checks for the custom "acfsync" action.
  523. *
  524. * @date 15/4/20
  525. * @since 5.9.0
  526. *
  527. * @param void
  528. * @return void
  529. */
  530. public function check_sync() {
  531. // Display notice on success redirect.
  532. if ( isset( $_GET['acfsynccomplete'] ) ) {
  533. $ids = array_map( 'intval', explode( ',', $_GET['acfsynccomplete'] ) );
  534. // Generate text.
  535. $text = sprintf(
  536. _n( 'Field group synchronised.', '%s field groups synchronised.', count( $ids ), 'acf' ),
  537. count( $ids )
  538. );
  539. // Append links to text.
  540. $links = array();
  541. foreach ( $ids as $id ) {
  542. $links[] = '<a href="' . get_edit_post_link( $id ) . '">' . get_the_title( $id ) . '</a>';
  543. }
  544. $text .= ' ' . implode( ', ', $links );
  545. // Add notice.
  546. acf_add_admin_notice( $text, 'success' );
  547. return;
  548. }
  549. // Find items to sync.
  550. $keys = array();
  551. if ( isset( $_GET['acfsync'] ) ) {
  552. $keys[] = sanitize_text_field( $_GET['acfsync'] );
  553. } elseif ( isset( $_GET['post'], $_GET['action2'] ) && $_GET['action2'] === 'acfsync' ) {
  554. $keys = array_map( 'sanitize_text_field', $_GET['post'] );
  555. }
  556. if ( $keys && $this->sync ) {
  557. check_admin_referer( 'bulk-posts' );
  558. // Disabled "Local JSON" controller to prevent the .json file from being modified during import.
  559. acf_update_setting( 'json', false );
  560. // Sync field groups and generate array of new IDs.
  561. $files = acf_get_local_json_files();
  562. $new_ids = array();
  563. foreach ( $this->sync as $key => $field_group ) {
  564. if ( $field_group['key'] && in_array( $field_group['key'], $keys ) ) {
  565. // Import.
  566. } elseif ( $field_group['ID'] && in_array( $field_group['ID'], $keys ) ) {
  567. // Import.
  568. } else {
  569. // Ignore.
  570. continue;
  571. }
  572. $local_field_group = json_decode( file_get_contents( $files[ $key ] ), true );
  573. $local_field_group['ID'] = $field_group['ID'];
  574. $result = acf_import_field_group( $local_field_group );
  575. $new_ids[] = $result['ID'];
  576. }
  577. // Redirect.
  578. wp_redirect( $this->get_current_admin_url( '&acfsynccomplete=' . implode( ',', $new_ids ) ) );
  579. exit;
  580. }
  581. }
  582. /**
  583. * Customizes the admin table subnav.
  584. *
  585. * @date 17/4/20
  586. * @since 5.9.0
  587. *
  588. * @param array $views The available views.
  589. * @return array
  590. */
  591. public function admin_table_views( $views ) {
  592. global $wp_list_table, $wp_query;
  593. // Count items.
  594. $count = count( $this->sync );
  595. // Append "sync" link to subnav.
  596. if ( $count ) {
  597. $views['sync'] = sprintf(
  598. '<a %s href="%s">%s <span class="count">(%s)</span></a>',
  599. ( $this->view === 'sync' ? 'class="current"' : '' ),
  600. esc_url( $this->get_admin_url( '&post_status=sync' ) ),
  601. esc_html( __( 'Sync available', 'acf' ) ),
  602. $count
  603. );
  604. }
  605. // Modify table pagination args to match JSON data.
  606. if ( $this->view === 'sync' ) {
  607. $wp_list_table->set_pagination_args(
  608. array(
  609. 'total_items' => $count,
  610. 'total_pages' => 1,
  611. 'per_page' => $count,
  612. )
  613. );
  614. $wp_query->post_count = 1; // At least one post is needed to render bulk drop-down.
  615. }
  616. return $views;
  617. }
  618. /**
  619. * Prints scripts into the admin footer.
  620. *
  621. * @date 20/4/20
  622. * @since 5.9.0
  623. *
  624. * @param void
  625. * @return void
  626. */
  627. function admin_footer() {
  628. ?>
  629. <script type="text/javascript">
  630. (function($){
  631. // Displays a modal comparing local changes.
  632. function reviewSync( props ) {
  633. var modal = acf.newModal({
  634. title: acf.__('Review local JSON changes'),
  635. content: '<p class="acf-modal-feedback"><i class="acf-loading"></i> ' + acf.__('Loading diff') + '</p>',
  636. toolbar: '<a href="' + props.href + '" class="button button-primary button-sync-changes disabled">' + acf.__('Sync changes') + '</a>',
  637. });
  638. // Call AJAX.
  639. var xhr = $.ajax({
  640. url: acf.get('ajaxurl'),
  641. method: 'POST',
  642. dataType: 'json',
  643. data: acf.prepareForAjax({
  644. action: 'acf/ajax/local_json_diff',
  645. id: props.id
  646. })
  647. })
  648. .done(function( data, textStatus, jqXHR ) {
  649. modal.content( data.html );
  650. modal.$('.button-sync-changes').removeClass('disabled');
  651. })
  652. .fail(function( jqXHR, textStatus, errorThrown ) {
  653. if( error = acf.getXhrError(jqXHR) ) {
  654. modal.content( '<p class="acf-modal-feedback error">' + error + '</p>' );
  655. }
  656. });
  657. }
  658. // Add event listener.
  659. $(document).on('click', 'a[data-event="review-sync"]', function( e ){
  660. e.preventDefault();
  661. reviewSync( $(this).data() );
  662. });
  663. })(jQuery);
  664. </script>
  665. <?php
  666. }
  667. /**
  668. * Customizes the admin table HTML when viewing "sync" post_status.
  669. *
  670. * @date 17/4/20
  671. * @since 5.9.0
  672. *
  673. * @param array $views The available views.
  674. * @return array
  675. */
  676. public function admin_footer__sync() {
  677. global $wp_list_table;
  678. // Get table columns.
  679. $columns = $wp_list_table->get_columns();
  680. $hidden = get_hidden_columns( $wp_list_table->screen );
  681. ?>
  682. <div style="display: none;">
  683. <table>
  684. <tbody id="acf-the-list">
  685. <?php
  686. foreach ( $this->sync as $k => $field_group ) {
  687. echo '<tr>';
  688. foreach ( $columns as $column_name => $column_label ) {
  689. $el = 'td';
  690. if ( $column_name === 'cb' ) {
  691. $el = 'th';
  692. $classes = 'check-column';
  693. $column_label = '';
  694. } elseif ( $column_name === 'title' ) {
  695. $classes = "$column_name column-$column_name column-primary";
  696. } else {
  697. $classes = "$column_name column-$column_name";
  698. }
  699. if ( in_array( $column_name, $hidden, true ) ) {
  700. $classes .= ' hidden';
  701. }
  702. echo "<$el class=\"$classes\" data-colname=\"$column_label\">";
  703. switch ( $column_name ) {
  704. // Checkbox.
  705. case 'cb':
  706. echo '<label for="cb-select-' . esc_attr( $k ) . '" class="screen-reader-text">' . esc_html( sprintf( __( 'Select %s', 'acf' ), $field_group['title'] ) ) . '</label>';
  707. echo '<input id="cb-select-' . esc_attr( $k ) . '" type="checkbox" value="' . esc_attr( $k ) . '" name="post[]">';
  708. break;
  709. // Title.
  710. case 'title':
  711. $post_state = '';
  712. if ( ! $field_group['active'] ) {
  713. $post_state = ' — <span class="post-state">' . $this->get_disabled_post_state() . '</span>';
  714. }
  715. echo '<strong><span class="row-title">' . esc_html( $field_group['title'] ) . '</span>' . $post_state . '</strong>';
  716. echo '<div class="row-actions"><span class="file acf-secondary-text">' . $this->get_human_readable_file_location( $field_group['local_file'] ) . '</span></div>';
  717. echo '<button type="button" class="toggle-row"><span class="screen-reader-text">Show more details</span></button>';
  718. break;
  719. // All other columns.
  720. default:
  721. $this->render_admin_table_column( $column_name, $field_group );
  722. break;
  723. }
  724. echo "</$el>";
  725. }
  726. echo '</tr>';
  727. }
  728. ?>
  729. </tbody>
  730. </table>
  731. </div>
  732. <script type="text/javascript">
  733. (function($){
  734. $('#the-list').html( $('#acf-the-list').children() );
  735. })(jQuery);
  736. </script>
  737. <?php
  738. }
  739. /**
  740. * Fires when trashing a field group post.
  741. *
  742. * @date 8/01/2014
  743. * @since 5.0.0
  744. *
  745. * @param int $post_id The post ID.
  746. * @return void
  747. */
  748. public function trashed_post( $post_id ) {
  749. if ( get_post_type( $post_id ) === 'acf-field-group' ) {
  750. acf_trash_field_group( $post_id );
  751. }
  752. }
  753. /**
  754. * Fires when untrashing a field group post.
  755. *
  756. * @date 8/01/2014
  757. * @since 5.0.0
  758. *
  759. * @param int $post_id The post ID.
  760. * @return void
  761. */
  762. public function untrashed_post( $post_id ) {
  763. if ( get_post_type( $post_id ) === 'acf-field-group' ) {
  764. acf_untrash_field_group( $post_id );
  765. }
  766. }
  767. /**
  768. * Fires when deleting a field group post.
  769. *
  770. * @date 8/01/2014
  771. * @since 5.0.0
  772. *
  773. * @param int $post_id The post ID.
  774. * @return void
  775. */
  776. public function deleted_post( $post_id ) {
  777. if ( get_post_type( $post_id ) === 'acf-field-group' ) {
  778. acf_delete_field_group( $post_id );
  779. }
  780. }
  781. }
  782. // Instantiate.
  783. acf_new_instance( 'ACF_Admin_Field_Groups' );
  784. endif; // class_exists check