commit 433de27d9beb932121860814b7bc36d910e797f4 Author: David Keane Date: Fri May 22 16:43:03 2026 +0100 chore: archive A-WP-Notes v3.0.2 — minimalist parallel fork Imports the v3.0.2 line of A-WP-Notes as it existed on M5 at: Local Sites/wordpress/public/wp-content/plugins/a-wp-notes/ This is a deliberately minimal parallel fork of the plugin, distinct from the v1.2.0 line in ranger/a-wp-notes. It carries only the core note-taking functionality: wp-notes.php — plugin bootstrap + admin UI inc/wp-notes-display.php — note rendering inc/wp-notes-about.php — About page inc/wp-notes-feedback.php — feedback module inc/wp-notes-styles.php — style enqueues inc/wp-notes-updater.php — self-hosted updater stub inc/admin-bar.php — admin bar integration js/wp-notes-feedback.js — feedback front-end js/Chart.js — charting (bundled) assets/wp-notes-banner.jpg — plugin banner The AI ecosystem (chat, personalities, MCP), speed-test system, OS info pages, and the bulk of the v1.2.0 / v2.0.x feature surface are intentionally absent — this fork was created by copying the plugin to a second WP install and trimming back to a lighter baseline. Archived for comparison and parallel-line testing. No further development is planned on this line; the active line continues at v1.2.0 in ranger/a-wp-notes. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc57627 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# OS files +.DS_Store +Thumbs.db +ehthumbs.db + +# Editor / IDE +.idea/ +.vscode/ +*.sublime-project +*.sublime-workspace +*.swp +*~ + +# Build / dependencies (not used by this fork, kept for hygiene) +node_modules/ +vendor/ +*.log +*.zip +*.tar.gz + +# Local-only files +.env +.env.* +!.env.example diff --git a/assets/wp-notes-banner.jpg b/assets/wp-notes-banner.jpg new file mode 100644 index 0000000..7865fe3 Binary files /dev/null and b/assets/wp-notes-banner.jpg differ diff --git a/inc/admin-bar.php b/inc/admin-bar.php new file mode 100644 index 0000000..6b8803e --- /dev/null +++ b/inc/admin-bar.php @@ -0,0 +1,111 @@ + + + add_node(array( + 'id' => 'wp-notes', + 'title' => sprintf('WP Notes %d', $count), + 'href' => admin_url('admin.php?page=wp-notes'), + )); + + // Add New Note submenu + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-new-note', + 'title' => 'New Note', + 'href' => admin_url('admin.php?page=wp-notes#new-note'), + 'parent' => 'wp-notes' + )); + } + + // Add About submenu + if (current_user_can('manage_options')) { + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-about', + 'title' => 'About', + 'href' => admin_url('admin.php?page=wp-notes-about'), + 'parent' => 'wp-notes' + )); + + // Add Settings submenu + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-settings', + 'title' => 'Settings', + 'href' => admin_url('admin.php?page=wp-notes-settings'), + 'parent' => 'wp-notes', + )); + } + + // Add Import/Export submenu (for admins) + if (current_user_can('manage_options')) { + $export_url = add_query_arg('action', 'export', admin_url('admin.php?page=wp-notes')); + $export_nonce_url = wp_nonce_url($export_url, 'wp_notes_export_action', 'wp_notes_export_nonce'); + + // Add export submenu + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-export', + 'title' => 'Export Notes', + 'href' => $export_nonce_url, + 'parent' => 'wp-notes' + )); + + // Add import submenu + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-import', + 'title' => 'Import Notes', + 'href' => add_query_arg('section', 'import', admin_url('admin.php?page=wp-notes')), + 'parent' => 'wp-notes' + )); + } + + // Add update check for admins + if (current_user_can('manage_options')) { + $update_check_url = add_query_arg('check_update', '1', admin_url('admin.php?page=wp-notes')); + $update_check_nonce_url = wp_nonce_url($update_check_url, 'wp_notes_check_update_action', 'wp_notes_check_update_nonce'); + + $wp_admin_bar->add_node(array( + 'id' => 'wp-notes-update', + 'title' => 'Check Updates', + 'href' => $update_check_nonce_url, + 'parent' => 'wp-notes' + )); + } +} diff --git a/inc/wp-notes-about.php b/inc/wp-notes-about.php new file mode 100644 index 0000000..21128f5 --- /dev/null +++ b/inc/wp-notes-about.php @@ -0,0 +1,216 @@ + +
+ +
+ +

