run(); } } // Hook into WordPress init add_action('plugins_loaded', 'wp_notes_init', 10); // Register activation and deactivation hooks register_activation_hook(__FILE__, 'wp_notes_activate'); register_deactivation_hook(__FILE__, 'wp_notes_deactivate'); /** * Plugin activation handler */ function wp_notes_activate() { // Ensure PHP version is compatible if (version_compare(PHP_VERSION, '7.2', '<')) { deactivate_plugins(basename(__FILE__)); wp_die('This plugin requires PHP version 7.2 or higher.'); } // Initialize plugin settings add_option('wp_notes_version', WP_NOTES_VERSION); // Create database tables wp_notes_create_tables(); // Backup existing notes $existing_notes = get_option('wp_notes', array()); $existing_done_notes = get_option('wp_done_notes', array()); if (!empty($existing_notes)) { update_option('wp_notes_backup', $existing_notes); } if (!empty($existing_done_notes)) { update_option('wp_done_notes_backup', $existing_done_notes); } } /** * Plugin deactivation handler */ function wp_notes_deactivate() { delete_option('wp_notes_version'); } /** * Create required database tables */ function wp_notes_create_tables() { global $wpdb; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); $charset_collate = $wpdb->get_charset_collate(); $sql = array(); // Notes table $sql[] = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wp_notes ( id bigint(20) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, note_content text NOT NULL, note_status varchar(20) NOT NULL DEFAULT 'active', created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY user_id (user_id), KEY note_status (note_status) ) $charset_collate;"; foreach ($sql as $query) { dbDelta($query); } } // Initialize plugin add_action('plugins_loaded', 'wp_notes_init'); // Set text domain for translations function wp_notes_load_textdomain() { load_plugin_textdomain('a-wp-notes', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'wp_notes_load_textdomain'); // Include required files if (defined('WP_NOTES_PATH')) { require_once WP_NOTES_PATH . 'inc/admin-bar.php'; require_once WP_NOTES_PATH . 'inc/wp-notes-about.php'; require_once WP_NOTES_PATH . 'inc/wp-notes-feedback.php'; require_once WP_NOTES_PATH . 'inc/wp-notes-display.php'; require_once WP_NOTES_PATH . 'inc/wp-notes-styles.php'; } // Admin Menu function wp_notes_admin_menu() { add_menu_page( 'WP Logbook', 'WP Logbook', 'manage_options', 'wp-notes', 'wp_notes_page_callback', 'dashicons-admin-generic', 3 ); // "My Log" — the main landing submenu. Same slug as the parent // menu so clicking either WP Logbook or My Log lands on the same // central dashboard (the parent's wp_notes_page_callback already // renders form + active list + completed list). // // CRITICAL: callback must be empty here. WordPress registers BOTH // the parent's and the submenu's callbacks against the same page // hook when slugs match, and runs them in order — passing // wp_notes_create_page as the callback caused a duplicate // "Create a New WP Note" form to render BELOW the main page // content. Empty callback means only the parent's renderer fires. add_submenu_page( 'wp-notes', // Parent slug 'My Log', // Page title (browser tab) 'My Log', // Menu label (sidebar) 'manage_options', // Capability 'wp-notes', // Menu slug (matches parent → same page) '' // Empty callback — see comment above ); // Settings submenu add_submenu_page( 'wp-notes', 'Settings', 'Settings', 'manage_options', 'wp-notes-settings', 'wp_notes_settings_page' ); // Import/Export submenu add_submenu_page( 'wp-notes', 'Import/Export', 'Import/Export', 'manage_options', 'wp-notes-import-export', 'wp_notes_import_export_page' ); // About submenu — the WP Logbook brand is already carried by the // parent menu, so the submenu can be plain-spoken. add_submenu_page( 'wp-notes', 'About', // Page title (browser tab) 'About', // Menu label (sidebar) 'manage_options', 'wp-notes-about', 'wp_notes_about_page' // Renderer in inc/wp-notes-about.php ); // Register settings with validation callback register_setting( 'wp_notes_settings', 'wp_notes_settings', array( 'sanitize_callback' => 'wp_notes_validate_settings', 'default' => array( 'default_font' => 'Arial', 'default_size' => '16' ) ) ); add_settings_section( 'wp_notes_main_section', 'General Settings', 'wp_notes_section_callback', 'wp-notes-settings' ); add_settings_field( 'wp_notes_default_font', 'Default Font', 'wp_notes_font_callback', 'wp-notes-settings', 'wp_notes_main_section' ); add_settings_field( 'wp_notes_default_size', 'Default Size', 'wp_notes_size_callback', 'wp-notes-settings', 'wp_notes_main_section' ); } // Settings validation callback function wp_notes_validate_settings($input) { $output = array(); // Validate font $allowed_fonts = ['Arial', 'Helvetica', 'Times New Roman', 'Verdana']; $output['default_font'] = in_array($input['default_font'], $allowed_fonts) ? $input['default_font'] : 'Arial'; // Validate size (8-72px) $size = absint($input['default_size']); $output['default_size'] = ($size >= 8 && $size <= 72) ? $size : 16; add_settings_error( 'wp_notes_settings', 'settings_updated', 'Settings saved successfully.', 'updated' ); return $output; } // Settings page callback function wp_notes_settings_page() { // Show any settings messages settings_errors('wp_notes_settings'); ?>

