| 1 |
<?php |
| 2 |
namespace BTNB\Admin; |
| 3 |
|
| 4 |
if ( !defined( 'ABSPATH' ) ) { exit; } |
| 5 |
|
| 6 |
/** |
| 7 |
* CPT class |
| 8 |
* Handles the 'button-block' custom post type registration, shortcode, and admin features. |
| 9 |
* |
| 10 |
* @package BTN\Admin |
| 11 |
*/ |
| 12 |
class CPT{ |
| 13 |
public $post_type = 'button-block'; |
| 14 |
|
| 15 |
/** |
| 16 |
* Constructor. |
| 17 |
* Registers hooks for CPT, shortcode, admin columns, and row actions. |
| 18 |
*/ |
| 19 |
public function __construct(){ |
| 20 |
add_action( 'admin_enqueue_scripts', [$this, 'adminEnqueueScripts'] ); |
| 21 |
add_action( 'init', [$this, 'onInit'] ); |
| 22 |
add_shortcode( 'btn_block', [$this, 'onAddShortcode'] ); |
| 23 |
add_filter( 'manage_button-block_posts_columns', [$this, 'manageBTNPostsColumns'], 10 ); |
| 24 |
add_action( 'manage_button-block_posts_custom_column', [$this, 'manageBTNPostsCustomColumns'], 10, 2 ); |
| 25 |
add_action( 'use_block_editor_for_post', [$this, 'useBlockEditorForPost'], 999, 2 ); |
| 26 |
add_action( 'post_row_actions', [$this, 'postRowActions'], 10, 2 ); |
| 27 |
add_action( 'admin_action_btnDuplicatePost', [$this, 'duplicatePost'] ); |
| 28 |
} |
| 29 |
|
| 30 |
/** |
| 31 |
* Enqueues admin styles and scripts for the post list and edit screens. |
| 32 |
* |
| 33 |
* @param string $hook The current admin page hook suffix. |
| 34 |
* @return void |
| 35 |
*/ |
| 36 |
function adminEnqueueScripts( $hook ){ |
| 37 |
if( 'edit.php' === $hook || 'post.php' === $hook ){ |
| 38 |
wp_enqueue_style( 'btn-admin-post', BTNB_DIR_URL . 'build/admin/post.css', [], BTNB_VERSION ); |
| 39 |
wp_enqueue_script( 'btn-admin-post', BTNB_DIR_URL . 'build/admin/post.js', [], BTNB_VERSION, true ); |
| 40 |
wp_set_script_translations( 'btn-admin-post', 'button-block', BTNB_DIR_PATH . 'languages' ); |
| 41 |
} |
| 42 |
} |
| 43 |
|
| 44 |
/** |
| 45 |
* Registers the 'button-block' custom post type on init. |
| 46 |
* |
| 47 |
* @return void |
| 48 |
*/ |
| 49 |
function onInit(){ |
| 50 |
$menuIcon = "<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 48 48' fill='#fff'><path d='M7 34Q5.75 34 4.875 33.125Q4 32.25 4 31V17Q4 15.75 4.875 14.875Q5.75 14 7 14H41Q42.25 14 43.125 14.875Q44 15.75 44 17V31Q44 32.25 43.125 33.125Q42.25 34 41 34H37.8V31H41Q41 31 41 31Q41 31 41 31V17Q41 17 41 17Q41 17 41 17H7Q7 17 7 17Q7 17 7 17V31Q7 31 7 31Q7 31 7 31H20.2V34ZM29 38 27.2 34 23.2 32.2 27.2 30.4 29 26.4 30.8 30.4 34.8 32.2 30.8 34ZM34 27.4 32.95 25.05 30.6 24 32.95 22.95 34 20.6 35.05 22.95 37.4 24 35.05 25.05Z'></path></svg>"; |
| 51 |
|
| 52 |
register_post_type( $this->post_type, [ |
| 53 |
'labels' => [ |
| 54 |
'name' => __( 'Button Block', 'button-block' ), |
| 55 |
'singular_name' => __( 'Button Block', 'button-block' ), |
| 56 |
'add_new' => __( 'Add New', 'button-block' ), |
| 57 |
'add_new_item' => __( 'Add New Button', 'button-block' ), |
| 58 |
'edit_item' => __( 'Edit Button', 'button-block' ), |
| 59 |
'new_item' => __( 'New Button', 'button-block' ), |
| 60 |
'view_item' => __( 'View Button', 'button-block' ), |
| 61 |
'search_items' => __( 'Search Button', 'button-block' ), |
| 62 |
'not_found' => __( 'Sorry, we couldn\'t find any item you are looking for.', 'button-block' ) |
| 63 |
], |
| 64 |
'public' => false, |
| 65 |
'show_ui' => 'true' !== get_option( 'button_block_option', '' ), |
| 66 |
'publicly_queryable' => false, |
| 67 |
'exclude_from_search' => false, |
| 68 |
'show_in_rest' => true, |
| 69 |
'menu_position' => 14, |
| 70 |
'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode($menuIcon), |
| 71 |
'has_archive' => false, |
| 72 |
'hierarchical' => false, |
| 73 |
'capability_type' => 'page', |
| 74 |
'rewrite' => [ 'slug' => 'button-block' ], |
| 75 |
'supports' => [ 'title', 'editor' ], |
| 76 |
'template' => [ ['btn/button'] ], |
| 77 |
'template_lock' => 'all' |
| 78 |
]); // Register Post Type |
| 79 |
} |
| 80 |
|
| 81 |
/** |
| 82 |
* Handles the [btn_block] shortcode output. |
| 83 |
* |
| 84 |
* Renders the button block content based on post status and user capabilities. |
| 85 |
* |
| 86 |
* @param array $atts Shortcode attributes. Accepts 'id' (int) for the post ID. |
| 87 |
* @return string The rendered block HTML or empty string. |
| 88 |
*/ |
| 89 |
function onAddShortcode( $atts ) { |
| 90 |
$atts = shortcode_atts( [ 'id' => 0 ], $atts, 'btn_block' ); |
| 91 |
$post_id = absint( $atts['id'] ); |
| 92 |
if ( ! $post_id ) { |
| 93 |
return ''; |
| 94 |
} |
| 95 |
$post = get_post( $post_id ); |
| 96 |
|
| 97 |
if ( !$post ) { |
| 98 |
return ''; |
| 99 |
} |
| 100 |
|
| 101 |
if ( post_password_required( $post ) ) { |
| 102 |
return get_the_password_form( $post ); |
| 103 |
} |
| 104 |
|
| 105 |
switch ( $post->post_status ) { |
| 106 |
case 'publish': |
| 107 |
return $this->displayContent( $post ); |
| 108 |
|
| 109 |
case 'private': |
| 110 |
if (current_user_can('read_private_posts')) { |
| 111 |
return $this->displayContent( $post ); |
| 112 |
} |
| 113 |
return ''; |
| 114 |
|
| 115 |
case 'draft': |
| 116 |
case 'pending': |
| 117 |
case 'future': |
| 118 |
if ( current_user_can( 'edit_post', $post_id ) ) { |
| 119 |
return $this->displayContent( $post ); |
| 120 |
} |
| 121 |
return ''; |
| 122 |
|
| 123 |
default: |
| 124 |
return ''; |
| 125 |
} |
| 126 |
} |
| 127 |
|
| 128 |
/** |
| 129 |
* Renders the first block from a post's content through wp_kses. |
| 130 |
* |
| 131 |
* @param \WP_Post $post The post object to render. |
| 132 |
* @return string The sanitized rendered block HTML. |
| 133 |
*/ |
| 134 |
function displayContent( $post ){ |
| 135 |
$blocks = parse_blocks( $post->post_content ); |
| 136 |
|
| 137 |
global $allowedposttags; |
| 138 |
$allowed_html = wp_parse_args( ['style' => [] ], $allowedposttags ); |
| 139 |
|
| 140 |
return wp_kses( render_block( $blocks[0] ), $allowed_html ); |
| 141 |
} |
| 142 |
|
| 143 |
/** |
| 144 |
* Adds the 'ShortCode' column to the button-block post list table. |
| 145 |
* |
| 146 |
* @param array $defaults The existing column headers. |
| 147 |
* @return array Modified column headers. |
| 148 |
*/ |
| 149 |
function manageBTNPostsColumns( $defaults ) { |
| 150 |
unset( $defaults['date'] ); |
| 151 |
$defaults['shortcode'] = __( 'ShortCode', 'button-block' ); |
| 152 |
$defaults['date'] = __( 'Date', 'button-block' ); |
| 153 |
return $defaults; |
| 154 |
} |
| 155 |
|
| 156 |
/** |
| 157 |
* Renders the content for the custom 'ShortCode' column. |
| 158 |
* |
| 159 |
* @param string $column_name The column slug. |
| 160 |
* @param int $post_ID The current post ID. |
| 161 |
* @return void |
| 162 |
*/ |
| 163 |
function manageBTNPostsCustomColumns( $column_name, $post_ID ) { |
| 164 |
if ( 'shortcode' === $column_name ) { |
| 165 |
echo '<div class="bPlAdminShortcode" id="bPlAdminShortcode-' . esc_attr( $post_ID ) . '"> |
| 166 |
<input value="[btn_block id=' . esc_attr( $post_ID ) . ']" onclick="copyBPlAdminShortcode(\'' . esc_attr( $post_ID ) . '\')"> |
| 167 |
<span class="tooltip">' . esc_html__( 'Copy To Clipboard', 'button-block' ) . '</span> |
| 168 |
</div>'; |
| 169 |
} |
| 170 |
} |
| 171 |
|
| 172 |
/** |
| 173 |
* Forces the block editor for button-block post type. |
| 174 |
* |
| 175 |
* @param bool $use Whether to use the block editor. |
| 176 |
* @param \WP_Post $post The post object. |
| 177 |
* @return bool True for button-block posts, original value otherwise. |
| 178 |
*/ |
| 179 |
function useBlockEditorForPost($use, $post){ |
| 180 |
if ( $this->post_type === $post->post_type ) { |
| 181 |
return true; |
| 182 |
} |
| 183 |
return $use; |
| 184 |
} |
| 185 |
|
| 186 |
/** |
| 187 |
* Adds a 'Duplicate' action link to the post row actions for button-block posts. |
| 188 |
* |
| 189 |
* @param array $actions The existing row actions. |
| 190 |
* @param \WP_Post $post The current post object. |
| 191 |
* @return array Modified row actions. |
| 192 |
*/ |
| 193 |
function postRowActions( $actions, $post ){ |
| 194 |
if ( $this->post_type === $post->post_type ) { |
| 195 |
$actions['duplicate'] = '<a href="' . admin_url( "admin.php?action=btnDuplicatePost&_wpnonce=" . wp_create_nonce( 'btnDuplicatePost' ) . "&post={$post->ID}" ) . '">' . esc_html__( 'Duplicate', 'button-block' ) . '</a>'; |
| 196 |
} |
| 197 |
|
| 198 |
return $actions; |
| 199 |
} // Duplicate Button Post Action |
| 200 |
|
| 201 |
/** |
| 202 |
* Handles the post duplication admin action. |
| 203 |
* |
| 204 |
* Verifies nonce and capabilities, then creates a copy of the post and redirects to the editor. |
| 205 |
* |
| 206 |
* @return void Redirects on success, dies on failure. |
| 207 |
*/ |
| 208 |
function duplicatePost(){ |
| 209 |
if ( |
| 210 |
!wp_verify_nonce( |
| 211 |
sanitize_text_field( wp_unslash( isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : null ) ), |
| 212 |
'btnDuplicatePost' |
| 213 |
) |
| 214 |
) { |
| 215 |
wp_send_json_error( 'Invalid Request' ); |
| 216 |
} |
| 217 |
|
| 218 |
if ( !isset( $_GET['post'] ) || !current_user_can( 'edit_posts') ) { |
| 219 |
wp_send_json_error( 'Permission Denied' ); |
| 220 |
} |
| 221 |
|
| 222 |
$postId = absint( wp_unslash( $_GET['post'] ) ); |
| 223 |
$post = get_post( $postId ); |
| 224 |
|
| 225 |
if ( !$post ) { |
| 226 |
wp_die( esc_html__( 'Invalid post ID', 'button-block' ) ); |
| 227 |
} |
| 228 |
|
| 229 |
if ( $post && !current_user_can( 'edit_post', $postId ) ) { |
| 230 |
wp_die( esc_html__( 'You do not have permission to duplicate this post.', 'button-block' ) ); |
| 231 |
} |
| 232 |
|
| 233 |
$newPost = [ |
| 234 |
'post_title' => $post->post_title . '(copy)', |
| 235 |
'post_content' => $post->post_content, |
| 236 |
'post_status' => $post->post_status, |
| 237 |
'post_type' => $post->post_type, |
| 238 |
]; |
| 239 |
|
| 240 |
$newPostId = wp_insert_post( $newPost ); |
| 241 |
wp_safe_redirect( admin_url( "post.php?action=edit&post={$newPostId}" ) ); |
| 242 |
exit; |
| 243 |
} // Duplicate Button Post |
| 244 |
} |
| 245 |
new CPT(); |