Welcome to WP Notes About Page v

+
+

WP Notes is your task management solution for WordPress. Keep track of your todos, collaborate with team members, and never lose important notes.

+ +
+ + + +

About WP Notes v

+
+ + + +
+ +

+ +

Version History v

+
+ + + +
+
+ + + + +
+

Leave Feedback

+ + + + + + + +
+
+
+ +
+
+ + + Buy me a coffee + +
+
+
+ + + + \ No newline at end of file diff --git a/inc/wp-notes-display.php b/inc/wp-notes-display.php new file mode 100644 index 0000000..2ba17c5 --- /dev/null +++ b/inc/wp-notes-display.php @@ -0,0 +1,117 @@ +'; + echo '

' . esc_html__('No ' . ($type === 'active' ? 'active' : 'completed') . ' notes found.', 'a-wp-notes') . '

'; + echo ''; + return; + } + + // Section header with status count + $status_class = ($type === 'active') ? 'status-active' : 'status-completed'; + echo '
'; + echo '

'; + echo '' . esc_html($section_title) . ''; + echo '(' . count($notes) . ')'; + echo '

'; + + echo '
'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + if ($type === 'completed') { + echo ''; + echo ''; + } + echo ''; + 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 ''; + echo ''; + echo ''; + echo ''; + + if ($type === 'completed') { + echo ''; + echo ''; + } + + echo ''; + + // Edit form (for active notes only) + if ($type === 'active') { + echo ''; + } + } + + echo '
'; + echo ''; + echo '' . esc_html__('Note', 'a-wp-notes') . '' . esc_html__('Author', 'a-wp-notes') . '' . esc_html__('Created', 'a-wp-notes') . '' . esc_html__('Completed By', 'a-wp-notes') . '' . esc_html__('Completed On', 'a-wp-notes') . '' . esc_html__('Actions', 'a-wp-notes') . '
' . $text . '' . $author . '' . $timestamp . '' . esc_html($note['completed_by'] ?? 'Unknown') . '' . esc_html($note['completed_on'] ?? 'Unknown') . ''; + if ($type === 'active') { + echo ' '; + echo '
'; + echo ''; + echo ''; + echo '
'; + } else { + echo '
'; + echo ''; + echo ''; + echo '
'; + } + echo '
'; +} diff --git a/inc/wp-notes-feedback.php b/inc/wp-notes-feedback.php new file mode 100644 index 0000000..ab52c76 --- /dev/null +++ b/inc/wp-notes-feedback.php @@ -0,0 +1,161 @@ + admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('wp_notes_feedback_nonce') + )); +} +add_action('admin_enqueue_scripts', 'wp_notes_enqueue_feedback_script'); + +// Feedback Form HTML +function wp_notes_feedback_form() { + ?> + + + + $name, + 'email' => $email, + 'message' => $message, + 'timestamp' => current_time('mysql') + ); + + // Example: Save feedback to an option + $feedbacks = get_option('wp_notes_feedbacks', array()); + $feedbacks[] = $feedback; + update_option('wp_notes_feedbacks', $feedbacks); + + // Example: Send feedback via email + $to = 'david@icanhelp.ie'; + $subject = 'WP Notes Feedback'; + $body = "Name: $name\nEmail: $email\nMessage: $message"; + $headers = array('Content-Type: text/plain; charset=UTF-8'); + + wp_mail($to, $subject, $body, $headers); + + wp_send_json_success('Feedback submitted successfully.'); +} +add_action('wp_ajax_wp_notes_submit_feedback', 'wp_notes_submit_feedback'); + +// Handle Help Form Submission +function wp_notes_submit_help() { + // 1. Nonce Check (already good) + if (!check_ajax_referer('wp_notes_feedback_nonce', 'nonce', false)) { + wp_send_json_error('Invalid nonce.'); + return; + } + + // 2. Capability Check (e.g., any logged-in user can request help) + if (!current_user_can('read')) { // 'read' is a basic capability for any logged-in user + wp_send_json_error('You do not have permission to submit a help request.', 403); + return; + } + + // Validate required fields + $required_fields = ['help_name', 'help_email', 'help_message']; + foreach ($required_fields as $field) { + if (!isset($_POST[$field]) || empty($_POST[$field])) { + wp_send_json_error("Missing required field: $field"); + return; + } + } + + // 3. Sanitize and validate data (already good) + $name = sanitize_text_field($_POST['help_name']); + $email = sanitize_email($_POST['help_email']); + $message = sanitize_textarea_field($_POST['help_message']); + + // Save help request to database or send via email + $help_request = array( + 'name' => $name, + 'email' => $email, + 'message' => $message, + 'timestamp' => current_time('mysql') + ); + + // Example: Save help request to an option + $help_requests = get_option('wp_notes_help_requests', array()); + $help_requests[] = $help_request; + update_option('wp_notes_help_requests', $help_requests); + + // Example: Send help request via email + $to = 'david@icanhelp.ie'; + $subject = 'WP Notes Help Request'; + $body = "Name: $name\nEmail: $email\nMessage: $message"; + $headers = array('Content-Type: text/plain; charset=UTF-8'); + + wp_mail($to, $subject, $body, $headers); + + wp_send_json_success('Help request submitted successfully.'); +} +add_action('wp_ajax_wp_notes_submit_help', 'wp_notes_submit_help'); + +?> \ No newline at end of file diff --git a/inc/wp-notes-styles.php b/inc/wp-notes-styles.php new file mode 100644 index 0000000..b346f8c --- /dev/null +++ b/inc/wp-notes-styles.php @@ -0,0 +1,213 @@ + + + setBranch('main'); +} +add_action('init', 'wp_notes_setup_updater'); + +// Add update checking functionality +function wp_notes_check_for_updates() { + // Check if the 'check_update' GET parameter is set and the user has the capability to manage plugin options. + if (isset($_GET['check_update']) && $_GET['check_update'] === '1' && current_user_can('manage_a_wp_notes_options')) { + // Verify the nonce. The nonce name 'a_wp_notes_check_update_nonce' and action 'a_wp_notes_check_update_action' + // should match what's generated in admin-bar.php. + if (!isset($_GET['a_wp_notes_check_update_nonce']) || !wp_verify_nonce(sanitize_key($_GET['a_wp_notes_check_update_nonce']), 'a_wp_notes_check_update_action')) { + wp_die('Nonce verification failed!', 'Error', array('response' => 403)); + } + // Trigger a manual update check + wp_clean_plugins_cache(); + wp_update_plugins(); + + add_settings_error( + 'wp_notes', + 'update_check', + 'Update check completed.', + 'updated' + ); + } +} +add_action('admin_init', 'wp_notes_check_for_updates'); + +// Note: For the PucFactory::buildUpdateChecker, the second argument `__FILE__` currently refers to +// `wp-notes-updater.php`. For the library to work correctly, this should typically be the path +// to your main plugin file (e.g., `WP_PLUGIN_DIR . '/a-wp-notes/a-wp-notes.php'`). +// This is a setup detail for the update checker rather than a direct security fix from this review. diff --git a/js/Chart.js b/js/Chart.js new file mode 100644 index 0000000..e69de29 diff --git a/js/wp-notes-feedback.js b/js/wp-notes-feedback.js new file mode 100644 index 0000000..910645a --- /dev/null +++ b/js/wp-notes-feedback.js @@ -0,0 +1,66 @@ +// wp-notes-feedback.js +jQuery(document).ready(function($) { + console.log('wp-notes-feedback.js loaded'); + + // Handle feedback form submission + $('#wp-notes-feedback-form').on('submit', function(e) { + e.preventDefault(); + var formData = $(this).serialize(); + + $.ajax({ + url: wp_notes_feedback_vars.ajax_url, + type: 'POST', + data: formData + '&action=wp_notes_submit_feedback&nonce=' + wp_notes_feedback_vars.nonce, + success: function(response) { + if (response.success) { + alert(response.data); + $('#wp-notes-feedback-form')[0].reset(); + $('#feedback-form-improve').hide(); + } else { + alert('Error: ' + (response.data || 'Unknown error occurred')); + } + }, + error: function(xhr, status, error) { + alert('Server error: ' + error); + } + }); + }); + + // Handle help form submission + $('#wp-notes-help-form').on('submit', function(e) { + e.preventDefault(); + var formData = $(this).serialize(); + + $.ajax({ + url: wp_notes_feedback_vars.ajax_url, + type: 'POST', + data: formData + '&action=wp_notes_submit_help&nonce=' + wp_notes_feedback_vars.nonce, + success: function(response) { + if (response.success) { + alert(response.data); + $('#wp-notes-help-form')[0].reset(); + $('#feedback-form-help').hide(); + } else { + alert('Error: ' + (response.data || 'Unknown error occurred')); + } + }, + error: function(xhr, status, error) { + alert('Server error: ' + error); + } + }); + }); + + // Function to toggle the display of a section + function toggleSection(sectionId) { + var section = document.getElementById(sectionId); + if (section) { + if (section.style.display === "none") { + section.style.display = "block"; + } else { + section.style.display = "none"; + } + } else { + console.error("Element with ID " + sectionId + " not found."); + } + } +}); \ No newline at end of file diff --git a/wp-notes.php b/wp-notes.php new file mode 100644 index 0000000..cfab3f8 --- /dev/null +++ b/wp-notes.php @@ -0,0 +1,1774 @@ +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 Notes', + 'WP Notes', + 'manage_options', + 'wp-notes', + 'wp_notes_page_callback', + 'dashicons-admin-generic', + 3 + ); + + // Add "Create WP Note" to the Admin Bar menu + add_submenu_page( + 'wp-notes', // Page title + 'Create WP Note', // Menu title + 'Create WP Note', + 'manage_options', // Capability required to see this item + 'wp-notes', // Menu slug + 'wp_notes_create_page' // Function to display the page content + ); + + // 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 WP Notes submenu + add_submenu_page( + 'wp-notes', + 'About WP Notes', + 'About WP Notes', + 'manage_options', + 'wp-notes-about', + 'wp_notes_about_page' // Callback for About WP Notes page in 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 Notes Settings

