Files
rangerhq-logbook/wp-notes.php
T
ranger 868911dfa6 ux: move banner from Create-Note page to About page (side-by-side intro)
The banner image was the first thing under the page title on the
Create-Note page — taking significant vertical space before the user
saw the textarea. The Create-Note page is for *doing*, not for
*reading-about*.

CHANGES
- Banner removed from wp_notes_page_callback() — Create-Note page
  now opens straight into the form.
- Banner added to the top of the About page in a new side-by-side
  row: image on the left capped at 320px wide (not full-bleed), and
  a short intro paragraph + "Go to WP Notes →" CTA on the right.
  Stacks vertically on narrow screens via flex-wrap.
- The side-by-side block lives in its own .wp-notes-about-intro
  container so it doesn't tangle with the rest of the About page's
  pre-existing nested toggles (those are still on the Tier 2 list).
- .wp-notes-header-banner / .wp-notes-banner-img CSS removed from
  wp-notes-styles.php — no longer referenced anywhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 07:41:09 +01:00

1589 lines
58 KiB
PHP

<?php
/**
* A-WP-Notes - WordPress Task Management Plugin
*
* Plugin Name: A-WP-Notes
* Plugin URI: https://icanhelp.ie/wp-notes
* Description: A plugin to add your notes to the WordPress dashboard with import/export functionality
* Version: 3.0.2
* Requires at least: 5.0
* Requires PHP: 7.2
* Author: IR240474
* Author URI: https://rangersmyth.xyz/
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: a-wp-notes
* Domain Path: /languages
*
* @package WP_Notes
*/
/**
* Security check and WordPress core loading
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
die;
}
// One-time initialization of plugin constants
global $wp_notes_init;
if (!isset($wp_notes_init)) {
$wp_notes_init = true;
// Plugin Constants
if (!defined('WP_NOTES_VERSION')) define('WP_NOTES_VERSION', '3.0.2');
if (!defined('WP_NOTES_FILE')) define('WP_NOTES_FILE', __FILE__);
if (!defined('WP_NOTES_PATH')) define('WP_NOTES_PATH', plugin_dir_path(__FILE__));
if (!defined('WP_NOTES_URL')) define('WP_NOTES_URL', plugin_dir_url(__FILE__));
if (!defined('WP_NOTES_BASENAME')) define('WP_NOTES_BASENAME', plugin_basename(__FILE__));
}
// Initialize error logging if not already defined
if (!function_exists('wp_notes_log_error')) {
function wp_notes_log_error($message) {
if (defined('WP_DEBUG') && WP_DEBUG === true) {
error_log('[WP Notes] ' . $message);
}
}
}
// Plugin class autoloader
spl_autoload_register(function ($class) {
$prefix = 'WP_Notes\\';
$base_dir = plugin_dir_path(__FILE__) . 'includes/';
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
});
// Plugin initialization function
function wp_notes_init() {
// Load plugin functionality
if (class_exists('WP_Notes\\Plugin')) {
$plugin = new WP_Notes\Plugin();
$plugin->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');
?>
<div class="wrap">
<h1>WP Notes Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('wp_notes_settings');
do_settings_sections('wp-notes-settings');
submit_button();
?>
</form>
</div>
<?php
}
// Settings section callback
function wp_notes_section_callback() {
echo '<p>Configure default settings for WP Notes.</p>';
}
// Font setting callback
function wp_notes_font_callback() {
$options = get_option('wp_notes_settings');
$current = $options['default_font'] ?? 'Arial';
?>
<select name="wp_notes_settings[default_font]">
<option value="Arial" <?php selected($current, 'Arial'); ?>>Arial</option>
<option value="Helvetica" <?php selected($current, 'Helvetica'); ?>>Helvetica</option>
<option value="Times New Roman" <?php selected($current, 'Times New Roman'); ?>>Times New Roman</option>
<option value="Verdana" <?php selected($current, 'Verdana'); ?>>Verdana</option>
</select>
<?php
}
// Size setting callback
function wp_notes_size_callback() {
$options = get_option('wp_notes_settings');
$current = $options['default_size'] ?? '16';
?>
<input type="number" name="wp_notes_settings[default_size]" value="<?php echo esc_attr($current); ?>" min="8" max="72">
<span class="description">Font size in pixels (8-72)</span>
<?php
}
// Import/Export page callback
function wp_notes_import_export_page() {
// Display settings errors
settings_errors();
if (isset($_POST['export_notes'])) {
wp_notes_export_data();
}
if (isset($_POST['import_notes']) && !empty($_FILES['import_file']['tmp_name'])) {
try {
wp_notes_import_data();
} catch (Exception $e) {
add_settings_error(
'wp_notes_import',
'import_error',
'Error importing notes: ' . esc_html($e->getMessage()),
'error'
);
}
}
// Get current settings for display
$options = get_option('wp_notes_settings');
?>
<div class="wrap">
<h1>Import/Export Notes</h1>
<!-- Export Section -->
<div class="postbox">
<div class="inside">
<h2>Export Notes</h2>
<p>Download your notes as JSON or CSV file.</p>
<form method="post">
<select name="export_format">
<option value="json">JSON</option>
<option value="csv">CSV</option>
</select>
<?php wp_nonce_field('wp_notes_export', 'export_nonce'); ?>
<input type="submit" name="export_notes" class="button button-primary" value="Export Notes">
</form>
</div>
</div>
<!-- Import Section -->
<div class="postbox">
<div class="inside">
<h2>Import Notes</h2>
<p>Import notes from JSON or CSV file.</p>
<form method="post" enctype="multipart/form-data">
<input type="file" name="import_file" accept=".json,.csv">
<?php wp_nonce_field('wp_notes_import', 'import_nonce'); ?>
<input type="submit" name="import_notes" class="button button-primary" value="Import Notes">
</form>
</div>
</div>
</div>
<?php
}
// Export functionality
function wp_notes_export_data() {
if (!isset($_POST['export_nonce']) || !wp_verify_nonce($_POST['export_nonce'], 'wp_notes_export')) {
wp_die('Security check failed');
}
$format = $_POST['export_format'] ?? 'json';
$notes = get_option('wp_notes', array());
$done_notes = get_option('wp_done_notes', array());
$data = array(
'active_notes' => $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() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Initialize emoji picker
function initEmojiPicker() {
// Handle emoji picker toggle
$('.emoji-picker-button, .emoji-input').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var $container = $(this).closest('.emoji-picker-container');
$('.emoji-picker-dropdown').not($container.find('.emoji-picker-dropdown')).hide();
$container.find('.emoji-picker-dropdown').toggle();
});
// Handle emoji selection
$('.emoji-option').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
var emoji = $(this).data('emoji');
var $container = $(this).closest('.emoji-picker-container');
var $noteText = $('#wp_notes_text');
// Get cursor position or selection
var start = $noteText[0].selectionStart;
var end = $noteText[0].selectionEnd;
var text = $noteText.val();
// Insert emoji at cursor position
var newText = text.substring(0, start) + emoji + text.substring(end);
$noteText.val(newText);
// Update cursor position
var newCursorPos = start + emoji.length;
$noteText[0].setSelectionRange(newCursorPos, newCursorPos);
// Focus back on textarea
$noteText.focus();
// Hide dropdown
$container.find('.emoji-picker-dropdown').hide();
});
// Close emoji picker when clicking outside
$(document).on('click', function(e) {
if (!$(e.target).closest('.emoji-picker-container').length) {
$('.emoji-picker-dropdown').hide();
}
});
// Handle keyboard navigation
$('.emoji-picker-container').on('keydown', function(e) {
var $dropdown = $(this).find('.emoji-picker-dropdown');
var $button = $(this).find('.emoji-picker-button');
// Toggle dropdown with Enter or Space
if ((e.key === 'Enter' || e.key === ' ') && e.target === $button[0]) {
e.preventDefault();
$dropdown.toggle();
}
// Close with Escape
if (e.key === 'Escape' && $dropdown.is(':visible')) {
$dropdown.hide();
$button.focus();
}
});
}
// Initialize on page load
initEmojiPicker();
});
</script>
<?php
}
// Callback function for "Create WP Note" page
function wp_notes_create_page() {
?>
<div class="wrap">
<h1>Create a New WP Note</h1>
<form method="post">
<label for="wp_notes_text">Note:</label><br>
<textarea name="wp_notes_text" id="wp_notes_text" required></textarea><br>
<label for="wp_notes_color">Color:</label><br>
<input type="color" name="wp_notes_color" id="wp_notes_color" value="#000000"><br>
<label for="wp_notes_size">Size:</label><br>
<input type="number" name="wp_notes_size" id="wp_notes_size" min="8" max="72" value="16"><br>
<label for="wp_notes_font">Font:</label><br>
<select name="wp_notes_font" id="wp_notes_font">
<option value="Arial">Arial</option>
<option value="Helvetica">Helvetica</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Verdana">Verdana</option>
</select><br>
<input type="submit" value="Add Note" class="button button-primary">
</form>
</div>
<?php
}
// JavaScript for saving edited notes
add_action('admin_footer', 'wp_notes_add_inline_scripts');
function wp_notes_add_inline_scripts() {
echo "
<script type='text/javascript'>
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&note_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);
}
});
});
});
</script>";
}
// 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'
));
?>
<div class="wrap">
<!-- Header Section with WordPress Admin Styling -->
<h1 class="wp-heading-inline">WP Notes</h1>
<span class="page-title-action">v<?php echo esc_html(WP_NOTES_VERSION); ?></span>
<hr class="wp-header-end">
<p class="description" style="max-width: 720px;">
Log your daily tasks and keep a tidy work-log inside WordPress.
<a href="<?php echo esc_url(admin_url('admin.php?page=wp-notes-about')); ?>">Read more on the About page →</a>
</p>
<!-- Note Creation Form with WordPress Admin Styling -->
<div class="postbox">
<div class="postbox-header">
<h2 class="hndle">Add New Note</h2>
</div>
<div class="inside">
<form method="post" class="wp-notes-form">
<div class="form-field">
<label for="wp_notes_text" class="screen-reader-text">Note Content:</label>
<textarea
name="wp_notes_text"
id="wp_notes_text"
class="large-text"
required
rows="4"
aria-label="Enter your note content"
placeholder="Enter your note here..."></textarea>
</div>
<div class="wp-notes-formatting form-field">
<fieldset>
<legend class="screen-reader-text">Note Formatting Options</legend>
<div class="formatting-options">
<div class="format-option">
<label for="wp_notes_color" class="format-label">
<span class="dashicons dashicons-color-picker"></span>
Text Color
</label>
<input
type="color"
name="wp_notes_color"
id="wp_notes_color"
value="#000000"
aria-label="Choose text color">
</div>
<div class="format-option">
<label for="wp_notes_size" class="format-label">
<span class="dashicons dashicons-editor-textcolor"></span>
Size (px)
</label>
<input
type="number"
name="wp_notes_size"
id="wp_notes_size"
min="8"
max="72"
value="16"
class="small-text"
aria-label="Choose text size">
</div>
<div class="format-option">
<label for="wp_notes_font" class="format-label">
<span class="dashicons dashicons-editor-paragraph"></span>
Font
</label>
<select
name="wp_notes_font"
id="wp_notes_font"
class="regular-text"
aria-label="Choose font">
<option value="Arial">Arial</option>
<option value="Helvetica">Helvetica</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Verdana">Verdana</option>
</select>
</div>
<div class="format-option">
<label for="wp_notes_emoji" class="format-label">
<span class="dashicons dashicons-smiley"></span>
Emoji
</label>
<div class="emoji-picker-container">
<input
type="text"
name="wp_notes_emoji"
id="wp_notes_emoji"
placeholder="Click to add emoji"
class="small-text emoji-input"
aria-label="Add emoji"
readonly>
<button type="button"
class="button emoji-picker-button"
aria-label="Open emoji picker">
<span class="dashicons dashicons-smiley"></span>
</button>
<div class="emoji-picker-dropdown" style="display: none;">
<div class="emoji-list">
<!-- Common emojis -->
<button type="button" class="emoji-option" data-emoji="😊">😊</button>
<button type="button" class="emoji-option" data-emoji="👍">👍</button>
<button type="button" class="emoji-option" data-emoji="✅">✅</button>
<button type="button" class="emoji-option" data-emoji="⭐">⭐</button>
<button type="button" class="emoji-option" data-emoji="📝">📝</button>
<button type="button" class="emoji-option" data-emoji="❗">❗</button>
<button type="button" class="emoji-option" data-emoji="❓">❓</button>
<button type="button" class="emoji-option" data-emoji="⚠️">⚠️</button>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="submit-button">
<input
type="submit"
value="Add Note"
class="button button-primary button-large"
aria-label="Add new note">
</div>
</form>
</div>
</div>
<style>
.wp-notes-form .form-field { margin-bottom: 1em; }
.wp-notes-formatting { background: #f9f9f9; padding: 15px; border-radius: 4px; }
.formatting-options { display: flex; gap: 20px; flex-wrap: wrap; align-items: center; }
.format-option { display: flex; flex-direction: column; gap: 5px; }
.format-label { display: flex; align-items: center; gap: 5px; }
.submit-button { margin-top: 1em; }
.emoji-picker-container { position: relative; }
.emoji-picker-button { margin-left: 5px; padding: 2px 6px; }
.emoji-picker-dropdown {
position: absolute;
top: 100%;
left: 0;
background: white;
border: 1px solid #ccd0d4;
border-radius: 4px;
padding: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
z-index: 100;
}
.emoji-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 5px;
}
.emoji-option {
border: none;
background: none;
font-size: 20px;
padding: 5px;
cursor: pointer;
border-radius: 4px;
}
.emoji-option:hover {
background: #f0f0f1;
}
.emoji-input {
cursor: pointer;
}
</style>
<!-- Notes List -->
<h2>Notes Todo List:</h2>
<!-- Active Notes List -->
<div class="wp-notes-active" id="active-notes">
<?php wp_notes_display_notes('active'); ?>
</div>
<!-- Completed Notes List -->
<div class="wp-notes-completed" id="completed-notes">
<?php wp_notes_display_notes('completed'); ?>
</div>
<!-- Footer: support link (lives at the BOTTOM, not the top) -->
<p class="wp-notes-footer-support">
<a href="https://www.buymeacoffee.com/davidkeanek" target="_blank" rel="noopener"
class="button button-secondary"
aria-label="Support the developer by buying them a coffee">
☕ Buy me a coffee
</a>
</p>
</div>
<?php
}
// List Table Function
function wp_notes_list_table() {
// Check user capabilities
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());
if (!$notes) {
echo '<p>' . esc_html__('No notes found.', 'a-wp-notes') . '</p>';
return;
}
/* Dashboard widget table. No checkbox column — bulk actions are not
wired up; re-add when there is something to act on. */
echo '<table class="wp-list-table widefat striped">
<thead>
<tr>
<th scope="col" class="manage-column">Note</th>
<th scope="col" class="manage-column">Created By</th>
<th scope="col" class="manage-column">Created On</th>
<th scope="col" class="manage-column">Actions</th>
</tr>
</thead>
<tbody>';
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 "<tr>
<td style='color: $color; font-size: {$size}px; font-family: $font;'>$text</td>
<td>$author</td>
<td>$timestamp</td>
<td>
<form method='post' style='display:inline;'>
<input type='hidden' name='note_id' value='$key'>
<button type='submit' name='mark_done' value='Mark as Done' class='button button-secondary'>Mark as Done</button>
</form>
<button type='button' class='button edit-note' data-note-id='$key'>Edit</button>
<div id='edit-note-$key' style='display:none;'>
<form class='edit-note-form' data-note-id='$key'>
" . wp_nonce_field('wp_notes_nonce', '_wpnonce', true, false) . "
<input type='hidden' name='note_id' value='$key'>
<textarea name='new_text'>$text</textarea><br>
<input type='color' name='edit_color' value='$color'><br>
<input type='number' name='edit_size' value='$size' min='8' max='72'><br>
<select name='edit_font'>
<option value='Arial' " . selected('Arial', $font, false) . ">Arial</option>
<option value='Helvetica' " . selected('Helvetica', $font, false) . ">Helvetica</option>
<option value='Times New Roman' " . selected('Times New Roman', $font, false) . ">Times New Roman</option>
<option value='Verdana' " . selected('Verdana', $font, false) . ">Verdana</option>
</select><br>
<button type='submit' class='button button-primary'>Save</button>
</form>
</div>
</td>
</tr>";
}
echo '</tbody></table>';
// JavaScript for form toggling and AJAX submissions
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Show edit form
$('.edit-note').on('click', function(e) {
e.preventDefault();
var noteId = $(this).data('note-id');
$('.edit-note-form').not('#edit-note-' + noteId).hide();
$('#edit-note-' + noteId).toggle();
});
// AJAX submit the edit form
$('.edit-note-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $submitButton = $form.find('button[type="submit"]');
var noteId = $form.data('note-id');
// Log form data for debugging
console.log('Form data:', $form.serialize());
console.log('Note ID:', noteId);
// Disable submit button and show loading state
$submitButton.prop('disabled', true).text('Saving...');
// AJAX request
$.ajax({
url: ajaxurl,
type: 'POST',
data: $form.serialize() + '&action=wp_notes_save_edit&note_id=' + noteId,
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error: ' + (response.data || 'Unknown error occurred'));
}
},
error: function(xhr, status, error) {
alert('Server error: ' + error);
},
complete: function() {
$submitButton.prop('disabled', false).text('Save');
}
});
// Emoji Picker Functionality
$('.emoji-picker-button, .emoji-input').on('click', function(e) {
e.preventDefault();
var $container = $(this).closest('.emoji-picker-container');
$container.find('.emoji-picker-dropdown').toggle();
});
// Handle emoji selection
$('.emoji-option').on('click', function() {
var emoji = $(this).data('emoji');
var $container = $(this).closest('.emoji-picker-container');
var $input = $container.find('.emoji-input');
// Append emoji to note text
var $noteText = $('#wp_notes_text');
var currentPosition = $noteText[0].selectionStart;
var text = $noteText.val();
var newText = text.slice(0, currentPosition) + emoji + text.slice(currentPosition);
$noteText.val(newText);
// Update cursor position
$noteText[0].setSelectionRange(currentPosition + emoji.length, currentPosition + emoji.length);
$noteText.focus();
// Hide dropdown
$container.find('.emoji-picker-dropdown').hide();
});
// Close emoji picker when clicking outside
$(document).on('click', function(e) {
if (!$(e.target).closest('.emoji-picker-container').length) {
$('.emoji-picker-dropdown').hide();
}
});
});
</script>
<?php
}
// Done Notes Table Function
function wp_notes_done_table() {
$done_notes = get_option('wp_done_notes', array());
if (!$done_notes) {
echo '<p>' . esc_html__('No completed notes.', 'a-wp-notes') . '</p>';
return;
}
echo '<form method="post"><table class="wp-list-table widefat striped">
<thead>
<tr>
<th scope="col" class="manage-column column-cb check-column">
<input type="checkbox" id="select-all-done">
</th>
<th scope="col">Note</th>
<th scope="col">Created By</th>
<th scope="col">Created On</th>
<th scope="col">Completed By</th>
<th scope="col">Completed On</th>
<th scope="col">Last Modified</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>';
foreach ($done_notes as $key => $note) {
echo '<tr>
<td><input type="checkbox" name="done_ids[]" value="' . esc_attr($key) . '" class="done-checkbox"></td>
<td style="color: ' . esc_attr($note['color']) . '; font-size: 14px; font-family: ' . esc_attr($note['font']) . ';">'
. esc_html($note['text']) . '</td>
<td>' . esc_html($note['author_name'] ?? 'Unknown') . '</td>
<td>' . esc_html($note['timestamp'] ?? 'Unknown') . '</td>
<td>' . esc_html($note['completed_by'] ?? 'Unknown') . '</td>
<td>' . esc_html($note['completed_on'] ?? 'Unknown') . '</td>
<td>' . esc_html($note['last_modified'] ?? 'N/A') . '</td>
<td>
<input type="submit" name="restore_note" value="Restore" class="button button-secondary">
</td>
</tr>';
}
echo '</tbody></table></form>';
// Add JavaScript for select all functionality
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('#select-all-done').change(function() {
$('.done-checkbox').prop('checked', $(this).prop('checked'));
});
});
</script>
<?php
}
// Handle note actions
add_action('admin_init', 'wp_notes_handle_actions');
function wp_notes_handle_actions() {
$current_user = wp_get_current_user();
// Check if we're adding a new note
if (isset($_POST['wp_notes_text'])) {
$notes = get_option('wp_notes', array());
// Create the new note
$new_note = array(
'text' => 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=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';
?>
<p>
<label for="wp_note_color"><?php _e('Color:', 'a-wp-notes'); ?></label><br>
<input type="color" id="wp_note_color" name="wp_note_color" value="<?php echo esc_attr($color); ?>">
</p>
<p>
<label for="wp_note_size"><?php _e('Size:', 'a-wp-notes'); ?></label><br>
<input type="number" id="wp_note_size" name="wp_note_size" value="<?php echo esc_attr($size); ?>" min="8" max="72">
</p>
<p>
<label for="wp_note_font"><?php _e('Font:', 'a-wp-notes'); ?></label><br>
<select id="wp_note_font" name="wp_note_font">
<option value="Arial" <?php selected($font, 'Arial'); ?>>Arial</option>
<option value="Helvetica" <?php selected($font, 'Helvetica'); ?>>Helvetica</option>
<option value="Times New Roman" <?php selected($font, 'Times New Roman'); ?>>Times New Roman</option>
<option value="Verdana" <?php selected($font, 'Verdana'); ?>>Verdana</option>
</select>
</p>
<p>
<label for="wp_note_status"><?php _e('Status:', 'a-wp-notes'); ?></label><br>
<select id="wp_note_status" name="wp_note_status">
<option value="active" <?php selected($status, 'active'); ?>><?php _e('Active', 'a-wp-notes'); ?></option>
<option value="completed" <?php selected($status, 'completed'); ?>><?php _e('Completed', 'a-wp-notes'); ?></option>
</select>
</p>
<?php
}
// Save meta box data
function wp_notes_save_meta_box_data($post_id) {
// Check if our nonce is set and verify it
if (!isset($_POST['wp_notes_meta_box_nonce']) ||
!wp_verify_nonce($_POST['wp_notes_meta_box_nonce'], 'wp_notes_meta_box')) {
return;
}
// Don't save during autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Check user permissions
if (!current_user_can('edit_post', $post_id)) {
return;
}
// Save the meta fields
$fields = array(
'_wp_note_color' => '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) {
?>
<div class="notice notice-info is-dismissible">
<p>
<?php _e('WP Notes needs to migrate your existing notes to the new storage system.', 'a-wp-notes'); ?>
<a href="<?php echo esc_url(admin_url('admin.php?page=wp-notes-settings&action=migrate')); ?>" class="button button-primary">
<?php _e('Start Migration', 'a-wp-notes'); ?>
</a>
</p>
</div>
<?php
}
}
add_action('admin_notices', 'wp_notes_migration_notice');
// Handle migration request
function wp_notes_handle_migration() {
if (isset($_GET['page']) && $_GET['page'] === 'wp-notes-settings' &&
isset($_GET['action']) && $_GET['action'] === 'migrate') {
check_admin_referer('wp_notes_migration');
$result = wp_notes_migrate_to_cpt();
if ($result['success']) {
update_option('wp_notes_migration_completed', true);
add_settings_error(
'wp_notes_migration',
'migration_success',
sprintf(
__('Successfully migrated %d notes. %s', 'a-wp-notes'),
$result['migrated_count'],
!empty($result['errors']) ? 'Errors: ' . implode(', ', $result['errors']) : ''
),
'updated'
);
} else {
add_settings_error(
'wp_notes_migration',
'migration_error',
__('Migration failed. Please try again or contact support.', 'a-wp-notes'),
'error'
);
}
wp_redirect(admin_url('admin.php?page=wp-notes-settings'));
exit;
}
}
add_action('admin_init', 'wp_notes_handle_migration');
// End of wp-notes.php
?>