PluginProbe
Custom Post Type UI / 1.19.3
Custom Post Type UI v1.19.3
1.19.3 1.19.2 1.19.1 1.19.0 trunk 0.7.0.0 0.7.1.0 0.7.2.0 0.8.0.0 0.8.1 0.8.2 0.8.3 0.8.4 0.8.5 0.9.0 0.9.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 All 96 releases
custom-post-type-ui / custom-post-type-ui.php

custom-post-type-ui.php in Custom Post Type UI 1.19.3, at custom-post-type-ui.php

1,187 lines 41.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Custom Post Type UI.
4 *
5 * For all your post type and taxonomy needs.
6 *
7 * @package CPTUI
8 * @subpackage Loader
9 * @author WebDevStudios
10 * @since 0.1.0.0
11 * @license GPL-2.0+
12 */
13
14 /**
15 * Plugin Name: Custom Post Type UI
16 * Plugin URI: https://github.com/WebDevStudios/custom-post-type-ui/
17 * Description: Admin UI panel for registering custom post types and taxonomies
18 * Author: WebDevStudios
19 * Version: 1.19.3
20 * Author URI: https://webdevstudios.com/
21 * Text Domain: custom-post-type-ui
22 * License: GPL-2.0+
23 * Requires at least: 6.6
24 * Requires PHP: 7.4
25 */
26
27 // phpcs:disable WebDevStudios.All.RequireAuthor
28 // phpcs:set WordPress.WP.I18n check_translator_comments false
29
30 // Exit if accessed directly.
31 if ( ! defined( 'ABSPATH' ) ) {
32 exit;
33 }
34
35 define( 'CPT_VERSION', '1.19.3' ); // Left for legacy purposes.
36 define( 'CPTUI_VERSION', '1.19.3' );
37 define( 'CPTUI_WP_VERSION', get_bloginfo( 'version' ) );
38
39 /**
40 * Load our Admin UI class that powers our form inputs.
41 *
42 * @since 1.0.0
43 *
44 * @internal
45 */
46 function cptui_load_ui_class() {
47 require_once plugin_dir_path( __FILE__ ) . 'classes/class.cptui_admin_ui.php';
48 require_once plugin_dir_path( __FILE__ ) . 'classes/class.cptui_debug_info.php';
49 }
50 add_action( 'init', 'cptui_load_ui_class' );
51
52 /**
53 * Set a transient used for redirection upon activation.
54 *
55 * @since 1.4.0
56 */
57 function cptui_activation_redirect() {
58 // Bail if activating from network, or bulk.
59 if ( is_network_admin() ) {
60 return;
61 }
62
63 // Add the transient to redirect.
64 set_transient( 'cptui_activation_redirect', true, 30 );
65 }
66 add_action( 'activate_' . plugin_basename( __FILE__ ), 'cptui_activation_redirect' );
67
68 /**
69 * Redirect user to CPTUI about page upon plugin activation.
70 *
71 * @since 1.4.0
72 */
73 function cptui_make_activation_redirect() {
74
75 if ( ! get_transient( 'cptui_activation_redirect' ) ) {
76 return;
77 }
78
79 delete_transient( 'cptui_activation_redirect' );
80
81 // Bail if activating from network, or bulk.
82 if ( is_network_admin() ) {
83 return;
84 }
85
86 if ( ! cptui_is_new_install() ) {
87 return;
88 }
89
90 // Redirect to CPTUI about page.
91 wp_safe_redirect(
92 add_query_arg(
93 [ 'page' => 'cptui_main_menu' ],
94 cptui_admin_url( 'admin.php?page=cptui_main_menu' )
95 )
96 );
97 }
98 add_action( 'admin_init', 'cptui_make_activation_redirect', 1 );
99
100 /**
101 * Flush our rewrite rules on deactivation.
102 *
103 * @since 0.8.0
104 *
105 * @internal
106 */
107 function cptui_deactivation() {
108 flush_rewrite_rules();
109 delete_option( 'cptui-user-dismissed-pro-upsell' );
110 delete_option( 'cptui-user-dismissed-extended-upsell' );
111 }
112 register_deactivation_hook( __FILE__, 'cptui_deactivation' );
113
114 /**
115 * Load our main menu.
116 *
117 * Submenu items added in version 1.1.0
118 *
119 * @since 0.1.0
120 *
121 * @internal
122 */
123 function cptui_plugin_menu() {
124
125 /**
126 * Filters the required capability to manage CPTUI settings.
127 *
128 * @since 1.3.0
129 *
130 * @param string $value Capability required.
131 */
132 $capability = apply_filters( 'cptui_required_capabilities', 'manage_options' );
133 $parent_slug = 'cptui_main_menu';
134
135 add_menu_page( esc_html__( 'Custom post types', 'custom-post-type-ui' ), esc_html__( 'CPT UI', 'custom-post-type-ui' ), $capability, $parent_slug, 'cptui_settings', cptui_menu_icon() );
136 add_submenu_page( $parent_slug, esc_html__( 'Add/edit post types', 'custom-post-type-ui' ), esc_html__( 'Add/edit post types', 'custom-post-type-ui' ), $capability, 'cptui_manage_post_types', 'cptui_manage_post_types' );
137 add_submenu_page( $parent_slug, esc_html__( 'Add/edit taxonomies', 'custom-post-type-ui' ), esc_html__( 'Add/edit taxonomies', 'custom-post-type-ui' ), $capability, 'cptui_manage_taxonomies', 'cptui_manage_taxonomies' );
138 add_submenu_page( $parent_slug, esc_html__( 'Custom Post Type UI content types', 'custom-post-type-ui' ), esc_html__( 'Registered types & taxonomies', 'custom-post-type-ui' ), $capability, 'cptui_listings', 'cptui_listings' );
139 add_submenu_page( $parent_slug, esc_html__( 'Custom Post Type UI tools', 'custom-post-type-ui' ), esc_html__( 'Tools', 'custom-post-type-ui' ), $capability, 'cptui_tools', 'cptui_tools' );
140 add_submenu_page( $parent_slug, esc_html__( 'Help/support', 'custom-post-type-ui' ), esc_html__( 'Help/support', 'custom-post-type-ui' ), $capability, 'cptui_support', 'cptui_support' );
141
142 /**
143 * Fires after the default submenu pages.
144 *
145 * @since 1.3.0
146 *
147 * @param string $value Parent slug for Custom Post Type UI menu.
148 * @param string $capability Capability required to manage CPTUI settings.
149 */
150 do_action( 'cptui_extra_menu_items', $parent_slug, $capability );
151
152 // Remove the default one so we can add our customized version.
153 remove_submenu_page( $parent_slug, 'cptui_main_menu' );
154 add_submenu_page( $parent_slug, esc_html__( 'About CPT UI', 'custom-post-type-ui' ), esc_html__( 'About CPT UI', 'custom-post-type-ui' ), $capability, 'cptui_main_menu', 'cptui_settings' );
155 }
156 add_action( 'admin_menu', 'cptui_plugin_menu' );
157
158 /**
159 * Fire our CPTUI Loaded hook.
160 *
161 * @since 1.3.0
162 *
163 * @internal Use `cptui_loaded` hook.
164 */
165 function cptui_loaded() {
166
167 if ( class_exists( 'WPGraphQL' ) ) {
168 require_once plugin_dir_path( __FILE__ ) . 'external/wpgraphql.php';
169 }
170
171 if ( defined( 'WPML_ST_VERSION' ) ) {
172 require_once plugin_dir_path( __FILE__ ) . 'external/wpml.php';
173 }
174
175 /**
176 * Fires upon plugins_loaded WordPress hook.
177 *
178 * CPTUI loads its required files on this hook.
179 *
180 * @since 1.3.0
181 */
182 do_action( 'cptui_loaded' );
183 }
184 add_action( 'plugins_loaded', 'cptui_loaded' );
185
186 /**
187 * Load our submenus.
188 *
189 * @since 1.0.0
190 *
191 * @internal
192 */
193 function cptui_includes() {
194 require_once plugin_dir_path( __FILE__ ) . 'inc/about.php';
195 require_once plugin_dir_path( __FILE__ ) . 'inc/utility.php';
196 require_once plugin_dir_path( __FILE__ ) . 'inc/post-types.php';
197 require_once plugin_dir_path( __FILE__ ) . 'inc/taxonomies.php';
198 require_once plugin_dir_path( __FILE__ ) . 'inc/listings.php';
199 require_once plugin_dir_path( __FILE__ ) . 'inc/tools.php';
200 require_once plugin_dir_path( __FILE__ ) . 'inc/tools-sections/tools-post-types.php';
201 require_once plugin_dir_path( __FILE__ ) . 'inc/tools-sections/tools-taxonomies.php';
202 require_once plugin_dir_path( __FILE__ ) . 'inc/tools-sections/tools-get-code.php';
203 require_once plugin_dir_path( __FILE__ ) . 'inc/tools-sections/tools-debug.php';
204 require_once plugin_dir_path( __FILE__ ) . 'inc/support.php';
205
206 if ( defined( 'WP_CLI' ) && WP_CLI ) {
207 require_once plugin_dir_path( __FILE__ ) . 'inc/wp-cli.php';
208 }
209 }
210 add_action( 'cptui_loaded', 'cptui_includes' );
211
212 /**
213 * Fire our CPTUI init hook.
214 *
215 * @since 1.3.0
216 *
217 * @internal Use `cptui_init` hook.
218 */
219 function cptui_init() {
220
221 /**
222 * Fires upon init WordPress hook.
223 *
224 * @since 1.3.0
225 */
226 do_action( 'cptui_init' );
227 }
228 add_action( 'init', 'cptui_init' );
229
230 /**
231 * Enqueue CPTUI admin styles.
232 *
233 * @since 1.0.0
234 *
235 * @internal
236 */
237 function cptui_add_styles() {
238 if ( wp_doing_ajax() ) {
239 return;
240 }
241 $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
242 wp_register_script( 'icon-picker', plugins_url( "build/vanilla-icon-picker/dist/icon-picker.min.js", __FILE__ ), [], '1.4.2', true );
243 wp_register_style( 'icon-picker', plugins_url( "build/vanilla-icon-picker/dist/themes/default.min.css", __FILE__ ), [], '1.4.2' );
244 wp_register_script( 'cptui', plugins_url( "build/cptui$min.js", __FILE__ ), [ 'jquery', 'jquery-ui-dialog', 'postbox', 'icon-picker' ], CPTUI_VERSION, true );
245 wp_register_style( 'cptui-css', plugins_url( "build/cptui-styles$min.css", __FILE__ ), [ 'wp-jquery-ui-dialog', 'icon-picker' ], CPTUI_VERSION );
246
247 wp_localize_script( 'icon-picker', 'cptuiIconPicker', [
248 'iconsJSON' => plugins_url( 'build/dashicons.json', __FILE__ ),
249 'iconsPlaceholder' => esc_attr__( 'Search icon &hellip;', 'custom-post-type-ui' ),
250 'iconsTitle' => esc_attr__( 'Select icon', 'custom-post-type-ui' ),
251 'iconsEmpty' => esc_attr__( 'No results found &hellip;', 'custom-post-type-ui' ),
252 'iconsLoading' => esc_attr__( 'Loading &hellip;', 'custom-post-type-ui' ),
253 'iconsSave' => esc_attr__( 'Save', 'custom-post-type-ui' ),
254 ] );
255 }
256 add_action( 'admin_enqueue_scripts', 'cptui_add_styles' );
257
258 /**
259 * Register our users' custom post types.
260 *
261 * @since 0.5.0
262 *
263 * @internal
264 */
265 function cptui_create_custom_post_types() {
266 $cpts = get_option( 'cptui_post_types', [] );
267 /**
268 * Filters an override array of post type data to be registered instead of our saved option.
269 *
270 * @since 1.10.0
271 *
272 * @param array $value Default override value.
273 */
274 $cpts_override = apply_filters( 'cptui_post_types_override', [] );
275
276 if ( empty( $cpts ) && empty( $cpts_override ) ) {
277 return;
278 }
279
280 // Assume good intent, and we're also not wrecking the option so things are always reversable.
281 if ( is_array( $cpts_override ) && ! empty( $cpts_override ) ) {
282 $cpts = $cpts_override;
283 }
284
285 /**
286 * Fires before the start of the post type registrations.
287 *
288 * @since 1.3.0
289 *
290 * @param array $cpts Array of post types to register.
291 */
292 do_action( 'cptui_pre_register_post_types', $cpts );
293
294 if ( is_array( $cpts ) ) {
295 foreach ( $cpts as $post_type ) {
296
297 if ( ! is_string( $post_type['name'] ) ) {
298 $post_type['name'] = (string) $post_type['name'];
299 }
300 /**
301 * Filters whether or not to skip registration of the current iterated post type.
302 *
303 * Dynamic part of the filter name is the chosen post type slug.
304 *
305 * @since 1.7.0
306 *
307 * @param bool $value Whether or not to skip the post type.
308 * @param array $post_type Current post type being registered.
309 */
310 if ( (bool) apply_filters( "cptui_disable_{$post_type['name']}_cpt", false, $post_type ) ) {
311 continue;
312 }
313
314 /**
315 * Filters whether or not to skip registration of the current iterated post type.
316 *
317 * @since 1.7.0
318 *
319 * @param bool $value Whether or not to skip the post type.
320 * @param array $post_type Current post type being registered.
321 */
322 if ( (bool) apply_filters( 'cptui_disable_cpt', false, $post_type ) ) {
323 continue;
324 }
325
326 cptui_register_single_post_type( $post_type );
327 }
328 }
329
330 /**
331 * Fires after the completion of the post type registrations.
332 *
333 * @since 1.3.0
334 *
335 * @param array $cpts Array of post types registered.
336 */
337 do_action( 'cptui_post_register_post_types', $cpts );
338 }
339 add_action( 'init', 'cptui_create_custom_post_types' ); // Leave on standard init for legacy purposes.
340
341 /**
342 * Helper function to register the actual post_type.
343 *
344 * @since 1.0.0
345 *
346 * @internal
347 *
348 * @param array $post_type Post type array to register. Optional.
349 * @return null Result of register_post_type.
350 */
351 function cptui_register_single_post_type( array $post_type = [] ) {
352
353 /**
354 * Filters the map_meta_cap value.
355 *
356 * @since 1.0.0
357 *
358 * @param bool $value True.
359 * @param string $name Post type name being registered.
360 * @param array $post_type All parameters for post type registration.
361 */
362 $post_type['map_meta_cap'] = apply_filters( 'cptui_map_meta_cap', true, $post_type['name'], $post_type );
363
364 if ( empty( $post_type['supports'] ) ) {
365 $post_type['supports'] = [];
366 }
367
368 /**
369 * Filters custom supports parameters for 3rd party plugins.
370 *
371 * @since 1.0.0
372 *
373 * @param array $value Empty array to add supports keys to.
374 * @param string $name Post type slug being registered.
375 * @param array $post_type Array of post type arguments to be registered.
376 */
377 $user_supports_params = apply_filters( 'cptui_user_supports_params', [], $post_type['name'], $post_type );
378
379 if ( is_array( $user_supports_params ) && ! empty( $user_supports_params ) ) {
380 if ( is_array( $post_type['supports'] ) ) {
381 $post_type['supports'] = array_merge( $post_type['supports'], $user_supports_params );
382 } else {
383 $post_type['supports'] = [ $user_supports_params ];
384 }
385 }
386
387 $yarpp = false; // Prevent notices.
388 if ( ! empty( $post_type['custom_supports'] ) ) {
389 $custom = explode( ',', $post_type['custom_supports'] );
390 foreach ( $custom as $part ) {
391 // We'll handle YARPP separately.
392 if ( in_array( $part, [ 'YARPP', 'yarpp' ], true ) ) {
393 $yarpp = true;
394 continue;
395 }
396 $post_type['supports'][] = trim( $part );
397 }
398 }
399
400 if ( isset( $post_type['supports'] ) && is_array( $post_type['supports'] ) && in_array( 'none', $post_type['supports'], true ) ) {
401 $post_type['supports'] = false;
402 }
403
404 $labels = [
405 'name' => $post_type['label'],
406 'singular_name' => $post_type['singular_label'],
407 ];
408
409 $preserved = cptui_get_preserved_keys( 'post_types' );
410 $preserved_labels = cptui_get_preserved_labels();
411 foreach ( $post_type['labels'] as $key => $label ) {
412
413 if ( ! empty( $label ) ) {
414 if ( 'parent' === $key ) {
415 $labels['parent_item_colon'] = $label;
416 } else {
417 $labels[ $key ] = $label;
418 }
419 } elseif ( empty( $label ) && in_array( $key, $preserved, true ) ) {
420 $singular_or_plural = ( in_array( $key, array_keys( $preserved_labels['post_types']['plural'] ) ) ) ? 'plural' : 'singular'; // phpcs:ignore.
421 $label_plurality = ( 'plural' === $singular_or_plural ) ? $post_type['label'] : $post_type['singular_label'];
422 $labels[ $key ] = sprintf( $preserved_labels['post_types'][ $singular_or_plural ][ $key ], $label_plurality );
423 }
424 }
425
426 $has_archive = isset( $post_type['has_archive'] ) ? get_disp_boolean( $post_type['has_archive'] ) : false;
427 if ( $has_archive && ! empty( $post_type['has_archive_string'] ) ) {
428 $has_archive = $post_type['has_archive_string'];
429 }
430
431 $show_in_menu = get_disp_boolean( $post_type['show_in_menu'] );
432 if ( ! empty( $post_type['show_in_menu_string'] ) ) {
433 $show_in_menu = $post_type['show_in_menu_string'];
434 }
435
436 $rewrite = get_disp_boolean( $post_type['rewrite'] );
437 if ( false !== $rewrite ) {
438 // Core converts to an empty array anyway, so safe to leave this instead of passing in boolean true.
439 $rewrite = [];
440 $rewrite['slug'] = ! empty( $post_type['rewrite_slug'] ) ? $post_type['rewrite_slug'] : $post_type['name'];
441
442 $rewrite['with_front'] = true; // Default value.
443 if ( isset( $post_type['rewrite_withfront'] ) ) {
444 $rewrite['with_front'] = 'false' === disp_boolean( $post_type['rewrite_withfront'] ) ? false : true;
445 }
446 }
447
448 $menu_icon = ! empty( $post_type['menu_icon'] ) ? $post_type['menu_icon'] : null;
449 $register_meta_box_cb = ! empty( $post_type['register_meta_box_cb'] ) ? $post_type['register_meta_box_cb'] : null;
450
451 if ( in_array( $post_type['query_var'], [ 'true', 'false', '0', '1' ], true ) ) {
452 $post_type['query_var'] = get_disp_boolean( $post_type['query_var'] );
453 }
454 if ( ! empty( $post_type['query_var_slug'] ) ) {
455 $post_type['query_var'] = $post_type['query_var_slug'];
456 }
457
458 $menu_position = null;
459 if ( ! empty( $post_type['menu_position'] ) ) {
460 $menu_position = (int) $post_type['menu_position'];
461 }
462
463 $delete_with_user = null;
464 if ( ! empty( $post_type['delete_with_user'] ) ) {
465 $delete_with_user = get_disp_boolean( $post_type['delete_with_user'] );
466 }
467
468 $capability_type = 'post';
469 if ( ! empty( $post_type['capability_type'] ) ) {
470 $capability_type = $post_type['capability_type'];
471 if ( false !== strpos( $post_type['capability_type'], ',' ) ) {
472 $caps = array_map( 'trim', explode( ',', $post_type['capability_type'] ) );
473 if ( count( $caps ) > 2 ) {
474 $caps = array_slice( $caps, 0, 2 );
475 }
476 $capability_type = $caps;
477 }
478 }
479
480 $public = get_disp_boolean( $post_type['public'] );
481 if ( ! empty( $post_type['exclude_from_search'] ) ) {
482 $exclude_from_search = get_disp_boolean( $post_type['exclude_from_search'] );
483 } else {
484 $exclude_from_search = false === $public;
485 }
486
487 $queryable = ( ! empty( $post_type['publicly_queryable'] ) && isset( $post_type['publicly_queryable'] ) ) ? get_disp_boolean( $post_type['publicly_queryable'] ) : $public;
488
489 if ( empty( $post_type['show_in_nav_menus'] ) ) {
490 // Defaults to value of public.
491 $post_type['show_in_nav_menus'] = $public;
492 }
493
494 if ( empty( $post_type['show_in_rest'] ) ) {
495 $post_type['show_in_rest'] = false;
496 }
497
498 $rest_base = null;
499 if ( ! empty( $post_type['rest_base'] ) ) {
500 $rest_base = $post_type['rest_base'];
501 }
502
503 $rest_controller_class = null;
504 if ( ! empty( $post_type['rest_controller_class'] ) ) {
505 $rest_controller_class = $post_type['rest_controller_class'];
506 }
507
508 $rest_namespace = null;
509 if ( ! empty( $post_type['rest_namespace'] ) ) {
510 $rest_namespace = $post_type['rest_namespace'];
511 }
512
513 $can_export = null;
514 if ( ! empty( $post_type['can_export'] ) ) {
515 $can_export = get_disp_boolean( $post_type['can_export'] );
516 }
517
518 $args = [
519 'labels' => $labels,
520 'description' => $post_type['description'],
521 'public' => get_disp_boolean( $post_type['public'] ),
522 'publicly_queryable' => $queryable,
523 'show_ui' => get_disp_boolean( $post_type['show_ui'] ),
524 'show_in_nav_menus' => get_disp_boolean( $post_type['show_in_nav_menus'] ),
525 'has_archive' => $has_archive,
526 'show_in_menu' => $show_in_menu,
527 'delete_with_user' => $delete_with_user,
528 'show_in_rest' => get_disp_boolean( $post_type['show_in_rest'] ),
529 'rest_base' => $rest_base,
530 'rest_controller_class' => $rest_controller_class,
531 'rest_namespace' => $rest_namespace,
532 'exclude_from_search' => $exclude_from_search,
533 'capability_type' => $capability_type,
534 'map_meta_cap' => $post_type['map_meta_cap'],
535 'hierarchical' => get_disp_boolean( $post_type['hierarchical'] ),
536 'can_export' => $can_export,
537 'rewrite' => $rewrite,
538 'menu_position' => $menu_position,
539 'menu_icon' => $menu_icon,
540 'register_meta_box_cb' => $register_meta_box_cb,
541 'query_var' => $post_type['query_var'],
542 'supports' => $post_type['supports'],
543 'taxonomies' => $post_type['taxonomies'],
544 ];
545
546 if ( true === $yarpp ) {
547 $args['yarpp_support'] = $yarpp;
548 }
549
550 /**
551 * Filters the arguments used for a post type right before registering.
552 *
553 * @since 1.0.0
554 * @since 1.3.0 Added original passed in values array
555 *
556 * @param array $args Array of arguments to use for registering post type.
557 * @param string $value Post type slug to be registered.
558 * @param array $post_type Original passed in values for post type.
559 */
560 $args = apply_filters( 'cptui_pre_register_post_type', $args, $post_type['name'], $post_type );
561
562 return register_post_type( $post_type['name'], $args );
563 }
564
565 /**
566 * Register our users' custom taxonomies.
567 *
568 * @since 0.5.0
569 *
570 * @internal
571 */
572 function cptui_create_custom_taxonomies() {
573 $taxes = get_option( 'cptui_taxonomies', [] );
574 /**
575 * Filters an override array of taxonomy data to be registered instead of our saved option.
576 *
577 * @since 1.10.0
578 *
579 * @param array $value Default override value.
580 */
581 $taxes_override = apply_filters( 'cptui_taxonomies_override', [] );
582
583 if ( empty( $taxes ) && empty( $taxes_override ) ) {
584 return;
585 }
586
587 // Assume good intent, and we're also not wrecking the option so things are always reversable.
588 if ( is_array( $taxes_override ) && ! empty( $taxes_override ) ) {
589 $taxes = $taxes_override;
590 }
591
592 /**
593 * Fires before the start of the taxonomy registrations.
594 *
595 * @since 1.3.0
596 *
597 * @param array $taxes Array of taxonomies to register.
598 */
599 do_action( 'cptui_pre_register_taxonomies', $taxes );
600
601 if ( is_array( $taxes ) ) {
602 foreach ( $taxes as $tax ) {
603
604 if ( ! is_string( $tax['name'] ) ) {
605 $tax['name'] = (string) $tax['name'];
606 }
607 /**
608 * Filters whether or not to skip registration of the current iterated taxonomy.
609 *
610 * Dynamic part of the filter name is the chosen taxonomy slug.
611 *
612 * @since 1.7.0
613 *
614 * @param bool $value Whether or not to skip the taxonomy.
615 * @param array $tax Current taxonomy being registered.
616 */
617 if ( (bool) apply_filters( "cptui_disable_{$tax['name']}_tax", false, $tax ) ) {
618 continue;
619 }
620
621 /**
622 * Filters whether or not to skip registration of the current iterated taxonomy.
623 *
624 * @since 1.7.0
625 *
626 * @param bool $value Whether or not to skip the taxonomy.
627 * @param array $tax Current taxonomy being registered.
628 */
629 if ( (bool) apply_filters( 'cptui_disable_tax', false, $tax ) ) {
630 continue;
631 }
632
633 cptui_register_single_taxonomy( $tax );
634 }
635 }
636
637 /**
638 * Fires after the completion of the taxonomy registrations.
639 *
640 * @since 1.3.0
641 *
642 * @param array $taxes Array of taxonomies registered.
643 */
644 do_action( 'cptui_post_register_taxonomies', $taxes );
645 }
646 add_action( 'init', 'cptui_create_custom_taxonomies', 9 ); // Leave on standard init for legacy purposes.
647
648 /**
649 * Helper function to register the actual taxonomy.
650 *
651 * @since 1.0.0
652 *
653 * @internal
654 *
655 * @param array $taxonomy Taxonomy array to register. Optional.
656 * @return null Result of register_taxonomy.
657 */
658 function cptui_register_single_taxonomy( array $taxonomy = [] ) {
659
660 $labels = [
661 'name' => $taxonomy['label'],
662 'singular_name' => $taxonomy['singular_label'],
663 ];
664
665 $description = '';
666 if ( ! empty( $taxonomy['description'] ) ) {
667 $description = $taxonomy['description'];
668 }
669
670 $preserved = cptui_get_preserved_keys( 'taxonomies' );
671 $preserved_labels = cptui_get_preserved_labels();
672 foreach ( $taxonomy['labels'] as $key => $label ) {
673
674 if ( ! empty( $label ) ) {
675 $labels[ $key ] = $label;
676 } elseif ( in_array( $key, $preserved, true ) ) {
677 $singular_or_plural = ( in_array( $key, array_keys( $preserved_labels['taxonomies']['plural'] ) ) ) ? 'plural' : 'singular'; // phpcs:ignore.
678 $label_plurality = ( 'plural' === $singular_or_plural ) ? $taxonomy['label'] : $taxonomy['singular_label'];
679 $labels[ $key ] = sprintf( $preserved_labels['taxonomies'][ $singular_or_plural ][ $key ], $label_plurality );
680 }
681 }
682
683 $rewrite = get_disp_boolean( $taxonomy['rewrite'] );
684 if ( false !== get_disp_boolean( $taxonomy['rewrite'] ) ) {
685 $rewrite = [];
686 $rewrite['slug'] = ! empty( $taxonomy['rewrite_slug'] ) ? $taxonomy['rewrite_slug'] : $taxonomy['name'];
687 $rewrite['with_front'] = true;
688 if ( isset( $taxonomy['rewrite_withfront'] ) ) {
689 $rewrite['with_front'] = ( 'false' === disp_boolean( $taxonomy['rewrite_withfront'] ) ) ? false : true;
690 }
691 $rewrite['hierarchical'] = false;
692 if ( isset( $taxonomy['rewrite_hierarchical'] ) ) {
693 $rewrite['hierarchical'] = ( 'true' === disp_boolean( $taxonomy['rewrite_hierarchical'] ) ) ? true : false;
694 }
695 }
696
697 if ( in_array( $taxonomy['query_var'], [ 'true', 'false', '0', '1' ], true ) ) {
698 $taxonomy['query_var'] = get_disp_boolean( $taxonomy['query_var'] );
699 }
700 if ( true === $taxonomy['query_var'] && ! empty( $taxonomy['query_var_slug'] ) ) {
701 $taxonomy['query_var'] = $taxonomy['query_var_slug'];
702 }
703
704 $public = ( ! empty( $taxonomy['public'] ) && false === get_disp_boolean( $taxonomy['public'] ) ) ? false : true;
705 $publicly_queryable = ( ! empty( $taxonomy['publicly_queryable'] ) && false === get_disp_boolean( $taxonomy['publicly_queryable'] ) ) ? false : true;
706 if ( empty( $taxonomy['publicly_queryable'] ) ) {
707 $publicly_queryable = $public;
708 }
709
710 $show_admin_column = ( ! empty( $taxonomy['show_admin_column'] ) && false !== get_disp_boolean( $taxonomy['show_admin_column'] ) ) ? true : false;
711
712 $show_in_menu = ( ! empty( $taxonomy['show_in_menu'] ) && false !== get_disp_boolean( $taxonomy['show_in_menu'] ) ) ? true : false;
713
714 if ( empty( $taxonomy['show_in_menu'] ) ) {
715 $show_in_menu = get_disp_boolean( $taxonomy['show_ui'] );
716 }
717
718 $show_in_nav_menus = ( ! empty( $taxonomy['show_in_nav_menus'] ) && false !== get_disp_boolean( $taxonomy['show_in_nav_menus'] ) ) ? true : false;
719 if ( empty( $taxonomy['show_in_nav_menus'] ) ) {
720 $show_in_nav_menus = $public;
721 }
722
723 $show_tagcloud = ( ! empty( $taxonomy['show_tagcloud'] ) && false !== get_disp_boolean( $taxonomy['show_tagcloud'] ) ) ? true : false;
724 if ( empty( $taxonomy['show_tagcloud'] ) ) {
725 $show_tagcloud = get_disp_boolean( $taxonomy['show_ui'] );
726 }
727
728 $show_in_rest = ( ! empty( $taxonomy['show_in_rest'] ) && false !== get_disp_boolean( $taxonomy['show_in_rest'] ) ) ? true : false;
729
730 $show_in_quick_edit = ( ! empty( $taxonomy['show_in_quick_edit'] ) && false !== get_disp_boolean( $taxonomy['show_in_quick_edit'] ) ) ? true : false;
731
732 $sort = ( ! empty( $taxonomy['sort'] ) && false !== get_disp_boolean( $taxonomy['sort'] ) ) ? true : false;
733
734 $rest_base = null;
735 if ( ! empty( $taxonomy['rest_base'] ) ) {
736 $rest_base = $taxonomy['rest_base'];
737 }
738
739 $rest_controller_class = null;
740 if ( ! empty( $taxonomy['rest_controller_class'] ) ) {
741 $rest_controller_class = $taxonomy['rest_controller_class'];
742 }
743
744 $rest_namespace = null;
745 if ( ! empty( $taxonomy['rest_namespace'] ) ) {
746 $rest_namespace = $taxonomy['rest_namespace'];
747 }
748
749 $meta_box_cb = null;
750 if ( ! empty( $taxonomy['meta_box_cb'] ) ) {
751 $meta_box_cb = ( false !== get_disp_boolean( $taxonomy['meta_box_cb'] ) ) ? $taxonomy['meta_box_cb'] : false;
752 }
753 $default_term = null;
754 if ( ! empty( $taxonomy['default_term'] ) ) {
755 $term_parts = explode( ',', $taxonomy['default_term'] );
756 if ( ! empty( $term_parts[0] ) ) {
757 $default_term['name'] = trim( $term_parts[0] );
758 }
759 if ( ! empty( $term_parts[1] ) ) {
760 $default_term['slug'] = trim( $term_parts[1] );
761 }
762 if ( ! empty( $term_parts[2] ) ) {
763 $default_term['description'] = trim( $term_parts[2] );
764 }
765 }
766
767 $args = [
768 'labels' => $labels,
769 'label' => $taxonomy['label'],
770 'description' => $description,
771 'public' => $public,
772 'publicly_queryable' => $publicly_queryable,
773 'hierarchical' => get_disp_boolean( $taxonomy['hierarchical'] ),
774 'show_ui' => get_disp_boolean( $taxonomy['show_ui'] ),
775 'show_in_menu' => $show_in_menu,
776 'show_in_nav_menus' => $show_in_nav_menus,
777 'show_tagcloud' => $show_tagcloud,
778 'query_var' => $taxonomy['query_var'],
779 'rewrite' => $rewrite,
780 'show_admin_column' => $show_admin_column,
781 'show_in_rest' => $show_in_rest,
782 'rest_base' => $rest_base,
783 'rest_controller_class' => $rest_controller_class,
784 'rest_namespace' => $rest_namespace,
785 'show_in_quick_edit' => $show_in_quick_edit,
786 'sort' => $sort,
787 'meta_box_cb' => $meta_box_cb,
788 'default_term' => $default_term,
789 ];
790
791 $object_type = ! empty( $taxonomy['object_types'] ) ? $taxonomy['object_types'] : '';
792
793 /**
794 * Filters the arguments used for a taxonomy right before registering.
795 *
796 * @since 1.0.0
797 * @since 1.3.0 Added original passed in values array
798 * @since 1.6.0 Added $obect_type variable to passed parameters.
799 *
800 * @param array $args Array of arguments to use for registering taxonomy.
801 * @param string $value Taxonomy slug to be registered.
802 * @param array $taxonomy Original passed in values for taxonomy.
803 * @param array $object_type Array of chosen post types for the taxonomy.
804 */
805 $args = apply_filters( 'cptui_pre_register_taxonomy', $args, $taxonomy['name'], $taxonomy, $object_type );
806
807 return register_taxonomy( $taxonomy['name'], $object_type, $args );
808 }
809
810 /**
811 * Construct and output tab navigation.
812 *
813 * @since 1.0.0
814 *
815 * @param string $page Whether it's the CPT or Taxonomy page. Optional. Default "post_types".
816 * @return string
817 */
818 function cptui_settings_tab_menu( string $page = 'post_types' ) {
819
820 /**
821 * Filters the tabs to render on a given page.
822 *
823 * @since 1.3.0
824 *
825 * @param array $value Array of tabs to render.
826 * @param string $page Current page being displayed.
827 */
828 $tabs = (array) apply_filters( 'cptui_get_tabs', [], $page );
829
830 if ( empty( $tabs['page_title'] ) ) {
831 return '';
832 }
833
834 $tmpl = '<h1>%s</h1><nav class="nav-tab-wrapper wp-clearfix" aria-label="%s">%s</nav>';
835
836 $tab_output = '';
837 foreach ( $tabs['tabs'] as $tab ) {
838 $tab_output .= sprintf(
839 '<a class="%s" href="%s" aria-selected="%s">%s</a>',
840 implode( ' ', $tab['classes'] ),
841 $tab['url'],
842 $tab['aria-selected'],
843 $tab['text']
844 );
845 }
846
847 printf(
848 $tmpl, // phpcs:ignore.
849 $tabs['page_title'], // phpcs:ignore.
850 esc_attr__( 'Secondary menu', 'custom-post-type-ui' ), // phpcs:ignore
851 $tab_output // phpcs:ignore.
852 );
853 }
854
855 /**
856 * Convert our old settings to the new options keys.
857 *
858 * These are left with standard get_option/update_option function calls for legacy and pending update purposes.
859 *
860 * @since 1.0.0
861 *
862 * @internal
863 *
864 * @return bool Whether or not options were successfully updated.
865 */
866 function cptui_convert_settings() {
867
868 if ( wp_doing_ajax() ) {
869 return false;
870 }
871
872 $retval = '';
873
874 if ( false === get_option( 'cptui_post_types' ) && ( $post_types = get_option( 'cpt_custom_post_types' ) ) ) { // phpcs:ignore.
875
876 $new_post_types = [];
877 foreach ( $post_types as $type ) {
878 $new_post_types[ $type['name'] ] = $type; // This one assigns the # indexes. Named arrays are our friend.
879 $new_post_types[ $type['name'] ]['supports'] = ! empty( $type[0] ) ? $type[0] : []; // Especially for multidimensional arrays.
880 $new_post_types[ $type['name'] ]['taxonomies'] = ! empty( $type[1] ) ? $type[1] : [];
881 $new_post_types[ $type['name'] ]['labels'] = ! empty( $type[2] ) ? $type[2] : [];
882 unset(
883 $new_post_types[ $type['name'] ][0],
884 $new_post_types[ $type['name'] ][1],
885 $new_post_types[ $type['name'] ][2]
886 ); // Remove our previous indexed versions.
887 }
888
889 $retval = update_option( 'cptui_post_types', $new_post_types );
890 }
891
892 if ( false === get_option( 'cptui_taxonomies' ) && ( $taxonomies = get_option( 'cpt_custom_tax_types' ) ) ) { // phpcs:ignore.
893
894 $new_taxonomies = [];
895 foreach ( $taxonomies as $tax ) {
896 $new_taxonomies[ $tax['name'] ] = $tax; // Yep, still our friend.
897 $new_taxonomies[ $tax['name'] ]['labels'] = $tax[0]; // Taxonomies are the only thing with.
898 $new_taxonomies[ $tax['name'] ]['object_types'] = $tax[1]; // "tax" in the name that I like.
899 unset(
900 $new_taxonomies[ $tax['name'] ][0],
901 $new_taxonomies[ $tax['name'] ][1]
902 );
903 }
904
905 $retval = update_option( 'cptui_taxonomies', $new_taxonomies );
906 }
907
908 if ( ! empty( $retval ) ) {
909 flush_rewrite_rules();
910 }
911
912 return $retval;
913 }
914 add_action( 'admin_init', 'cptui_convert_settings' );
915
916 /**
917 * Return a notice based on conditions.
918 *
919 * @since 1.0.0
920 *
921 * @param string $action The type of action that occurred. Optional. Default empty string.
922 * @param string $object_type Whether it's from a post type or taxonomy. Optional. Default empty string.
923 * @param bool $success Whether the action succeeded or not. Optional. Default true.
924 * @param string $custom Custom message if necessary. Optional. Default empty string.
925 * @return bool|string false on no message, else HTML div with our notice message.
926 */
927 function cptui_admin_notices( string $action = '', string $object_type = '', bool $success = true, string $custom = '' ) {
928
929 $class = [];
930 $class[] = $success ? 'updated' : 'error';
931 $class[] = 'notice is-dismissible';
932 $object_type = esc_attr( $object_type );
933
934 $messagewrapstart = '<div id="message" class="' . implode( ' ', $class ) . '"><p>';
935 $message = '';
936
937 $messagewrapend = '</p></div>';
938
939 if ( 'add' === $action ) {
940 if ( $success ) {
941 // translators: placeholder holds content name.
942 $message .= sprintf( esc_html__( '%s has been successfully added', 'custom-post-type-ui' ), $object_type );
943 } else {
944 // translators: placeholder holds content name.
945 $message .= sprintf( esc_html__( '%s has failed to be added', 'custom-post-type-ui' ), $object_type );
946 }
947 } elseif ( 'update' === $action ) {
948 if ( $success ) {
949 // translators: placeholder holds content name.
950 $message .= sprintf( esc_html__( '%s has been successfully updated', 'custom-post-type-ui' ), $object_type );
951 } else {
952 // translators: placeholder holds content name.
953 $message .= sprintf( esc_html__( '%s has failed to be updated', 'custom-post-type-ui' ), $object_type );
954 }
955 } elseif ( 'delete' === $action ) {
956 if ( $success ) {
957 // translators: placeholder holds content name.
958 $message .= sprintf( esc_html__( '%s has been successfully deleted', 'custom-post-type-ui' ), $object_type );
959 } else {
960 // translators: placeholder holds content name.
961 $message .= sprintf( esc_html__( '%s has failed to be deleted', 'custom-post-type-ui' ), $object_type );
962 }
963 } elseif ( 'import' === $action ) {
964 if ( $success ) {
965 // translators: placeholder holds content name.
966 $message .= sprintf( esc_html__( '%s has been successfully imported', 'custom-post-type-ui' ), $object_type );
967 } else {
968 // translators: placeholder holds content name.
969 $message .= sprintf( esc_html__( '%s has failed to be imported', 'custom-post-type-ui' ), $object_type );
970 }
971 } elseif ( 'error' === $action ) {
972 if ( ! empty( $custom ) ) {
973 $message = $custom;
974 }
975 }
976
977 if ( $message ) {
978
979 /**
980 * Filters the custom admin notice for CPTUI.
981 *
982 * @since 1.0.0
983 *
984 * @param string $value Complete HTML output for notice.
985 * @param string $action Action whose message is being generated.
986 * @param string $message The message to be displayed.
987 * @param string $messagewrapstart Beginning wrap HTML.
988 * @param string $messagewrapend Ending wrap HTML.
989 */
990 return apply_filters( 'cptui_admin_notice', $messagewrapstart . $message . $messagewrapend, $action, $message, $messagewrapstart, $messagewrapend );
991 }
992
993 return false;
994 }
995
996 /**
997 * Return array of keys needing preserved.
998 *
999 * @since 1.0.5
1000 *
1001 * @param string $type Type to return. Either 'post_types' or 'taxonomies'. Optional. Default empty string.
1002 * @return array Array of keys needing preservered for the requested type.
1003 */
1004 function cptui_get_preserved_keys( string $type = '' ) {
1005
1006 $preserved_labels = [
1007 'post_types' => [
1008 'add_new_item',
1009 'edit_item',
1010 'new_item',
1011 'view_item',
1012 'view_items',
1013 'all_items',
1014 'search_items',
1015 'not_found',
1016 'not_found_in_trash',
1017 ],
1018 'taxonomies' => [
1019 'search_items',
1020 'popular_items',
1021 'all_items',
1022 'parent_item',
1023 'parent_item_colon',
1024 'edit_item',
1025 'update_item',
1026 'add_new_item',
1027 'new_item_name',
1028 'separate_items_with_commas',
1029 'add_or_remove_items',
1030 'choose_from_most_used',
1031 ],
1032 ];
1033 return ! empty( $type ) ? $preserved_labels[ $type ] : [];
1034 }
1035
1036 /**
1037 * Return label for the requested type and label key.
1038 *
1039 * @since 1.0.5
1040 *
1041 * @deprecated
1042 *
1043 * @param string $type Type to return. Either 'post_types' or 'taxonomies'. Optional. Default empty string.
1044 * @param string $key Requested label key. Optional. Default empty string.
1045 * @param string $plural Plural verbiage for the requested label and type. Optional. Default empty string.
1046 * @param string $singular Singular verbiage for the requested label and type. Optional. Default empty string.
1047 * @return string Internationalized default label.
1048 */
1049 function cptui_get_preserved_label( string $type = '', string $key = '', string $plural = '', string $singular = '' ) {
1050
1051 $preserved_labels = [
1052 'post_types' => [
1053 // translators: placeholder holds content label.
1054 'add_new_item' => sprintf( esc_html__( 'Add new %s', 'custom-post-type-ui' ), $singular ),
1055 // translators: placeholder holds content label.
1056 'edit_item' => sprintf( esc_html__( 'Edit %s', 'custom-post-type-ui' ), $singular ),
1057 // translators: placeholder holds content label.
1058 'new_item' => sprintf( esc_html__( 'New %s', 'custom-post-type-ui' ), $singular ),
1059 // translators: placeholder holds content label.
1060 'view_item' => sprintf( esc_html__( 'View %s', 'custom-post-type-ui' ), $singular ),
1061 // translators: placeholder holds content label.
1062 'view_items' => sprintf( esc_html__( 'View %s', 'custom-post-type-ui' ), $plural ),
1063 // translators: placeholder holds content label.
1064 'all_items' => sprintf( esc_html__( 'All %s', 'custom-post-type-ui' ), $plural ),
1065 // translators: placeholder holds content label.
1066 'search_items' => sprintf( esc_html__( 'Search %s', 'custom-post-type-ui' ), $plural ),
1067 // translators: placeholder holds content label.
1068 'not_found' => sprintf( esc_html__( 'No %s found.', 'custom-post-type-ui' ), $plural ),
1069 // translators: placeholder holds content label.
1070 'not_found_in_trash' => sprintf( esc_html__( 'No %s found in trash.', 'custom-post-type-ui' ), $plural ),
1071 ],
1072 'taxonomies' => [
1073 // translators: placeholder holds content label.
1074 'search_items' => sprintf( esc_html__( 'Search %s', 'custom-post-type-ui' ), $plural ),
1075 // translators: placeholder holds content label.
1076 'popular_items' => sprintf( esc_html__( 'Popular %s', 'custom-post-type-ui' ), $plural ),
1077 // translators: placeholder holds content label.
1078 'all_items' => sprintf( esc_html__( 'All %s', 'custom-post-type-ui' ), $plural ),
1079 // translators: placeholder holds content label.
1080 'parent_item' => sprintf( esc_html__( 'Parent %s', 'custom-post-type-ui' ), $singular ),
1081 // translators: placeholder holds content label.
1082 'parent_item_colon' => sprintf( esc_html__( 'Parent %s:', 'custom-post-type-ui' ), $singular ),
1083 // translators: placeholder holds content label.
1084 'edit_item' => sprintf( esc_html__( 'Edit %s', 'custom-post-type-ui' ), $singular ),
1085 // translators: placeholder holds content label.
1086 'update_item' => sprintf( esc_html__( 'Update %s', 'custom-post-type-ui' ), $singular ),
1087 // translators: placeholder holds content label.
1088 'add_new_item' => sprintf( esc_html__( 'Add new %s', 'custom-post-type-ui' ), $singular ),
1089 // translators: placeholder holds content label.
1090 'new_item_name' => sprintf( esc_html__( 'New %s name', 'custom-post-type-ui' ), $singular ),
1091 // translators: placeholder holds content label.
1092 'separate_items_with_commas' => sprintf( esc_html__( 'Separate %s with commas', 'custom-post-type-ui' ), $plural ),
1093 // translators: placeholder holds content label.
1094 'add_or_remove_items' => sprintf( esc_html__( 'Add or remove %s', 'custom-post-type-ui' ), $plural ),
1095 // translators: placeholder holds content label.
1096 'choose_from_most_used' => sprintf( esc_html__( 'Choose from the most used %s', 'custom-post-type-ui' ), $plural ),
1097 ],
1098 ];
1099
1100 return $preserved_labels[ $type ][ $key ];
1101 }
1102
1103 /**
1104 * Returns an array of translated labels, ready for use with sprintf().
1105 *
1106 * Replacement for cptui_get_preserved_label for the sake of performance.
1107 *
1108 * @since 1.6.0
1109 *
1110 * @return array
1111 */
1112 function cptui_get_preserved_labels() {
1113 return [
1114 'post_types' => [
1115 'singular' => [
1116 // translators: placeholder holds content label.
1117 'add_new_item' => esc_html__( 'Add new %s', 'custom-post-type-ui' ),
1118 // translators: placeholder holds content label.
1119 'edit_item' => esc_html__( 'Edit %s', 'custom-post-type-ui' ),
1120 // translators: placeholder holds content label.
1121 'new_item' => esc_html__( 'New %s', 'custom-post-type-ui' ),
1122 // translators: placeholder holds content label.
1123 'view_item' => esc_html__( 'View %s', 'custom-post-type-ui' ),
1124 // translators: placeholder holds content label.
1125 'template_name' => esc_html__( 'Single item: %s', 'custom-post-type-ui' ),
1126 ],
1127 'plural' => [
1128 // translators: placeholder holds content label.
1129 'view_items' => esc_html__( 'View %s', 'custom-post-type-ui' ),
1130 // translators: placeholder holds content label.
1131 'all_items' => esc_html__( 'All %s', 'custom-post-type-ui' ),
1132 // translators: placeholder holds content label.
1133 'search_items' => esc_html__( 'Search %s', 'custom-post-type-ui' ),
1134 // translators: placeholder holds content label.
1135 'not_found' => esc_html__( 'No %s found.', 'custom-post-type-ui' ),
1136 // translators: placeholder holds content label.
1137 'not_found_in_trash' => esc_html__( 'No %s found in trash.', 'custom-post-type-ui' ),
1138 ],
1139 ],
1140 'taxonomies' => [
1141 'singular' => [
1142 // translators: placeholder holds content label.
1143 'parent_item' => esc_html__( 'Parent %s', 'custom-post-type-ui' ),
1144 // translators: placeholder holds content label.
1145 'parent_item_colon' => esc_html__( 'Parent %s:', 'custom-post-type-ui' ),
1146 // translators: placeholder holds content label.
1147 'edit_item' => esc_html__( 'Edit %s', 'custom-post-type-ui' ),
1148 // translators: placeholder holds content label.
1149 'update_item' => esc_html__( 'Update %s', 'custom-post-type-ui' ),
1150 // translators: placeholder holds content label.
1151 'add_new_item' => esc_html__( 'Add new %s', 'custom-post-type-ui' ),
1152 // translators: placeholder holds content label.
1153 'new_item_name' => esc_html__( 'New %s name', 'custom-post-type-ui' ),
1154 // translators: placeholder holds content label.
1155 'template_name' => esc_html__( '%s Archives', 'custom-post-type-ui' ),
1156 ],
1157 'plural' => [
1158 // translators: placeholder holds content label.
1159 'search_items' => esc_html__( 'Search %s', 'custom-post-type-ui' ),
1160 // translators: placeholder holds content label.
1161 'popular_items' => esc_html__( 'Popular %s', 'custom-post-type-ui' ),
1162 // translators: placeholder holds content label.
1163 'all_items' => esc_html__( 'All %s', 'custom-post-type-ui' ),
1164 // translators: placeholder holds content label.
1165 'separate_items_with_commas' => esc_html__( 'Separate %s with commas', 'custom-post-type-ui' ),
1166 // translators: placeholder holds content label.
1167 'add_or_remove_items' => esc_html__( 'Add or remove %s', 'custom-post-type-ui' ),
1168 // translators: placeholder holds content label.
1169 'choose_from_most_used' => esc_html__( 'Choose from the most used %s', 'custom-post-type-ui' ),
1170 ],
1171 ],
1172 ];
1173 }
1174
1175
1176 /**
1177 * Add "Back to top" button on admin pages.
1178 *
1179 * @since 1.14.0
1180 */
1181 function cptui_inside_wrap_callback() {
1182 ?>
1183 <a class="button button-secondary cptui-back-to-top" href="#"><?php esc_html_e( 'Back to top', 'custom-post-type-ui' ); ?> &uarr;</a>
1184 <?php
1185 }
1186 add_action( 'cptui_inside_wrap', 'cptui_inside_wrap_callback' );
1187