├── acf_admin_columns.php ├── readme.txt ├── screenshot-1.png └── screenshot-2.png /acf_admin_columns.php: -------------------------------------------------------------------------------- 1 | exclude_field_types = apply_filters('acf/admin_columns/exclude_field_types', $this->exclude_field_types); 79 | $acf_version = acf_get_setting('version'); 80 | $sections = acf_get_field_types(); 81 | if ((version_compare($acf_version, '5.5.0', '<') || version_compare($acf_version, '5.6.0', '>=')) && version_compare($acf_version, '5.7.0', '<')) { 82 | foreach ($sections as $section) { 83 | foreach ($section as $type => $label) { 84 | if (!in_array($type, $this->exclude_field_types)) { 85 | add_action('acf/render_field_settings/type=' . $type, array($this, 'render_field_settings'), 1); 86 | } 87 | } 88 | } 89 | } else { 90 | // >= 5.5.0 || < 5.6.0 91 | foreach ($sections as $type => $settings) { 92 | if (!in_array($type, $this->exclude_field_types)) { 93 | add_action('acf/render_field_settings/type=' . $type, array($this, 'render_field_settings'), 1); 94 | } 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Checks which columns to show on the current screen and attaches to the respective WP hooks 101 | * 102 | * @hook parse_request 103 | * @hook pre_get_terms 104 | * @hook pre_get_users 105 | * @return void 106 | * 107 | */ 108 | public function wp_action_prepare_columns() 109 | { 110 | $screen = $this->get_screen(); 111 | if (!empty($this->admin_columns) || !$screen || !$this->is_acf_active()) { 112 | return; 113 | } 114 | 115 | $field_group_location = ''; 116 | $field_group_location_value = null; 117 | 118 | if ($this->screen_is_post_type_index) { 119 | $field_group_location = 'post_type'; 120 | $field_group_location_value = $screen->post_type; 121 | } elseif ($this->screen_is_taxonomy_index) { 122 | $field_group_location = 'taxonomy'; 123 | $field_group_location_value = $screen->taxonomy; 124 | } elseif ($this->screen_is_user_index) { 125 | $field_group_location = 'user_form'; 126 | } 127 | 128 | $field_groups = []; 129 | foreach (acf_get_field_groups() as $acf_field_group) { 130 | if ($this->acf_field_group_has_location_type($acf_field_group['ID'], $field_group_location, $field_group_location_value)) { 131 | $field_groups[] = $acf_field_group; 132 | 133 | $acf_field_group_fields = acf_get_fields($acf_field_group); 134 | foreach ($acf_field_group_fields as $field) { 135 | if (!isset($field[self::ACF_SETTING_NAME_ENABLED]) || $field[self::ACF_SETTING_NAME_ENABLED] == false) { 136 | continue; 137 | } 138 | 139 | $this->admin_columns[self::COLUMN_NAME_PREFIX . $field['name']] = $field; 140 | } 141 | } 142 | } 143 | 144 | $this->admin_columns = apply_filters('acf/admin_columns/admin_columns', $this->admin_columns, $field_groups); 145 | 146 | if (!empty($this->admin_columns)) { 147 | if ($this->screen_is_post_type_index) { 148 | add_filter('manage_' . $screen->post_type . '_posts_columns', array($this, 'wp_filter_manage_posts_columns')); // creates the columns 149 | add_filter('manage_' . $screen->id . '_sortable_columns', array($this, 'wp_filter_manage_sortable_columns')); // make columns sortable 150 | add_action('manage_' . $screen->post_type . '_posts_custom_column', array($this, 'wp_filter_manage_custom_column'), 10, 2); // outputs the columns values for each post 151 | add_filter('posts_join', array($this, 'wp_filter_search_join')); 152 | add_filter('posts_where', array($this, 'wp_filter_search_where')); 153 | add_filter('posts_distinct', array($this, 'wp_filter_search_distinct')); 154 | } elseif ($this->screen_is_taxonomy_index) { 155 | add_filter('manage_edit-' . $screen->taxonomy . '_columns', array($this, 'wp_filter_manage_posts_columns')); // creates the columns 156 | add_filter('manage_' . $screen->taxonomy . '_custom_column', array($this, 'wp_filter_manage_taxonomy_custom_column'), 10, 3); // outputs the columns values for each post 157 | add_filter('manage_' . $screen->taxonomy . '_custom_column', array($this, 'wp_filter_manage_custom_column'), 10, 3); // outputs the columns values for each post 158 | } elseif ($this->screen_is_user_index) { 159 | add_filter('manage_users_columns', array($this, 'wp_filter_manage_posts_columns')); // creates the columns 160 | add_filter('manage_users_custom_column', array($this, 'wp_filter_manage_custom_column'), 10, 3); // outputs the columns values for each post 161 | } 162 | 163 | add_action('admin_head', array($this, 'wp_action_admin_head')); // add column styling, like column width 164 | } 165 | } 166 | 167 | /** 168 | * prepares WPs query object when sorting by ACF field 169 | * 170 | * @hook pre_get_posts 171 | * 172 | * @param $query WP_Query 173 | * @return mixed 174 | */ 175 | public function wp_action_prepare_query_sort($query) 176 | { 177 | 178 | if ($query->query_vars && isset($query->query_vars['orderby']) && $this->is_acf_active() && $this->get_screen()) { 179 | 180 | $sortby_column = $query->query_vars['orderby']; 181 | 182 | if (is_string($sortby_column) && array_key_exists($sortby_column, $this->admin_columns)) { 183 | 184 | // this makes sure we sort also when the custom field has never been set on some posts before 185 | $meta_query = array( 186 | 'relation' => 'OR', 187 | array('key' => $this->get_column_field_name($sortby_column), 'compare' => 'NOT EXISTS'), // 'NOT EXISTS' needs to go first for proper sorting 188 | array('key' => $this->get_column_field_name($sortby_column), 'compare' => 'EXISTS'), 189 | ); 190 | 191 | $query->set('meta_query', $meta_query); 192 | 193 | $sort_order_type = 'meta_value'; 194 | 195 | // make numerical field ordering useful: 196 | $field_properties = acf_get_field($this->get_column_field_name($sortby_column)); 197 | if (isset($field_properties['type']) && $field_properties['type'] === 'number') { 198 | $sort_order_type = 'meta_value_num'; 199 | } 200 | 201 | $sort_order_type = apply_filters('acf/admin_columns/sort_order_type', $sort_order_type, $field_properties); 202 | 203 | $query->set('orderby', $sort_order_type); 204 | } 205 | } 206 | 207 | return $query; 208 | } 209 | 210 | /** 211 | * Adds the designated columns to Wordpress admin list table. 212 | * 213 | * @hook manage_{$post_type}_posts_columns 214 | * @hook manage_edit-{$taxonomy}_columns 215 | * @hook manage_users_columns 216 | * 217 | * @param $columns array 218 | * @return array 219 | */ 220 | public function wp_filter_manage_posts_columns($columns) 221 | { 222 | 223 | if (empty($this->admin_columns)) { 224 | return $columns; 225 | } 226 | 227 | $acf_columns = $this->admin_columns; 228 | 229 | // first we need to make sure we have all field properties and apply the position filter 230 | foreach ($acf_columns as $column_name => $field_properties) { 231 | if (empty($field_properties) || !is_array($field_properties)) { 232 | $acf_columns[$column_name] = acf_get_field($this->get_column_field_name($column_name)); // refresh field options if they are not set, e.g. after incorrectly applied filter acf/admin_columns/admin_columns 233 | } 234 | 235 | $column_position = empty($acf_columns[$column_name][self::ACF_SETTING_NAME_POSITION]) ? 0 : $acf_columns[$column_name][self::ACF_SETTING_NAME_POSITION]; 236 | $acf_columns[$column_name][self::ACF_SETTING_NAME_POSITION] = apply_filters('acf/admin_columns/column_position', $column_position, $this->get_column_field_name($column_name), $acf_columns[$column_name]); 237 | } 238 | 239 | // next we need to sort our columns by their desired position in order to merge them with the existing columns in the right order 240 | uasort($acf_columns, static function ($a, $b) { 241 | if (!empty($a[self::ACF_SETTING_NAME_POSITION]) && !empty($b[self::ACF_SETTING_NAME_POSITION])) { 242 | return (int)$a[self::ACF_SETTING_NAME_POSITION] - (int)$b[self::ACF_SETTING_NAME_POSITION]; 243 | } 244 | 245 | if (empty($a[self::ACF_SETTING_NAME_POSITION]) && !empty($b[self::ACF_SETTING_NAME_POSITION])) { 246 | return -1; 247 | } 248 | 249 | if (empty($b[self::ACF_SETTING_NAME_POSITION]) && !empty($a[self::ACF_SETTING_NAME_POSITION])) { 250 | return 1; 251 | } 252 | 253 | return 0; 254 | }); 255 | 256 | // we'll merge the ACF columns with the existing columns and bring them all in the right order 257 | $columns_keys = array_keys($columns); 258 | foreach ($acf_columns as $aac_idx => $acf_column) { 259 | if (!empty($acf_column[self::ACF_SETTING_NAME_POSITION])) { 260 | array_splice($columns_keys, ((int)$acf_column[self::ACF_SETTING_NAME_POSITION] - 1), 0, $aac_idx); 261 | } else { 262 | $columns_keys[] = $aac_idx; 263 | } 264 | } 265 | 266 | // finally we prepare all column keys and labels for output 267 | $all_columns = array(); 268 | foreach ($columns_keys as $column_key) { 269 | if (array_key_exists($column_key, $acf_columns)) { 270 | $all_columns[$column_key] = $acf_columns[$column_key]['label']; 271 | } else if (array_key_exists($column_key, $columns)) { 272 | $all_columns[$column_key] = $columns[$column_key]; 273 | } 274 | } 275 | return $all_columns; 276 | } 277 | 278 | /** 279 | * Makes the columns sortable. 280 | * 281 | * @hook manage_{$post_type}_sortable_columns 282 | * 283 | * @param $columns array 284 | * @return array 285 | */ 286 | public function wp_filter_manage_sortable_columns($columns) 287 | { 288 | 289 | foreach ($this->admin_columns as $idx => $acol) { 290 | $columns[$idx] = $idx; 291 | } 292 | 293 | return apply_filters('acf/admin_columns/sortable_columns', $columns); 294 | } 295 | 296 | /** 297 | * WP Hook for displaying the field value inside of a columns cell 298 | * 299 | * @hook manage_{$post_type}_posts_custom_column 300 | * @hook manage_{$taxonomy}_custom_column 301 | * @hook manage_users_custom_column 302 | * 303 | * @param $arg1 string 304 | * @param $arg2 mixed 305 | * @param $arg3 mixed 306 | * @return void 307 | */ 308 | 309 | public function wp_filter_manage_custom_column($arg1, $arg2 = null, $arg3 = null) 310 | { 311 | 312 | $current_filter = current_filter(); 313 | if ($current_filter) { 314 | $render_args = array(); 315 | $echo_value = true; 316 | 317 | if (strpos($current_filter, '_posts_custom_column')) { 318 | $render_args['column'] = $arg1; 319 | $render_args['post_id'] = $arg2; 320 | } elseif (strpos($current_filter, '_custom_column')) { 321 | $render_args['column'] = $arg2; 322 | $render_args['post_id'] = $arg3; 323 | $echo_value = false; 324 | 325 | $screen = $this->get_screen(); 326 | 327 | if ($this->screen_is_taxonomy_index) { 328 | $render_args['post_id'] = $screen->taxonomy . '_' . $render_args['post_id']; 329 | } elseif ($this->screen_is_user_index) { 330 | $render_args['post_id'] = 'user_' . $render_args['post_id']; 331 | } 332 | 333 | } 334 | if (array_key_exists($render_args['column'], $this->admin_columns)) { 335 | $field_name = $this->get_column_field_name($render_args['column']); 336 | $rendered_field_value = $this->render_column_field($render_args); 337 | $rendered_field_value = apply_filters_deprecated('acf/admin_columns/column/' . $field_name, array($rendered_field_value), '0.2.0', 'acf/admin_columns/render_output'); 338 | $rendered_field_value = apply_filters_deprecated('acf/admin_columns/column/' . $field_name . '/value', array($rendered_field_value), '0.2.2', 'acf/admin_columns/render_output'); 339 | if ($echo_value) { 340 | echo $rendered_field_value; 341 | } else { 342 | return $rendered_field_value; 343 | } 344 | } 345 | } 346 | 347 | } 348 | 349 | /** 350 | * WP Hook for displaying the field value inside of a columns cell taxonomy index pages 351 | * 352 | * @hook 353 | * @param $column 354 | * @param $post_id 355 | */ 356 | public function wp_filter_manage_taxonomy_custom_column($content, $column, $post_id) 357 | { 358 | 359 | if (array_key_exists($column, $this->admin_columns)) { 360 | 361 | $field_name = $this->get_column_field_name($column); 362 | 363 | $screen = get_current_screen(); 364 | $taxonomy = $screen->taxonomy; 365 | 366 | $rendered_field_value = $this->render_column_field(array('column' => $column, 'post_id' => $post_id, 'taxonomy' => $taxonomy)); 367 | $rendered_field_value = apply_filters_deprecated('acf/admin_columns/column/' . $field_name, $rendered_field_value, '0.2.2', 'acf/admin_columns/render_output'); 368 | 369 | $content = $rendered_field_value; 370 | } 371 | 372 | return $content; 373 | } 374 | 375 | /** 376 | * WP Hook for joining ACF fields to the search query 377 | * 378 | * @hook posts_join 379 | * 380 | * @param $join string 381 | * @return string 382 | */ 383 | public function wp_filter_search_join($join) 384 | { 385 | 386 | if ($this->get_search_term()) { 387 | global $wpdb; 388 | $join .= ' LEFT JOIN ' . $wpdb->postmeta . ' AS ' . self::COLUMN_NAME_PREFIX . $wpdb->postmeta . ' ON ' . $wpdb->posts . '.ID = ' . self::COLUMN_NAME_PREFIX . $wpdb->postmeta . '.post_id '; 389 | } 390 | 391 | return $join; 392 | } 393 | 394 | /** 395 | * WP Hook for searching in ACF fields 396 | * 397 | * @hook posts_where 398 | * 399 | * @param $where string 400 | * @return string 401 | */ 402 | 403 | public function wp_filter_search_where($where) 404 | { 405 | if ($this->get_search_term()) { 406 | 407 | global $wpdb; 408 | 409 | $where_column_sql = ''; 410 | foreach ($this->admin_columns as $column => $description) { 411 | $column = $this->get_column_field_name($column); 412 | $where_column_sql .= " OR (" . self::COLUMN_NAME_PREFIX . $wpdb->postmeta . ".meta_key ='$column' AND " . self::COLUMN_NAME_PREFIX . $wpdb->postmeta . ".meta_value LIKE $1)"; 413 | } 414 | 415 | $where = preg_replace( 416 | "/\(\s*" . $wpdb->posts . ".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/", 417 | "(" . $wpdb->posts . ".post_title LIKE $1)" . $where_column_sql, 418 | $where); 419 | } 420 | 421 | return $where; 422 | 423 | } 424 | 425 | /** 426 | * @hook posts_distinct 427 | * 428 | * @param $where 429 | * @return mixed|string 430 | */ 431 | public function wp_filter_search_distinct($where) 432 | { 433 | 434 | if ($this->get_search_term()) { 435 | return "DISTINCT"; 436 | } 437 | 438 | return $where; 439 | } 440 | 441 | /** 442 | * Output column styles to the admin head. 443 | * 444 | * @hook admin_head 445 | * 446 | * @return void 447 | */ 448 | public function wp_action_admin_head() 449 | { 450 | 451 | $all_column_styles = array(); 452 | 453 | foreach ($this->admin_columns as $column => $field_properties) { 454 | $column_styles = ''; 455 | 456 | $column_width = isset($field_properties[self::ACF_SETTING_NAME_WIDTH]) ? trim($field_properties[self::ACF_SETTING_NAME_WIDTH]) : ''; 457 | if (!empty($column_width)) { 458 | $column_styles .= 'width:' . $column_width . ';'; 459 | } 460 | 461 | if (!empty($column_styles)) { 462 | $all_column_styles[$column] = apply_filters('acf/admin_columns/column_styles', $column_styles, $this->get_column_field_name($column), $field_properties); 463 | } 464 | } 465 | 466 | if (!empty($all_column_styles)) { 467 | echo ''; 472 | } 473 | 474 | } 475 | 476 | 477 | /** 478 | * Renders the field value for a given column 479 | * 480 | * @param $args array 481 | * @return mixed|string 482 | */ 483 | public function render_column_field($args) 484 | { 485 | 486 | $column = $args['column']; 487 | $post_id = isset($args['post_id']) ? $args['post_id'] : false; 488 | 489 | $field_name = $this->get_column_field_name($column); 490 | $field_properties = acf_get_field($field_name, $post_id); 491 | 492 | // field values 493 | $field_value = get_field($field_name, $post_id); 494 | $original_field_value = $field_value; 495 | 496 | // if the field is really empty $field_value should be null 497 | if ($original_field_value === false && $field_properties['type'] !== 'true_false') { 498 | $field_value = null; 499 | } 500 | 501 | $render_output = ''; 502 | 503 | 504 | $field_images = $field_value; 505 | $preview_item_count = 1; 506 | $remaining_items_count = 0; 507 | $render_raw = false; 508 | $render_raw = apply_filters_deprecated('acf/admin_columns/column/' . $field_name . '/render_raw', array($render_raw, $field_properties, $original_field_value, $post_id), '0.2.2', 'acf/admin_columns/render_raw'); 509 | $render_raw = apply_filters('acf/admin_columns/render_raw', $render_raw, $field_properties, $original_field_value, $post_id); 510 | 511 | if (empty($field_value) && !empty($field_properties['default_value'])) { 512 | $field_value = apply_filters('acf/admin_columns/default_value', $field_properties['default_value'], $field_properties, $field_value, $post_id); 513 | } 514 | 515 | $field_value = apply_filters_deprecated('acf/admin_columns/column/' . $field_name . '/before_render_value', array($field_value, $field_properties, $post_id), '0.2.2', 'acf/admin_columns/before_render_output'); 516 | $field_value = apply_filters('acf/admin_columns/before_render_output', $field_value, $field_properties, $post_id); 517 | 518 | if (!$render_raw) { 519 | 520 | switch ($field_properties['type']) { 521 | case 'color_picker': 522 | if ($field_value) { 523 | $render_output .= '
' . $field_value . '

'; 524 | } 525 | break; 526 | case 'taxonomy': 527 | if (is_array($field_value)) { 528 | foreach ($field_value as $field_taxonomy) { 529 | $render_output .= $field_taxonomy->name . ' (ID ' . $field_taxonomy->term_id . ')
'; 530 | } 531 | } 532 | break; 533 | case 'file': 534 | $render_output = isset($field_value['filename']) ? $field_value['filename'] : ''; // @todo multiple values 535 | break; 536 | case 'wysiwyg': 537 | $render_output = wp_trim_excerpt(strip_tags(strval($field_value))); 538 | break; 539 | case 'link': 540 | if (is_array($field_value) && isset($field_value['url'])) { 541 | $render_output = $field_value['url']; 542 | } 543 | break; 544 | case 'post_object': 545 | case 'relationship': 546 | $related_post = $field_value; 547 | if (is_array($field_value) && !empty($field_value)) { 548 | $related_post = $field_value[0]; // use the first post as preview 549 | $remaining_items_count = count($field_value) - 1; 550 | } 551 | if ($related_post) { 552 | $render_output = '' . get_the_title($related_post) . ''; 553 | } 554 | break; 555 | case 'user': 556 | if (!empty($field_value)) { 557 | if (!empty($field_properties['multiple'])) { 558 | $remaining_items_count = count($field_value) - 1; 559 | $field_value = $field_value[0]; 560 | } 561 | 562 | if (is_array($field_value) && !empty($field_value['ID'])) { 563 | $user = get_user_by('id', $field_value['ID']); 564 | } elseif ($field_value instanceof \WP_User) { 565 | $user = $field_value; 566 | } elseif ((int)$field_value > 0) { 567 | $user = get_user_by('id', (int)$field_value); 568 | } 569 | 570 | if (!empty($user)) { 571 | $render_output = '' . $user->user_login . (!empty($user->display_name) && $user->user_login !== $user->display_name ? ' (' . $user->display_name . ')' : '') . ''; 572 | } 573 | } 574 | break; 575 | case 'image': 576 | $field_images = array($field_value); 577 | case 'gallery': 578 | 579 | if (!empty($field_images)) { 580 | $preview_image = $field_images[0]; // use first image as preview 581 | $preview_image_id = 0; 582 | $preview_image_url = ''; 583 | 584 | if (is_array($field_value) && isset($preview_image['ID'])) { 585 | $preview_image_id = $preview_image['ID']; 586 | } else if ((int)$preview_image > 0) { 587 | $preview_image_id = (int)$preview_image; 588 | } 589 | 590 | if (filter_var($preview_image, FILTER_VALIDATE_URL)) { // is the field return value a url string? 591 | $preview_image_url = $preview_image; 592 | } else if ($preview_image_id > 0) { // return value is either image array or id 593 | $preview_image_size = apply_filters('acf/admin_columns/preview_image_size', 'thumbnail', $field_properties, $field_value, $post_id); 594 | $img = wp_get_attachment_image_src($preview_image_id, $preview_image_size); 595 | if (is_array($img) && isset($img[0])) { 596 | $preview_image_url = $img[0]; 597 | } 598 | } 599 | 600 | $preview_image_url = apply_filters('acf/admin_columns/preview_image_url', $preview_image_url, $field_properties, $field_value, $post_id); 601 | 602 | if ($preview_image_url) { 603 | $render_output = ""; 604 | 605 | $remaining_items_count = count($field_images) - $preview_item_count; 606 | } 607 | } 608 | break; 609 | case 'radio': 610 | case 'checkbox': 611 | case 'select': 612 | if (!empty($field_value) && isset($field_properties['return_format'])) { 613 | if ($field_properties['type'] === 'checkbox' || (!empty($field_properties['multiple']))) { 614 | $render_output = array(); 615 | foreach ($field_value as $field_value_item) { 616 | $render_output[] = $this->render_value_label_field($field_properties['return_format'], $field_properties['choices'], $field_value_item); 617 | } 618 | } else { 619 | $render_output = $this->render_value_label_field($field_properties['return_format'], $field_properties['choices'], $field_value); 620 | } 621 | break; 622 | } 623 | case 'number': 624 | case 'true_false': 625 | case 'text': 626 | case 'textarea': 627 | case 'range': 628 | case 'email': 629 | case 'url': 630 | case 'password': 631 | case 'button_group': 632 | case 'page_link': 633 | case 'date_picker': 634 | case 'time_picker': 635 | default: 636 | $render_output = $field_value; 637 | } 638 | 639 | // wrap link around URL field value 640 | $link_wrap_url = apply_filters('acf/admin_columns/link_wrap_url', true, $field_properties, $original_field_value, $post_id); 641 | if ($link_wrap_url && filter_var($render_output, FILTER_VALIDATE_URL)) { 642 | $render_output = '' . $render_output . ''; 643 | } 644 | 645 | // convert array entries to column string 646 | if (is_array($render_output)) { 647 | $array_render_separator = apply_filters('acf/admin_columns/array_render_separator', ', ', $field_properties, $original_field_value, $post_id); 648 | $render_output = implode($array_render_separator, $render_output); 649 | } 650 | 651 | // default "no value" or "empty" output 652 | if (empty($render_output) && $field_value === null) { 653 | $render_output = apply_filters('acf/admin_columns/no_value_placeholder', '—', $field_properties, $original_field_value, $post_id); 654 | } 655 | } 656 | 657 | // search term highlighting 658 | if ($search_term = $this->get_search_term()) { 659 | 660 | $search_preg_replace_pattern = '\\0'; 661 | $search_preg_replace_pattern = apply_filters_deprecated('acf/admin_columns/search/highlight_preg_replace_pattern', array($search_preg_replace_pattern, $search_term, $field_properties, $original_field_value, $post_id), '0.2.2', 'acf/admin_columns/highlight_search_term_preg_replace_pattern'); 662 | $search_preg_replace_pattern = apply_filters('acf/admin_columns/highlight_search_term_preg_replace_pattern', $search_preg_replace_pattern, $search_term, $field_properties, $original_field_value, $post_id); 663 | 664 | $render_output = preg_replace('#' . preg_quote($search_term) . '#i', $search_preg_replace_pattern, $render_output); 665 | } 666 | 667 | if (!empty($remaining_items_count)) { 668 | $render_output .= "
and $remaining_items_count more"; 669 | } 670 | 671 | return apply_filters('acf/admin_columns/render_output', $render_output, $field_properties, $original_field_value, $post_id); 672 | } 673 | 674 | /** 675 | * Renders the field within ACF's field setting UI 676 | * 677 | * @param $field 678 | * @return void 679 | * 680 | */ 681 | public function render_field_settings($field) 682 | { 683 | 684 | /* 685 | * General settings switch 686 | */ 687 | $field_settings = array( 688 | array( 689 | 'type' => 'true_false', 690 | 'ui' => 1, 691 | 'label' => 'Admin Column', 692 | 'name' => self::ACF_SETTING_NAME_ENABLED, 693 | 'instructions' => 'Enable admin column for this field in post archive pages.', 694 | ), 695 | array( 696 | 'type' => 'number', 697 | 'min' => 1, 698 | 'ui' => 1, 699 | 'label' => 'Admin Column Position', 700 | 'name' => self::ACF_SETTING_NAME_POSITION, 701 | 'instructions' => 'Position of the admin column. Leave empty to append to the end.', 702 | 'conditional_logic' => array( 703 | array( 704 | array( 705 | 'field' => self::ACF_SETTING_NAME_ENABLED, 706 | 'operator' => '==', 707 | 'value' => '1', 708 | ), 709 | ), 710 | ), 711 | ), 712 | array( 713 | 'type' => 'text', 714 | 'ui' => 1, 715 | 'label' => 'Admin Column Width', 716 | 'name' => self::ACF_SETTING_NAME_WIDTH, 717 | 'instructions' => 'Width of the admin column. Specify as CSS value, e.g. "100px" or "20%". Leave empty to use the default auto width.', 718 | 'conditional_logic' => array( 719 | array( 720 | array( 721 | 'field' => self::ACF_SETTING_NAME_ENABLED, 722 | 'operator' => '==', 723 | 'value' => '1', 724 | ), 725 | ), 726 | ), 727 | ) 728 | ); 729 | 730 | foreach ($field_settings as $settings_args) { 731 | $settings_args['class'] = isset($settings_args['class']) ? $settings_args['class'] . ' aac-field-settings-' . $settings_args['name'] : ''; 732 | acf_render_field_setting($field, $settings_args, false); 733 | } 734 | 735 | } 736 | 737 | /** 738 | * checks whether ACF plugin is active 739 | * @return bool 740 | */ 741 | protected function is_acf_active() 742 | { 743 | return (function_exists('acf_get_field_groups') && function_exists('acf_get_fields')); 744 | } 745 | 746 | /** 747 | * Returns the current screen object 748 | * @return bool|WP_Screen 749 | */ 750 | protected function get_screen() 751 | { 752 | 753 | if (function_exists('get_current_screen') && $screen = get_current_screen()) { 754 | $this->screen_is_post_type_index = $screen->base === 'edit' && $screen->post_type; 755 | $this->screen_is_taxonomy_index = $screen->base === 'edit-tags' && $screen->taxonomy; 756 | $this->screen_is_user_index = $screen->base === 'users'; 757 | 758 | if ($this->screen_is_post_type_index || $this->screen_is_taxonomy_index || $this->screen_is_user_index) { 759 | return $screen; 760 | } 761 | } 762 | 763 | return false; 764 | } 765 | 766 | /** 767 | * Returns the search term if the current screen is a post type index and a search is active 768 | * @return bool|string 769 | */ 770 | protected function get_search_term() 771 | { 772 | 773 | $search_term = false; 774 | if (!empty($_GET['s'])) { 775 | $search_term = $_GET['s']; 776 | } 777 | 778 | return $search_term; 779 | } 780 | 781 | /** 782 | * Return the "real" ACF field name, without the prefix 783 | * @param $colum_name 784 | * @return mixed 785 | */ 786 | protected function get_column_field_name($colum_name) 787 | { 788 | return str_replace(self::COLUMN_NAME_PREFIX, '', $colum_name); 789 | } 790 | 791 | /** 792 | * Renders the value of a select, radio or checkbox field based on the return format 793 | * 794 | * @param $return_format 795 | * @param $choices 796 | * @param $field_value 797 | * @return mixed|string 798 | */ 799 | protected function render_value_label_field($return_format, $choices, $field_value) 800 | { 801 | if (empty($field_value)) { 802 | return $field_value; 803 | } 804 | $render_output = $field_value; 805 | $value = ''; 806 | $label = ''; 807 | 808 | if ($return_format === 'value' && !empty($choices[$field_value])) { 809 | $value = $field_value; 810 | $label = $choices[$field_value]; 811 | } else if ($return_format === 'label' && in_array($field_value, $choices)) { 812 | $value = array_search($field_value, $choices); 813 | $label = $field_value; 814 | } else if ($return_format === 'array' && is_array($field_value) && array_key_exists('value', $field_value) && array_key_exists('label', $field_value)) { 815 | $value = $field_value['value']; 816 | $label = $field_value['label']; 817 | } 818 | 819 | if (!empty($value) && !empty($label)) { 820 | $render_output = $value; 821 | if ($value !== $label) { 822 | $render_output .= ' (' . $label . ')'; 823 | } 824 | } 825 | 826 | return $render_output; 827 | } 828 | 829 | protected function acf_field_group_has_location_type($post_id, $location, $location_value = null) 830 | { 831 | if (empty($post_id) || empty($location)) { 832 | return false; 833 | } 834 | 835 | $field_group = acf_get_field_group($post_id); 836 | 837 | if (empty($field_group['location'])) { 838 | return false; 839 | } 840 | 841 | foreach ($field_group['location'] as $rule_group) { 842 | $params = array_column($rule_group, 'param'); 843 | 844 | if (in_array($location, $params, true)) { 845 | if ($location_value !== null && !in_array($location_value, array_column($rule_group, 'value'), true)) { 846 | continue; 847 | } 848 | 849 | return true; 850 | } 851 | } 852 | 853 | return false; 854 | } 855 | 856 | } 857 | 858 | if (is_admin()) { 859 | $flei_acf_admin_columns = FleiACFAdminColumns::get_instance(); 860 | } 861 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Admin Columns for ACF Fields === 2 | 3 | Contributors: flei 4 | Donate link: https://www.buymeacoffee.com/flei 5 | Tags: advanced custom fields, acf, admin columns 6 | Requires at least: 4.6 7 | Tested up to: 6.7.1 8 | Stable tag: 0.3.1 9 | Requires PHP: 5.6.2 10 | License: GPLv2 or later 11 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 12 | Date: 17.01.2025 13 | Version: 0.3.2 14 | 15 | 16 | Allows you to enable columns for your ACF fields in post and taxonomy overviews (e.g. "All Posts") in the Wordpress admin backend. This plugin requires the plugin "Advanced Custom Fields" (ACF) to work. 17 | 18 | == Description == 19 | 20 | This plugin requires the plugin "Advanced Custom Fields" (ACF) to work. 21 | 22 | Use this plugin to show ACF fields in the "All Posts", Taxonomy or User table view in the Wordpress admin backend. 23 | 24 | Simply enable the new option "Admin Column" in your ACF field settings for any regular field (see exceptions below), and optionally set the columns position and width. Now there will be an extra column for your field shown in any overview of built-in or custom posts, pages, taxonomies (e.g. "All Pages"), and users. 25 | 26 | You can use filters (see below) to control the plugins behaviour even more precisely. 27 | 28 | Works on any regular ACF field (see exceptions below). 29 | 30 | Compatible with Advanced Custom Fields 5.x and 6.x. 31 | 32 | Github: https://github.com/fleiflei/acf-admin-columns 33 | 34 | If you like this plugin please kindly leave your review and feedback here: https://wordpress.org/plugins/admin-columns-for-acf-fields/#reviews 35 | 36 | == Screenshots == 37 | 38 | 1. Example of various admin columns for Posts 39 | 40 | 2. New settings within the ACF field settings UI 41 | 42 | == Usage: == 43 | 44 | 1. Install ACF and this plugin (see below) 45 | 2. In ACF open/create a "field group" and open any field for editing (see exceptions below). 46 | 3. Enable the "Admin Column" option in the field settings. 47 | 4. Specify the desired column position (optional). 48 | 5. Specify the desired column width (optional). 49 | 6. Save the field group and go to the "All posts" view of the post type or taxonomy (e.g. "Posts > All Posts", or "Pages > All Pages") and notice the newly added column for your field. 50 | 51 | == Excluded ACF Fields == 52 | 53 | Due to their nature the option "Admin Column" is not shown in ACF for these fields: 54 | 55 | - Accordion 56 | - Clone 57 | - Flexible Content 58 | - Google Map 59 | - Group 60 | - Message 61 | - Repeater 62 | - Tab 63 | 64 | == Filters == 65 | 66 | = "acf/admin_columns/admin_columns" = 67 | 68 | Allows you to change which columns are displayed on the current admin screen. 69 | 70 | **Parameters** 71 | 72 | $acf_columns - Array of all ACF fields to be shown in current screen. Note that the column key is always prefixed with 'acf_'. 73 | $field_groups - Array of all ACF field groups to be shown in current screen. 74 | 75 | **Example:** 76 | 77 | Remove 'my_field' from the columns of the post type 'my_custom_post_type', even if it is set to be shown in the field settings. Note that the column key is always prefixed with 'acf_'. 78 | 79 | function my_admin_columns($acf_columns, $field_groups) { 80 | 81 | $screen = get_current_screen(); 82 | if (!empty($screen) && $screen->post_type == 'my_custom_post_type' && isset($acf_columns['acf_my_field'])) { 83 | unset($acf_columns['acf_my_field']); // the key is always prefixed with 'acf_' 84 | } 85 | return $acf_columns; 86 | } 87 | add_filter('acf/admin_columns/admin_columns','my_admin_columns', 10, 2); 88 | 89 | = "acf/admin_columns/sortable_columns" = 90 | 91 | Change which columns should be sortable. By default, every column is sortable. 92 | 93 | **Parameters** 94 | 95 | $columns - Array of all ACF fields to be shown in current screen. 96 | 97 | = "acf/admin_columns/sort_order_type" = 98 | 99 | Change the sort order type for a certain field. By default, most fields are sorted by string comparison. Number fields are ordered by numeric comparison. 100 | 101 | **Parameters** 102 | 103 | $sort_order_type - The sort order type (either 'meta_value' or 'meta_value_num') 104 | $field_properties - the ACF field properties 105 | 106 | **Example:** 107 | 108 | Change the sort order type for the field 'my_field' to 'meta_value_num' (see https://developer.wordpress.org/reference/classes/wp_query/#order-orderby-parameters). 109 | 110 | function my_sort_order_type($sort_order_type, $field_properties) { 111 | if ($field_properties['name'] == 'my_field') { 112 | return 'meta_value_num'; 113 | } 114 | return $sort_order_type; 115 | } 116 | add_filter('acf/admin_columns/sort_order_type','my_sort_order_type', 10, 2); 117 | 118 | = "acf/admin_columns/render_output" = 119 | 120 | Allows you to modify the output of a certain $field in every row of a posts table. 121 | 122 | **Parameters** 123 | 124 | $render_output - The field value after it was prepared for output 125 | $field_properties - the ACF field properties 126 | $field_value - the original raw field value 127 | $post_id - the post id 128 | 129 | **Example:** 130 | 131 | Output then length of text field 'my_text_field' instead of its contents. 132 | 133 | function my_column_value($rendered_output, $field_properties, $field_value, $post_id) { 134 | if ($field_properties['name'] == 'my_text_field') { 135 | return strlen($field_value); 136 | } 137 | return $rendered_output; 138 | } 139 | add_filter('acf/admin_columns/render_output','my_column_value', 10, 4); 140 | 141 | = "acf/admin_columns/render_raw" = 142 | 143 | Output a field value without any formatting. This is useful e.g. for image fields, where you might want to output the raw image url instead of a rendered image tag. 144 | 145 | **Parameters** 146 | 147 | $render_raw - boolean, set to true to render raw field value 148 | $field_properties - the ACF field properties 149 | $field_value - the original raw field value 150 | $post_id - the post id 151 | 152 | **Example:** 153 | 154 | Output the raw image url for image field 'my_image_field' for post ID 123. 155 | 156 | function my_render_raw($render_raw, $field_properties, $field_value, $post_id) { 157 | if ($field_properties['name'] == 'my_image_field' && $post_id == 123) { 158 | return true; 159 | } 160 | return $render_raw; 161 | } 162 | add_filter('acf/admin_columns/render_raw','my_render_raw', 10, 4); 163 | 164 | = "acf/admin_columns/default_value" = 165 | 166 | Allows you to override the default value for a certain field if it is empty. This only applies, if the field has a default value set in the field settings. 167 | 168 | **Parameters** 169 | 170 | $default_value - The default value 171 | $field_properties - the ACF field properties 172 | $field_value - the original raw field value 173 | $post_id - the post id 174 | 175 | **Example:** 176 | 177 | Change the default value for field 'my_field' to 'my default value' if it is empty. 178 | 179 | function my_default_value($default_value, $field_properties, $field_value, $post_id) { 180 | if ($field_properties['name'] == 'my_field' && empty($field_value)) { 181 | return 'my default value'; 182 | } 183 | return $default_value; 184 | } 185 | add_filter('acf/admin_columns/default_value','my_default_value', 10, 4); 186 | 187 | = "acf/admin_columns/before_render_output" = 188 | 189 | Allows you to modify the field value of a certain $field before it is prepared for rendering. This filter is applied before 'acf/admin_columns/render_output'. 190 | 191 | **Parameters** 192 | 193 | $field_value - the original raw field value 194 | $field_properties - the ACF field properties 195 | $post_id - the post id 196 | 197 | 198 | = "acf/admin_columns/preview_image_size" = 199 | 200 | Change the preview image size for image or gallery fields. Default value is "thumbnail". 201 | 202 | **Parameters** 203 | 204 | $preview_image_size - string with image size name 205 | $field_properties - the ACF field properties 206 | $post_id - the post id 207 | 208 | **Example** 209 | 210 | Change preview image size to "medium" 211 | 212 | function my_preview_image_size($preview_image_size, $field_properties, $post_id) { 213 | return 'medium'; 214 | } 215 | add_filter('acf/admin_columns/preview_image_size','my_preview_image_size', 10, 3); 216 | 217 | = "acf/admin_columns/preview_image_url" = 218 | 219 | Allows for manipulation of the url of the preview image for image or gallery fields. 220 | 221 | **Parameters** 222 | 223 | $preview_image_url - string with image url 224 | $field_properties - the ACF field properties 225 | $post_id - the post id 226 | 227 | **Example** 228 | 229 | Replace preview image of field 'my_image_field' for post ID 123 to a random 100x100px image from https://picsum.photos. 230 | 231 | function my_preview_image_url($preview_image_url, $field_properties, $post_id) { 232 | if ($field_properties['name'] == 'my_image_field' && $post_id == 123) { 233 | return 'https://picsum.photos/100/100'; 234 | } 235 | return $preview_image_url; 236 | } 237 | add_filter('acf/admin_columns/preview_image_url','my_preview_image_url', 10, 3); 238 | 239 | 240 | = "acf/admin_columns/link_wrap_url" = 241 | 242 | Automatically wrap url in link to that url. This is useful e.g. for text fields that contain a url, where you might want to output a link to the url instead of the url itself. 243 | 244 | **Parameters** 245 | 246 | $link_wrap_url - boolean, set to true to wrap url in link 247 | $field_properties - the ACF field properties 248 | $field_value - the original raw field value 249 | $post_id - the post id 250 | 251 | **Example:** 252 | 253 | Wrap url in link for text field 'my_link_text_field'. 254 | 255 | function my_link_wrap_url($link_wrap_url, $field_properties, $field_value, $post_id) { 256 | if ($field_properties['name'] == 'my_link_text_field') { 257 | return true; 258 | } 259 | return $link_wrap_url; 260 | } 261 | add_filter('acf/admin_columns/link_wrap_url','my_link_wrap_url', 10, 4); 262 | 263 | = "acf/admin_columns/array_render_separator" = 264 | 265 | Allows you to change the separator for array fields (e.g. repeater, flexible content, gallery). Default value is ", ". 266 | 267 | **Parameters** 268 | 269 | $array_render_separator - string with separator, default = ", " 270 | $field_properties - the ACF field properties 271 | $field_value - the original raw field value 272 | $post_id - the post id 273 | 274 | **Example:** 275 | 276 | Output every array item on a new line, using the `
` tag. 277 | 278 | function my_array_render_separator($array_render_separator, $field_properties, $field_value, $post_id) { 279 | return "
"; 280 | } 281 | add_filter('acf/admin_columns/array_render_separator','my_array_render_separator', 10, 4); 282 | 283 | 284 | = "acf/admin_columns/no_value_placeholder" = 285 | 286 | Change the placeholder for empty values. Default value is "-". 287 | 288 | **Parameters** 289 | 290 | $no_value_placeholder - string with placeholder, default = "-" 291 | $field_properties - the ACF field properties 292 | $field_value - the original raw field value 293 | $post_id - the post id 294 | 295 | **Example:** 296 | 297 | Output "n/a" for empty values. 298 | 299 | function my_no_value_placeholder($no_value_placeholder, $field_properties, $field_value, $post_id) { 300 | return "n/a"; 301 | } 302 | add_filter('acf/admin_columns/no_value_placeholder','my_no_value_placeholder', 10, 4); 303 | 304 | = "acf/admin_columns/highlight_search_term_preg_replace_pattern" = 305 | 306 | Change the preg_replace pattern for highlighting the search term in the column output. 307 | 308 | **Parameters** 309 | 310 | $highlight_search_term_preg_replace_pattern - string with preg_replace pattern, default is '\\0' (yellow background, black font color) 311 | $field_properties - the ACF field properties 312 | $field_value - the original raw field value 313 | $post_id - the post id 314 | 315 | **Example:** 316 | 317 | Highlight search terms with red background and white font color. 318 | 319 | function my_highlight_search_term_preg_replace_pattern($highlight_search_term_preg_replace_pattern, $field_properties, $field_value, $post_id) { 320 | return '\\0'; 321 | } 322 | add_filter('acf/admin_columns/highlight_search_term_preg_replace_pattern','my_highlight_search_term_preg_replace_pattern', 10, 4); 323 | 324 | 325 | = "acf/admin_columns/exclude_field_types" = 326 | 327 | Change which field types should not have the admin column option in the field settings. 328 | 329 | **Parameters** 330 | 331 | $excluded_field_types - array of excluded_field_types 332 | 333 | **Example: disallow the admin column option for TEXT fields** 334 | 335 | function my_exclude_field_types($excluded_field_types) { 336 | $excluded_field_types[] = 'text'; 337 | return $excluded_field_types; 338 | } 339 | add_filter('acf/admin_columns/exclude_field_types','my_exclude_field_types'); 340 | 341 | 342 | = "acf/admin_columns/column_position" = 343 | 344 | Change the column position for a certain field. 345 | 346 | **Parameters** 347 | 348 | $column_position - integer with column position 349 | $field_name - the ACF field name 350 | $field_properties - the ACF field properties 351 | 352 | **Example:** 353 | 354 | Change the column position for field 'my_field' to 2. 355 | 356 | function my_column_position($column_position, $field_name, $field_properties) { 357 | if ($field_name == 'my_field') { 358 | return 2; 359 | } 360 | return $column_position; 361 | } 362 | add_filter('acf/admin_columns/column_position','my_column_position', 10, 3); 363 | 364 | = "acf/admin_columns/column_styles" = 365 | 366 | Change the column styles for a column. 367 | 368 | **Parameters** 369 | 370 | $column_styles - string with column styles 371 | $field_name - the ACF field name 372 | $field_properties - the ACF field properties 373 | 374 | **Example:** 375 | 376 | Change the column width for field 'my_field' to 20% of the screen width and set the max-width of the column to 200px. 377 | 378 | function my_column_styles($column_styles, $field_name, $field_properties) { 379 | if ($field_name == 'my_field') { 380 | return 'width: 20%; max-width: 200px;'; 381 | } 382 | return $column_styles; 383 | } 384 | add_filter('acf/admin_columns/column_styles','my_column_styles', 10, 3); 385 | 386 | 387 | == Installation == 388 | 389 | This section describes how to install the plugin and get it working. 390 | 391 | 1. Upload the plugin files to the `/wp-content/plugins/admin-columns-for-acf-fields` directory, or install the plugin through the WordPress plugins screen directly. 392 | 2. Activate the plugin through the 'Plugins' screen in WordPress 393 | 3. Open ACF and enable "Admin Column" in any fields settings section. 394 | 395 | == Frequently Asked Questions == 396 | 397 | = How can I change the preview image size of image and gallery fields? = 398 | 399 | Use the filter "acf/admin_columns/preview_image_size" to change the preview image size. See "Filters" section above for details. 400 | 401 | == Changelog == 402 | 403 | = 0.3.2 = 404 | 405 | *Release date: 17.01.2025* 406 | 407 | * Fix: field columns not rendered for users 408 | * Fix: upwards compatibility with PHP 7.4+ in strip_tags() 409 | * Fix: documentation fix for filter 'acf/admin_columns/column/render_output' (old) -> 'acf/admin_columns/render_output' (new) 410 | 411 | = 0.3.1 = 412 | 413 | *Release date: 17.11.2023* 414 | 415 | * Fix: error in column positioning 416 | * Fix: improved handling of user fields 417 | 418 | = 0.3.0 = 419 | 420 | *Release date: 15.11.2023* 421 | 422 | * Improvement: Added column position field setting. This allows you to control the position of the column in the overview. Added new filter "acf/admin_columns/column_position" to change a columns position programmatically. 423 | * Improvement: Added column width field setting. This allows you to control the width of the column in the overview. Added new filter "acf/admin_columns/column_styles" to change a columns width or other styles programmatically. 424 | 425 | = 0.2.2 = 426 | 427 | *Release date: 10.11.2023* 428 | 429 | * improved filters and updated filter documentation 430 | 431 | = 0.2.1 = 432 | *Release date: 10.11.2023* 433 | 434 | * added compatibility with PHP 8.2 435 | * select, radio & checkbox fields: improved handling of return formats 436 | 437 | = 0.2.0 = 438 | *Release date: 06.10.2023* 439 | 440 | * New feature: searchable columns for post archives. Searching columns in taxonomies and users will follow. 441 | * Removed the field settings for the location where the column should be rendered (post type, taxonomy, user). A field column will be rendered according to the "location rules" in the ACF field group settings. 442 | * Default values are now shown in the column if the field is empty. 443 | * Fix: numeric sorting for 'number' field instead of string sorting 444 | * New filter: 'acf/admin_columns/preview_image_size' - set preview size for image or gallery fields 445 | * New filter: 'acf/admin_columns/preview_image_url' - allows for manipulation of the preview_image_url in image or gallery fields 446 | * New filter: 'acf/admin_columns/link_wrap_url' - automatically wrap url in link to that url 447 | * New filter: 'acf/admin_columns/render_raw' - set to true to render raw field value (e.g. for image fields) 448 | 449 | = 0.1.2 = 450 | *Release date: 17.12.2019* 451 | 452 | * Fix: error in plugin when using Customizer preview in WP backend (see https://github.com/fleiflei/acf-admin-columns/issues/3) 453 | * Fix: field values are now always shown unless the field is really empty (see 454 | 455 | = 0.1.1 = 456 | *Release date: 27.11.2019* 457 | 458 | * Fix: disable plugin for AJAX requests (see https://github.com/fleiflei/acf-admin-columns/issues/2) 459 | * Documentation: screenshot added, formatting and content updates -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fleiflei/acf-admin-columns/b8885ede0cb08b3cdfd802a143a600d8b672c08c/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fleiflei/acf-admin-columns/b8885ede0cb08b3cdfd802a143a600d8b672c08c/screenshot-2.png --------------------------------------------------------------------------------