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