Files

591 lines
20 KiB
PHP

<?php
/**
* Admin Handler
*
* @package PCChangelogManager
*/
// Prevent direct access to the file.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handles all admin functionality for the changelog manager.
*/
class PC_CLM_Admin {
/**
* Instance of the post type class.
*
* @var PC_CLM_Post_Type
*/
private $post_type;
/**
* Constructor.
*
* @param PC_CLM_Post_Type $post_type Post type instance.
*/
public function __construct( $post_type ) {
$this->post_type = $post_type;
$this->init_hooks();
}
/**
* Initialize admin hooks.
*/
private function init_hooks() {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'save_post_' . $this->post_type->get_post_type_slug(), array( $this, 'save_meta' ), 10, 2 );
add_filter( 'manage_' . $this->post_type->get_post_type_slug() . '_posts_columns', array( $this, 'set_custom_columns' ) );
add_action( 'manage_' . $this->post_type->get_post_type_slug() . '_posts_custom_column', array( $this, 'render_custom_columns' ), 10, 2 );
add_filter( 'post_row_actions', array( $this, 'modify_row_actions' ), 10, 2 );
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'parent_file', array( $this, 'highlight_menu' ) );
add_action( 'admin_init', array( $this, 'handle_add_new_form' ) );
}
/**
* Enqueue admin styles.
*/
public function enqueue_styles() {
$screen = get_current_screen();
if ( ! $screen ) {
return;
}
// Only enqueue on changelog post type screens.
if ( $this->post_type->get_post_type_slug() !== $screen->post_type ) {
return;
}
// Enqueue admin stylesheet.
if ( file_exists( PC_CLM_PLUGIN_DIR . 'admin/css/admin-style.css' ) ) {
wp_enqueue_style(
'pc-clm-admin-style',
PC_CLM_PLUGIN_URL . 'admin/css/admin-style.css',
array(),
PC_CLM_VERSION
);
}
}
/**
* Add admin menu items.
*/
public function add_admin_menu() {
add_menu_page(
__( 'Changelog', 'pc-changelog-manager-abc123' ),
__( 'Changelog', 'pc-changelog-manager-abc123' ),
'manage_options',
'pc-clm-view-changelog',
array( $this, 'render_view_changelog_page' ),
'dashicons-backup',
30
);
add_submenu_page(
'pc-clm-view-changelog',
__( 'Add New Changelog Entry', 'pc-changelog-manager-abc123' ),
__( 'Add New', 'pc-changelog-manager-abc123' ),
'manage_options',
'pc-clm-add-new',
array( $this, 'render_add_new_page' )
);
}
/**
* Render the main menu page (no-op).
* The top-level menu now links directly to the post list to avoid redirects
* that may run after headers/styles have already been sent.
*/
public function render_main_menu_page() {
// Intentionally left blank to avoid header() calls after output.
}
/**
* Render the view changelog page - displays changelog entries in admin.
*/
public function render_view_changelog_page() {
$post_type = new PC_CLM_Post_Type();
$query = $post_type->get_entries();
?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php esc_html_e( 'View Changelog', 'pc-changelog-manager-abc123' ); ?></h1>
<a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . $post_type->get_post_type_slug() ) ); ?>" class="page-title-action">
<?php esc_html_e( 'Add New Entry', 'pc-changelog-manager-abc123' ); ?>
</a>
<hr class="wp-header-end">
<?php if ( $query->have_posts() ) : ?>
<div class="pc-clm-admin-view-list">
<?php
while ( $query->have_posts() ) :
$query->the_post();
$post_id = get_the_ID();
$version = $post_type->get_meta( $post_id, 'version_number' );
$release_date = $post_type->get_meta( $post_id, 'release_date' );
$category = $post_type->get_meta( $post_id, 'category' );
$categories = $post_type->get_categories();
$category_name = isset( $categories[ $category ] ) ? $categories[ $category ] : $category;
?>
<div class="pc-clm-admin-entry">
<div class="pc-clm-admin-entry-header">
<?php if ( $version ) : ?>
<span class="pc-clm-admin-version"><?php echo esc_html( $version ); ?></span>
<?php endif; ?>
<?php if ( $release_date ) : ?>
<span class="pc-clm-admin-date"><?php echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $release_date ) ) ); ?></span>
<?php endif; ?>
<?php if ( $category ) : ?>
<span class="pc-clm-admin-category pc-clm-category-<?php echo esc_attr( $category ); ?>"><?php echo esc_html( $category_name ); ?></span>
<?php endif; ?>
</div>
<h3 class="pc-clm-admin-entry-title">
<a href="<?php echo esc_url( get_edit_post_link( $post_id ) ); ?>">
<?php the_title(); ?>
</a>
</h3>
<div class="pc-clm-admin-entry-content">
<?php echo wp_kses_post( wp_trim_words( get_the_content(), 30, '...' ) ); ?>
</div>
<div class="pc-clm-admin-entry-actions">
<a href="<?php echo esc_url( get_edit_post_link( $post_id ) ); ?>" class="button button-small">
<?php esc_html_e( 'Edit', 'pc-changelog-manager-abc123' ); ?>
</a>
<a href="<?php echo esc_url( get_permalink( $post_id ) ); ?>" target="_blank" class="button button-small">
<?php esc_html_e( 'View', 'pc-changelog-manager-abc123' ); ?>
</a>
</div>
</div>
<?php
endwhile;
wp_reset_postdata();
?>
</div>
<?php else : ?>
<div class="pc-clm-admin-empty">
<p>
<span class="dashicons dashicons-backup" style="font-size: 48px; width: 48px; height: 48px; display: block; margin: 0 auto 16px; opacity: 0.5;"></span>
<?php esc_html_e( 'No changelog entries found.', 'pc-changelog-manager-abc123' ); ?>
</p>
<a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . $post_type->get_post_type_slug() ) ); ?>" class="button button-primary">
<?php esc_html_e( 'Add Your First Changelog Entry', 'pc-changelog-manager-abc123' ); ?>
</a>
</div>
<?php endif; ?>
</div>
<?php
}
/**
* Handle the add new changelog form submission.
*/
public function handle_add_new_form() {
if ( ! isset( $_POST['pc_clm_add_new_nonce'] ) || ! isset( $_POST['pc_clm_submit_new'] ) ) {
return;
}
if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pc_clm_add_new_nonce'] ) ), 'pc_clm_add_new' ) ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$title = isset( $_POST['pc_clm_title'] ) ? sanitize_text_field( wp_unslash( $_POST['pc_clm_title'] ) ) : '';
$content = isset( $_POST['pc_clm_content'] ) ? wp_kses_post( wp_unslash( $_POST['pc_clm_content'] ) ) : '';
$version = isset( $_POST['pc_clm_version'] ) ? sanitize_text_field( wp_unslash( $_POST['pc_clm_version'] ) ) : '1.0.0';
$release_date = isset( $_POST['pc_clm_release_date'] ) ? sanitize_text_field( wp_unslash( $_POST['pc_clm_release_date'] ) ) : current_time( 'Y-m-d' );
$category = isset( $_POST['pc_clm_category'] ) ? sanitize_text_field( wp_unslash( $_POST['pc_clm_category'] ) ) : 'new-features';
if ( empty( $title ) ) {
return;
}
$post_id = wp_insert_post( array(
'post_title' => $title,
'post_content' => $content,
'post_status' => 'publish',
'post_type' => $this->post_type->get_post_type_slug(),
) );
if ( ! is_wp_error( $post_id ) ) {
$this->post_type->update_meta( $post_id, 'version_number', $version );
$this->post_type->update_meta( $post_id, 'release_date', $release_date );
$this->post_type->update_meta( $post_id, 'category', $category );
add_action( 'admin_notices', function() {
echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Changelog entry added successfully!', 'pc-changelog-manager-abc123' ) . '</p></div>';
} );
}
}
/**
* Render the add new changelog page.
*/
public function render_add_new_page() {
$categories = $this->post_type->get_categories();
$default_version = '1.0.0';
$default_date = current_time( 'Y-m-d' );
?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php esc_html_e( 'Add New Changelog Entry', 'pc-changelog-manager-abc123' ); ?></h1>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=pc-clm-view-changelog' ) ); ?>" class="page-title-action">
<?php esc_html_e( 'View All Entries', 'pc-changelog-manager-abc123' ); ?>
</a>
<hr class="wp-header-end">
<div class="pc-clm-admin-add-form">
<form method="post" action="">
<?php wp_nonce_field( 'pc_clm_add_new', 'pc_clm_add_new_nonce' ); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="pc_clm_title"><?php esc_html_e( 'Title', 'pc-changelog-manager-abc123' ); ?> <span class="required">*</span></label>
</th>
<td>
<input type="text" id="pc_clm_title" name="pc_clm_title" class="regular-text" required>
<p class="description"><?php esc_html_e( 'Enter a title for this changelog entry.', 'pc-changelog-manager-abc123' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="pc_clm_version"><?php esc_html_e( 'Version Number', 'pc-changelog-manager-abc123' ); ?></label>
</th>
<td>
<input type="text" id="pc_clm_version" name="pc_clm_version" value="<?php echo esc_attr( $default_version ); ?>" class="regular-text">
<p class="description"><?php esc_html_e( 'The version number (e.g., 1.0.0)', 'pc-changelog-manager-abc123' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="pc_clm_release_date"><?php esc_html_e( 'Release Date', 'pc-changelog-manager-abc123' ); ?></label>
</th>
<td>
<input type="date" id="pc_clm_release_date" name="pc_clm_release_date" value="<?php echo esc_attr( $default_date ); ?>">
<p class="description"><?php esc_html_e( 'The release date for this version.', 'pc-changelog-manager-abc123' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="pc_clm_category"><?php esc_html_e( 'Category', 'pc-changelog-manager-abc123' ); ?></label>
</th>
<td>
<select id="pc_clm_category" name="pc_clm_category">
<?php foreach ( $categories as $slug => $name ) : ?>
<option value="<?php echo esc_attr( $slug ); ?>">
<?php echo esc_html( $name ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'The category for this changelog entry.', 'pc-changelog-manager-abc123' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="pc_clm_content"><?php esc_html_e( 'Content', 'pc-changelog-manager-abc123' ); ?></label>
</th>
<td>
<?php
wp_editor( '', 'pc_clm_content', array(
'media_buttons' => true,
'textarea_rows' => 10,
'teeny' => false,
) );
?>
<p class="description"><?php esc_html_e( 'Describe the changes in this version.', 'pc-changelog-manager-abc123' ); ?></p>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="pc_clm_submit_new" class="button button-primary" value="<?php esc_attr_e( 'Add Changelog Entry', 'pc-changelog-manager-abc123' ); ?>">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=pc-clm-view-changelog' ) ); ?>" class="button">
<?php esc_html_e( 'Cancel', 'pc-changelog-manager-abc123' ); ?>
</a>
</p>
</form>
</div>
</div>
<?php
}
/**
* Highlight the correct menu item.
*
* @param string $parent_file Parent menu slug.
* @return string
*/
public function highlight_menu( $parent_file ) {
global $submenu_file, $current_screen;
if ( ! isset( $current_screen ) ) {
return $parent_file;
}
// Handle our custom view changelog page.
if ( isset( $current_screen->base ) && 'pc-clm-view-changelog' === $current_screen->base ) {
$parent_file = 'pc-clm-view-changelog';
$submenu_file = 'pc-clm-view-changelog';
}
// Handle our custom add new page.
if ( isset( $current_screen->base ) && 'pc-clm-add-new' === $current_screen->base ) {
$parent_file = 'pc-clm-view-changelog';
$submenu_file = 'pc-clm-add-new';
}
return $parent_file;
}
/**
* Add meta boxes to the changelog entry editor.
*/
public function add_meta_boxes() {
add_meta_box(
'pc_clm_entry_details',
__( 'Entry Details', 'pc-changelog-manager-abc123' ),
array( $this, 'render_entry_details_meta_box' ),
$this->post_type->get_post_type_slug(),
'normal',
'high'
);
add_meta_box(
'pc_clm_preview',
__( 'Preview', 'pc-changelog-manager-abc123' ),
array( $this, 'render_preview_meta_box' ),
$this->post_type->get_post_type_slug(),
'side',
'low'
);
}
/**
* Render the entry details meta box.
*
* @param WP_Post $post Current post object.
*/
public function render_entry_details_meta_box( $post ) {
// Add nonce field.
wp_nonce_field( 'pc_clm_save_meta', 'pc_clm_meta_nonce' );
// Get current values.
$version_number = $this->post_type->get_meta( $post->ID, 'version_number' );
$release_date = $this->post_type->get_meta( $post->ID, 'release_date' );
$category = $this->post_type->get_meta( $post->ID, 'category' );
$categories = $this->post_type->get_categories();
// Set default values for new posts.
if ( empty( $version_number ) ) {
$version_number = '1.0.0';
}
if ( empty( $release_date ) ) {
$release_date = current_time( 'Y-m-d' );
}
?>
<div class="pc-clm-meta-fields">
<div class="pc-clm-field-group">
<label for="pc_clm_version_number"><?php esc_html_e( 'Version Number', 'pc-changelog-manager-abc123' ); ?></label>
<input type="text" id="pc_clm_version_number" name="pc_clm_version_number" value="<?php echo esc_attr( $version_number ); ?>" placeholder="e.g., 1.0.0" />
<p class="description"><?php esc_html_e( 'The version number for this changelog entry (e.g., 1.0.0, 2.1.3).', 'pc-changelog-manager-abc123' ); ?></p>
</div>
<div class="pc-clm-field-group">
<label for="pc_clm_release_date"><?php esc_html_e( 'Release Date', 'pc-changelog-manager-abc123' ); ?></label>
<input type="date" id="pc_clm_release_date" name="pc_clm_release_date" value="<?php echo esc_attr( $release_date ); ?>" />
<p class="description"><?php esc_html_e( 'The release date for this version.', 'pc-changelog-manager-abc123' ); ?></p>
</div>
<div class="pc-clm-field-group">
<label for="pc_clm_category"><?php esc_html_e( 'Category', 'pc-changelog-manager-abc123' ); ?></label>
<select id="pc_clm_category" name="pc_clm_category">
<?php foreach ( $categories as $slug => $name ) : ?>
<option value="<?php echo esc_attr( $slug ); ?>" <?php selected( $category, $slug ); ?>>
<?php echo esc_html( $name ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'The category for this changelog entry.', 'pc-changelog-manager-abc123' ); ?></p>
</div>
</div>
<?php
}
/**
* Render the preview meta box.
*
* @param WP_Post $post Current post object.
*/
public function render_preview_meta_box( $post ) {
$view_page_url = pc_clm_get_changelog_page_url();
?>
<div class="pc-clm-preview-box">
<p><?php esc_html_e( 'View your changelog page to see how entries will appear to visitors.', 'pc-changelog-manager-abc123' ); ?></p>
<a href="<?php echo esc_url( $view_page_url ); ?>" target="_blank" class="button button-secondary">
<span class="dashicons dashicons-external" style="margin-top: 3px;"></span>
<?php esc_html_e( 'View Changelog Page', 'pc-changelog-manager-abc123' ); ?>
</a>
</div>
<?php
}
/**
* Save meta box data.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*/
public function save_meta( $post_id, $post ) {
// Verify nonce.
if ( ! isset( $_POST['pc_clm_meta_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pc_clm_meta_nonce'] ) ), 'pc_clm_save_meta' ) ) {
return $post_id;
}
// Check if user has permission.
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
// Check if autosave.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
// Save version number.
if ( isset( $_POST['pc_clm_version_number'] ) ) {
$this->post_type->update_meta( $post_id, 'version_number', sanitize_text_field( wp_unslash( $_POST['pc_clm_version_number'] ) ) );
}
// Save release date.
if ( isset( $_POST['pc_clm_release_date'] ) ) {
$this->post_type->update_meta( $post_id, 'release_date', sanitize_text_field( wp_unslash( $_POST['pc_clm_release_date'] ) ) );
}
// Save category.
if ( isset( $_POST['pc_clm_category'] ) ) {
$allowed_categories = array_keys( $this->post_type->get_categories() );
$category = sanitize_text_field( wp_unslash( $_POST['pc_clm_category'] ) );
if ( in_array( $category, $allowed_categories, true ) ) {
$this->post_type->update_meta( $post_id, 'category', $category );
}
}
}
/**
* Set custom columns for the admin list view.
*
* @param array $columns Existing columns.
* @return array
*/
public function set_custom_columns( $columns ) {
unset( $columns['date'] );
$new_columns = array(
'version' => __( 'Version', 'pc-changelog-manager-abc123' ),
'release_date' => __( 'Release Date', 'pc-changelog-manager-abc123' ),
'category' => __( 'Category', 'pc-changelog-manager-abc123' ),
'date' => __( 'Date', 'pc-changelog-manager-abc123' ),
);
return array_merge( $columns, $new_columns );
}
/**
* Render custom column content.
*
* @param string $column Column name.
* @param int $post_id Post ID.
*/
public function render_custom_columns( $column, $post_id ) {
switch ( $column ) {
case 'version':
$version = $this->post_type->get_meta( $post_id, 'version_number' );
echo esc_html( $version );
break;
case 'release_date':
$release_date = $this->post_type->get_meta( $post_id, 'release_date' );
if ( $release_date ) {
echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $release_date ) ) );
} else {
echo '&mdash;';
}
break;
case 'category':
$category = $this->post_type->get_meta( $post_id, 'category' );
$categories = $this->post_type->get_categories();
$category_name = isset( $categories[ $category ] ) ? $categories[ $category ] : $category;
$category_class = 'pc-clm-category-' . esc_attr( $category );
echo '<span class="pc-clm-category-badge ' . esc_attr( $category_class ) . '">';
echo esc_html( $category_name );
echo '</span>';
break;
}
}
/**
* Modify row actions in the admin list.
*
* @param array $actions Existing actions.
* @param WP_Post $post Post object.
* @return array
*/
public function modify_row_actions( $actions, $post ) {
if ( $this->post_type->get_post_type_slug() !== $post->post_type ) {
return $actions;
}
// Remove quick edit as it doesn't work well with custom meta.
if ( isset( $actions['inline hide-if-no-js'] ) ) {
unset( $actions['inline hide-if-no-js'] );
}
return $actions;
}
}
/**
* Get the changelog page URL.
*
* @return string
*/
function pc_clm_get_changelog_page_url() {
// First try to get the custom changelog page.
$changelog_page = get_page_by_path( 'changelog' );
if ( $changelog_page && 'publish' === $changelog_page->post_status ) {
return get_permalink( $changelog_page );
}
// Fall back to the post type archive URL.
$post_type = get_post_type_object( 'pc_changelog' );
if ( $post_type && $post_type->has_archive ) {
return get_post_type_archive_link( 'pc_changelog' );
}
// Final fallback to home URL with changelog slug.
return home_url( '/changelog/' );
}