WP Logbook Settings

Configure default settings for WP Logbook.

'; } // Font setting callback function wp_notes_font_callback() { $options = get_option('wp_notes_settings'); $current = $options['default_font'] ?? 'Arial'; ?> Font size in pixels (8-72) getMessage()), 'error' ); } } // Get current settings for display $options = get_option('wp_notes_settings'); ?>

Import/Export Notes

Export Notes

Download your notes as JSON or CSV file.

Import Notes

Import notes from JSON or CSV file.

$notes, 'completed_notes' => $done_notes, 'export_date' => current_time('mysql') ); if ($format === 'json') { header('Content-Type: application/json'); header('Content-Disposition: attachment; filename=wp-notes-export-' . date('Y-m-d') . '.json'); echo wp_json_encode($data, JSON_PRETTY_PRINT); } else { header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename=wp-notes-export-' . date('Y-m-d') . '.csv'); $output = fopen('php://output', 'w'); fputcsv($output, array('Type', 'Text', 'Created By', 'Created On', 'Status')); foreach ($notes as $note) { fputcsv($output, array( 'active', $note['text'], $note['author_name'] ?? 'Unknown', $note['timestamp'] ?? '', 'Active' )); } foreach ($done_notes as $note) { fputcsv($output, array( 'completed', $note['text'], $note['author_name'] ?? 'Unknown', $note['timestamp'] ?? '', 'Completed' )); } fclose($output); } exit; } // Import functionality function wp_notes_import_data() { if (!isset($_POST['import_nonce']) || !wp_verify_nonce($_POST['import_nonce'], 'wp_notes_import')) { wp_die('Security check failed'); } $file = $_FILES['import_file']; $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if ($ext === 'json') { $content = file_get_contents($file['tmp_name']); $data = json_decode($content, true); if (json_last_error() === JSON_ERROR_NONE) { if (isset($data['active_notes'])) { update_option('wp_notes', $data['active_notes']); } if (isset($data['completed_notes'])) { update_option('wp_done_notes', $data['completed_notes']); } add_settings_error('wp_notes_import', 'import_success', 'Notes imported successfully', 'updated'); } else { add_settings_error('wp_notes_import', 'import_error', 'Invalid JSON file'); } } elseif ($ext === 'csv') { $handle = fopen($file['tmp_name'], 'r'); $headers = fgetcsv($handle); $notes = array(); $done_notes = array(); while (($data = fgetcsv($handle)) !== false) { $note = array( 'text' => $data[1], 'author_name' => $data[2], 'timestamp' => $data[3], 'color' => '#000000', 'size' => '16', 'font' => 'Arial' ); if ($data[0] === 'active') { $notes[] = $note; } else { $done_notes[] = $note; } } fclose($handle); update_option('wp_notes', $notes); update_option('wp_done_notes', $done_notes); add_settings_error('wp_notes_import', 'import_success', 'Notes imported successfully', 'updated'); } else { add_settings_error('wp_notes_import', 'import_error', 'Invalid file format. Please use JSON or CSV files.'); } } add_action('admin_menu', 'wp_notes_admin_menu'); // The Tools → My Notes shortcut used to live here and routed to a // separate bare-bones form at ?page=wp-notes-create. It was removed // as a duplicate — the My Notes page (and the admin-bar "New Note" // shortcut that jumps to its #new-note anchor) covers the same need // with the proper UI. Any stale bookmark to the old URL is caught // by wp_notes_redirect_legacy_create_page() below. // Redirect anyone hitting the legacy ?page=wp-notes-create URL to // the My Notes page, so existing bookmarks don't 404 / show "you do // not have sufficient permissions" after the Tools shortcut removal. add_action('admin_init', 'wp_notes_redirect_legacy_create_page'); function wp_notes_redirect_legacy_create_page() { if (!is_admin()) { return; } if (!isset($_GET['page'])) { return; } if (sanitize_key(wp_unslash($_GET['page'])) !== 'wp-notes-create') { return; } wp_safe_redirect(admin_url('admin.php?page=wp-notes')); exit; } // Enqueue Scripts function wp_notes_enqueue_scripts() { // wp_enqueue_script('wp-notes-activity-tracker', WP_NOTES_URL . 'js/wp-notes-activity.js', ['jquery'], null, true); // Activity tracking removed 2025-05-09 wp_enqueue_script('wp-notes-feedback', WP_NOTES_URL . 'js/wp-notes-feedback.js', ['jquery'], null, true); wp_localize_script('wp-notes-feedback', 'wp_notes_feedback_vars', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wp_notes_feedback_nonce') )); } add_action('admin_enqueue_scripts', 'wp_notes_enqueue_scripts'); // Emoji picker initialization add_action('admin_footer', 'wp_notes_emoji_picker_init'); function wp_notes_emoji_picker_init() { ?> jQuery(document).ready(function($) { // Show edit form $('.edit-note').on('click', function(e) { e.preventDefault(); var noteId = $(this).data('note-id'); $('#edit-note-' + noteId).toggle(); }); // AJAX submit the edit form $('.edit-note-form').on('submit', function(e) { e.preventDefault(); var noteId = $(this).data('note-id'); var formData = $(this).serialize() + '&action=wp_notes_save_edit¬e_id=' + noteId; $.post(ajaxurl, formData, function(response) { if (response.success) { alert('Note updated successfully!'); location.reload(); // Reload to display updated notes } else { alert('Error updating note: ' + response.data); } }); }); // Persist the dismissal of the empty-state notice. // WP core's common.js attaches the X button and hides the // notice on click; we just hook the same click and fire an // AJAX call to set the user_meta flag so the notice does // not reappear on the next page load. $(document).on('click', '.wp-notes-empty .notice-dismiss', function() { var \$notice = $(this).closest('.wp-notes-empty'); var type = \$notice.data('wp-notes-empty-type'); var nonce = \$notice.data('wp-notes-nonce'); if (!type || !nonce) { return; } $.post(ajaxurl, { action: 'wp_notes_dismiss_empty', type: type, nonce: nonce }); }); }); "; } // Receive the "Leave Feedback" form from the About page and forward // it to the site admin via wp_mail(). Topics is an array of short // keys (improve / help / bug / feature / use-case / thanks / other); // message is an optional free-text textarea. Sender info is taken // from wp_get_current_user() so we never trust client-supplied // identity. Capability-gated to manage_options because the About // page is admin-only. add_action('wp_ajax_wp_notes_submit_feedback', 'wp_notes_ajax_submit_feedback'); function wp_notes_ajax_submit_feedback() { if (!current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions.', 403); } check_ajax_referer('wp_notes_feedback_submit', 'nonce'); $allowed_topics = ['improve', 'help', 'bug', 'feature', 'use-case', 'thanks', 'other']; $raw_topics = isset($_POST['topics']) && is_array($_POST['topics']) ? wp_unslash($_POST['topics']) : []; $topics = array_values(array_intersect($allowed_topics, array_map('sanitize_key', $raw_topics))); $message = isset($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : ''; if (empty($topics) && $message === '') { wp_send_json_error('Pick at least one topic or write a message.', 400); } $user = wp_get_current_user(); $site = get_bloginfo('name'); $to = get_option('admin_email'); $topic_labels = [ 'improve' => 'Ideas to improve the plugin', 'help' => 'Needs help with the plugin', 'bug' => 'Reporting a bug', 'feature' => 'New feature request', 'use-case' => 'Sharing a use case', 'thanks' => 'Saying thanks', 'other' => 'Other', ]; $topics_pretty = array_map(function ($t) use ($topic_labels) { return $topic_labels[$t] ?? $t; }, $topics); $subject = sprintf('[%s] WP Logbook feedback from %s', $site, $user->display_name ?: $user->user_login); $body = "Feedback received via WP Logbook → About page\n"; $body .= str_repeat('-', 48) . "\n\n"; $body .= 'From: ' . ($user->display_name ?: $user->user_login) . ' <' . $user->user_email . ">\n"; $body .= 'Site: ' . home_url() . "\n"; $body .= 'Plugin: v' . WP_NOTES_VERSION . "\n\n"; $body .= "Topics:\n"; if (!empty($topics_pretty)) { foreach ($topics_pretty as $label) { $body .= ' - ' . $label . "\n"; } } else { $body .= " (none selected)\n"; } $body .= "\nMessage:\n"; $body .= $message !== '' ? $message . "\n" : "(no message provided)\n"; $headers = [ 'Content-Type: text/plain; charset=UTF-8', 'Reply-To: ' . sanitize_email($user->user_email), ]; $sent = wp_mail($to, $subject, $body, $headers); if (!$sent) { wp_send_json_error('Email could not be sent. Check the site mail configuration.', 500); } wp_send_json_success(['delivered_to' => $to]); } // Persist a user's dismissal of the "No active/completed notes found" // empty-state notice so it doesn't reappear on the next page load. // Triggered by inline JS in wp_notes_add_inline_scripts() when the // user clicks the X on a .wp-notes-empty notice. add_action('wp_ajax_wp_notes_dismiss_empty', 'wp_notes_ajax_dismiss_empty'); function wp_notes_ajax_dismiss_empty() { if (!current_user_can('edit_posts')) { wp_send_json_error('Insufficient permissions.', 403); } check_ajax_referer('wp_notes_dismiss_empty', 'nonce'); $type = isset($_POST['type']) ? sanitize_key(wp_unslash($_POST['type'])) : ''; if (!in_array($type, ['active', 'completed'], true)) { wp_send_json_error('Invalid list type.', 400); } update_user_meta(get_current_user_id(), 'wp_notes_dismissed_empty_' . $type, 1); wp_send_json_success(['dismissed' => $type]); } // Handle saving edited notes add_action('wp_ajax_wp_notes_save_edit', 'wp_notes_save_edit'); function wp_notes_save_edit() { // Security checks if (!current_user_can('edit_posts')) { wp_send_json_error('Insufficient permissions.'); return; } // Security check using nonce check_ajax_referer('wp_notes_nonce', '_wpnonce'); // Validate required fields $required_fields = ['note_id', 'new_text', 'edit_color', 'edit_size', 'edit_font']; foreach ($required_fields as $field) { if (!isset($_POST[$field])) { wp_send_json_error("Missing required field: $field"); return; } } // Get and sanitize data $note_id = absint($_POST['note_id']); $new_text = sanitize_text_field($_POST['new_text']); $new_color = sanitize_hex_color($_POST['edit_color']); $new_size = absint($_POST['edit_size']); // Validate font against allowed list $allowed_fonts = ['Arial', 'Helvetica', 'Times New Roman', 'Verdana']; $new_font = in_array($_POST['edit_font'], $allowed_fonts) ? $_POST['edit_font'] : 'Arial'; // Validate size range if ($new_size < 8 || $new_size > 72) { wp_send_json_error('Font size must be between 8 and 72 pixels.'); return; } $notes = get_option('wp_notes', array()); if (!isset($notes[$note_id])) { wp_send_json_error('Note not found.'); return; } // Update note with validation $notes[$note_id] = array_merge($notes[$note_id], [ 'text' => $new_text, 'color' => $new_color ?: '#000000', 'size' => $new_size, 'font' => $new_font, 'modified_timestamp' => current_time('mysql'), 'modified_by' => get_current_user_id() ]); if (!update_option('wp_notes', $notes)) { wp_send_json_error('Failed to update note.'); return; } wp_send_json_success([ 'message' => 'Note updated successfully', 'note' => $notes[$note_id] ]); } /** * Main WP Logbook page — central hub for note management */ function wp_notes_page_callback() { if (!current_user_can('edit_posts')) { wp_die(__('You do not have sufficient permissions to access this page.', 'a-wp-notes')); } $notes = get_option('wp_notes', array()); $done_notes = get_option('wp_done_notes', array()); $total_notes = count($notes); $total_done = count($done_notes); // Get user settings $settings = get_option('wp_notes_settings', array( 'default_font' => 'Arial', 'default_size' => '16' )); ?>

WP Logbook

v

Log your daily tasks and keep a tidy work-log inside WordPress. Read more on the About page →

Add New Note

Note Formatting Options

Notes Todo List:

' . esc_html__('No notes found.', 'a-wp-notes') . '

'; return; } /* Dashboard widget table. No checkbox column — bulk actions are not wired up; re-add when there is something to act on. */ echo ''; foreach ($notes as $key => $note) { $text = esc_html($note['text']); $color = esc_attr($note['color'] ?? '#000000'); $size = esc_attr($note['size'] ?? '14'); $font = esc_attr($note['font'] ?? 'Arial'); $timestamp = esc_html($note['timestamp'] ?? current_time('mysql')); $author = esc_html($note['author_name'] ?? 'Unknown'); echo ""; } echo '
Note Created By Created On Actions
$text $author $timestamp
'; // JavaScript for form toggling and AJAX submissions ?> ' . esc_html__('No completed notes.', 'a-wp-notes') . '

'; return; } echo '
'; foreach ($done_notes as $key => $note) { echo ''; } echo '
Note Created By Created On Completed By Completed On Last Modified Actions
' . esc_html($note['text']) . ' ' . esc_html($note['author_name'] ?? 'Unknown') . ' ' . esc_html($note['timestamp'] ?? 'Unknown') . ' ' . esc_html($note['completed_by'] ?? 'Unknown') . ' ' . esc_html($note['completed_on'] ?? 'Unknown') . ' ' . esc_html($note['last_modified'] ?? 'N/A') . '
'; // Add JavaScript for select all functionality ?> sanitize_text_field($_POST['wp_notes_text']), 'color' => sanitize_hex_color($_POST['wp_notes_color']) ?: '#000000', 'size' => absint($_POST['wp_notes_size']) ?: 16, 'font' => sanitize_text_field($_POST['wp_notes_font']) ?: 'Arial', 'timestamp' => current_time('mysql'), 'author' => $current_user->ID, 'author_name' => $current_user->display_name ); $notes[] = $new_note; update_option('wp_notes', $notes); wp_redirect(admin_url('admin.php?page=wp-notes')); exit; } // Handle "Mark as Done" submissions if (isset($_POST['mark_done']) && isset($_POST['note_id'])) { $notes = get_option('wp_notes', array()); $done_notes = get_option('wp_done_notes', array()); $note_id = absint($_POST['note_id']); if (isset($notes[$note_id])) { $note = $notes[$note_id]; $note['completed_by'] = $current_user->display_name; $note['completed_on'] = current_time('mysql'); $done_notes[] = $note; unset($notes[$note_id]); update_option('wp_notes', $notes); update_option('wp_done_notes', $done_notes); wp_redirect(admin_url('admin.php?page=wp-notes')); exit; } } // Handle marking notes as done if 'mark_done' action is triggered if (isset($_POST['mark_done']) && isset($_POST['note_ids'])) { $notes = get_option('wp_notes', array()); $done_notes = get_option('wp_done_notes', array()); // Move selected notes to done list foreach ($notes as $key => $note) { if (in_array($key, $_POST['note_ids'])) { $note['completed_by'] = $current_user->display_name; $note['completed_on'] = current_time('mysql'); $done_notes[] = $note; unset($notes[$key]); // Remove from active notes } } // Update both lists in the database update_option('wp_notes', $notes); update_option('wp_done_notes', $done_notes); // Redirect to avoid form resubmission wp_redirect(admin_url('admin.php?page=wp-notes')); exit; } // Handle restore note action if (isset($_POST['restore_note']) && isset($_POST['done_ids'])) { $notes = get_option('wp_notes', array()); $done_notes = get_option('wp_done_notes', array()); $current_user = wp_get_current_user(); $new_done_notes = array(); foreach ($done_notes as $key => $note) { if (in_array($key, $_POST['done_ids'])) { $note['last_modified'] = current_time('mysql'); $note['restored_by'] = $current_user->display_name; $notes[] = $note; } else { $new_done_notes[] = $note; } } update_option('wp_notes', $notes); update_option('wp_done_notes', $new_done_notes); } // Handle edit note action if (isset($_POST['edit_note']) && isset($_POST['note_ids'])) { // Add edit functionality here $notes = get_option('wp_notes', array()); $current_user = wp_get_current_user(); foreach ($_POST['note_ids'] as $key) { if (isset($notes[$key])) { $notes[$key]['last_modified'] = current_time('mysql'); $notes[$key]['modified_by'] = $current_user->display_name; } } update_option('wp_notes', $notes); } } // AJAX handler for editing a note add_action('wp_ajax_wp_notes_edit_note', 'wp_notes_edit_note'); function wp_notes_edit_note() { if (!isset($_POST['note_id']) || !isset($_POST['edit_text'])) { wp_send_json_error('Invalid request'); return; } $note_id = absint($_POST['note_id']); $new_text = sanitize_text_field($_POST['edit_text']); $new_color = sanitize_hex_color($_POST['edit_color']); $new_size = absint($_POST['edit_size']); $new_font = sanitize_text_field($_POST['edit_font']); $notes = get_option('wp_notes', array()); if (isset($notes[$note_id])) { $notes[$note_id]['text'] = $new_text; $notes[$note_id]['color'] = $new_color; $notes[$note_id]['size'] = $new_size; $notes[$note_id]['font'] = $new_font; update_option('wp_notes', $notes); wp_send_json_success(); } else { wp_send_json_error('Note not found.'); } } // Optimize the restore function for performance function wp_notes_restore($note_id) { $done_notes = get_option('wp_done_notes', array()); $notes = get_option('wp_notes', array()); if (isset($done_notes[$note_id])) { $notes[$note_id] = $done_notes[$note_id]; unset($done_notes[$note_id]); update_option('wp_notes', $notes); update_option('wp_done_notes', $done_notes); wp_redirect(admin_url('admin.php?page=wp-notes')); } } // Dashboard widget function wp_notes_dashboard_widget() { wp_notes_list_table(); } function wp_notes_add_dashboard_widgets() { wp_add_dashboard_widget( 'wp_notes_dashboard_widget', 'WP Logbook', 'wp_notes_dashboard_widget' ); } add_action('wp_dashboard_setup', 'wp_notes_add_dashboard_widgets'); // Register Custom Post Type for notes function wp_notes_register_cpt() { $labels = array( 'name' => __('Notes', 'a-wp-notes'), 'singular_name' => __('Note', 'a-wp-notes'), 'menu_name' => __('WP Logbook', 'a-wp-notes'), 'add_new' => __('Add New', 'a-wp-notes'), 'add_new_item' => __('Add New Note', 'a-wp-notes'), 'edit_item' => __('Edit Note', 'a-wp-notes'), 'new_item' => __('New Note', 'a-wp-notes'), 'view_item' => __('View Note', 'a-wp-notes'), 'search_items' => __('Search Notes', 'a-wp-notes'), 'not_found' => __('No notes found', 'a-wp-notes'), 'not_found_in_trash'=> __('No notes found in trash', 'a-wp-notes'), ); $args = array( 'labels' => $labels, 'public' => false, // show_ui=false: the live UI stores notes in wp_options, not as // CPT posts. show_ui=true caused WordPress to auto-inject "All // Notes" and "Add New" submenus that pointed at post-new.php and // routed users into the standard post editor — which writes to // the wrong storage. The CPT remains registered so the // wp_notes_migrate_to_cpt() helper can still use wp_insert_post. 'show_ui' => false, 'show_in_menu' => false, 'capability_type' => 'post', 'hierarchical' => false, 'supports' => array('title', 'editor', 'author', 'custom-fields'), 'has_archive' => false, 'menu_position' => null, 'show_in_rest' => false, ); register_post_type('wp_note', $args); // Register taxonomy for note categories $tax_labels = array( 'name' => __('Note Categories', 'a-wp-notes'), 'singular_name' => __('Note Category', 'a-wp-notes'), 'search_items' => __('Search Categories', 'a-wp-notes'), 'all_items' => __('All Categories', 'a-wp-notes'), 'parent_item' => __('Parent Category', 'a-wp-notes'), 'parent_item_colon' => __('Parent Category:', 'a-wp-notes'), 'edit_item' => __('Edit Category', 'a-wp-notes'), 'update_item' => __('Update Category', 'a-wp-notes'), 'add_new_item' => __('Add New Category', 'a-wp-notes'), 'new_item_name' => __('New Category Name', 'a-wp-notes'), 'menu_name' => __('Categories', 'a-wp-notes'), ); register_taxonomy('wp_note_category', 'wp_note', array( 'hierarchical' => true, 'labels' => $tax_labels, // Hidden alongside the parent CPT — live UI doesn't surface // categories yet, and showing the taxonomy in admin with the // parent hidden would just dead-link. 'show_ui' => false, 'show_admin_column' => false, 'query_var' => true, 'show_in_rest' => false, )); } add_action('init', 'wp_notes_register_cpt'); // Add custom meta box for note properties function wp_notes_add_meta_boxes() { add_meta_box( 'wp_notes_properties', __('Note Properties', 'a-wp-notes'), 'wp_notes_render_properties_meta_box', 'wp_note', 'side', 'high' ); } add_action('add_meta_boxes', 'wp_notes_add_meta_boxes'); // Render meta box content function wp_notes_render_properties_meta_box($post) { // Add nonce for security wp_nonce_field('wp_notes_meta_box', 'wp_notes_meta_box_nonce'); // Get current values $color = get_post_meta($post->ID, '_wp_note_color', true) ?: '#000000'; $size = get_post_meta($post->ID, '_wp_note_size', true) ?: '16'; $font = get_post_meta($post->ID, '_wp_note_font', true) ?: 'Arial'; $status = get_post_meta($post->ID, '_wp_note_status', true) ?: 'active'; ?>





'sanitize_hex_color', '_wp_note_size' => 'absint', '_wp_note_font' => 'sanitize_text_field', '_wp_note_status' => 'sanitize_text_field' ); foreach ($fields as $key => $sanitize_callback) { if (isset($_POST[str_replace('_', '', $key)])) { $value = call_user_func($sanitize_callback, $_POST[str_replace('_', '', $key)]); update_post_meta($post_id, $key, $value); } } } add_action('save_post_wp_note', 'wp_notes_save_meta_box_data'); // Data Migration Tool function wp_notes_migrate_to_cpt() { $existing_notes = get_option('wp_notes', array()); $done_notes = get_option('wp_done_notes', array()); $migrated_count = 0; $errors = array(); // Start migration foreach ($existing_notes as $note) { $post_data = array( 'post_title' => wp_trim_words($note['text'], 10, '...'), 'post_content' => $note['text'], 'post_status' => 'publish', 'post_type' => 'wp_note', 'post_author' => $note['author'] ?? get_current_user_id(), ); // Insert the post $post_id = wp_insert_post($post_data, true); if (!is_wp_error($post_id)) { // Add note meta data update_post_meta($post_id, '_wp_note_color', $note['color'] ?? '#000000'); update_post_meta($post_id, '_wp_note_size', $note['size'] ?? '16'); update_post_meta($post_id, '_wp_note_font', $note['font'] ?? 'Arial'); update_post_meta($post_id, '_wp_note_status', 'active'); update_post_meta($post_id, '_wp_note_created', $note['timestamp'] ?? ''); update_post_meta($post_id, '_wp_note_author_name', $note['author_name'] ?? ''); $migrated_count++; } else { $errors[] = $post_id->get_error_message(); } } // Migrate completed notes foreach ($done_notes as $note) { $post_data = array( 'post_title' => wp_trim_words($note['text'], 10, '...'), 'post_content' => $note['text'], 'post_status' => 'publish', 'post_type' => 'wp_note', 'post_author' => $note['author'] ?? get_current_user_id(), ); $post_id = wp_insert_post($post_data, true); if (!is_wp_error($post_id)) { update_post_meta($post_id, '_wp_note_color', $note['color'] ?? '#000000'); update_post_meta($post_id, '_wp_note_size', $note['size'] ?? '16'); update_post_meta($post_id, '_wp_note_font', $note['font'] ?? 'Arial'); update_post_meta($post_id, '_wp_note_status', 'completed'); update_post_meta($post_id, '_wp_note_created', $note['timestamp'] ?? ''); update_post_meta($post_id, '_wp_note_completed_by', $note['completed_by'] ?? ''); update_post_meta($post_id, '_wp_note_completed_on', $note['completed_on'] ?? ''); update_post_meta($post_id, '_wp_note_author_name', $note['author_name'] ?? ''); $migrated_count++; } else { $errors[] = $post_id->get_error_message(); } } // Create backup of old data update_option('wp_notes_pre_migration_backup', array( 'notes' => $existing_notes, 'done_notes' => $done_notes, 'migration_date' => current_time('mysql') )); return array( 'success' => true, 'migrated_count' => $migrated_count, 'errors' => $errors ); } // Add migration notice for admin function wp_notes_migration_notice() { if (!current_user_can('manage_options')) { return; } $migrated = get_option('wp_notes_migration_completed'); if (!$migrated) { ?>