+
+ +
+
+ Configure default settings for WP Notes.

'; +} + +// 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'); + +// Add "Create WP Note" to the Tools menu 29th Oct 2024. +function wp_notes_add_tools_menu() { + add_management_page( + 'Create WP Note', // Page title + 'Create WP Note', // Menu title + 'manage_options', // Capability required to see this item + 'wp-notes-create', // Menu slug + 'wp_notes_create_page' // Function to display the page content + ); +} +add_action('admin_menu', 'wp_notes_add_tools_menu'); + + +// 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() { + ?> + + +
+

Create a New WP Note

+
+
+
+
+
+
+
+
+
+ +
+
+ + 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); + } + }); + }); + }); + "; +} + +// 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 Notes 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' + )); + ?> +
+ +

Welcome to WP Notes

+ Version + + + + + + + + +
+
+

About WP Notes

+

Thank you for using WP Notes! This plugin helps you manage your WordPress tasks and notes efficiently. Keep track of your todos, mark them as complete, and export your data for safekeeping.

+

I made this plugin for my website to log my daily tasks and notes as a logbook for my clients, so they can see the work done by me or you and get clear value for their investment.

+
+ +
+ + +
+ + +
+ + + + + + + + +

+ + +

+ + + + + +
+
+

Add New Note

+
+
+
+
+ + +
+ +
+
+ Note Formatting Options + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+
+
+
+
+ +
+ +
+
+
+
+ + + + +

Notes Todo List:

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

'; + return; + } + + 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 '
+ + NoteCreated ByCreated OnActions
$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 '
+ + NoteCreated ByCreated OnCompleted ByCompleted OnLast ModifiedActions
' + . 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 Notes', + '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 Notes', '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' => true, + 'show_in_menu' => 'wp-notes', + 'capability_type' => 'post', + 'hierarchical' => false, + 'supports' => array('title', 'editor', 'author', 'custom-fields'), + 'has_archive' => false, + 'menu_position' => null, + 'show_in_rest' => true, + ); + + 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, + 'show_ui' => true, + 'show_admin_column' => true, + 'query_var' => true, + 'show_in_rest' => true, + )); +} +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) { + ?> +
+

+ + + + +

+
+