3f881b01ca
The "Add emoji" formatting option rendered two clickable elements stacked: the "Click to add emoji" text input AND a separate button beside it with a smiley-face dashicon. Both opened the same dropdown picker — visually duplicated and slightly confusing for users who expected one or the other to be the trigger. The button is gone. Clicking the input itself still opens the picker (the existing JS already wired both .emoji-picker-button and .emoji-input to the same click handler — the click handler now just targets .emoji-input). Keyboard navigation (Enter/Space to toggle, Escape to close) refocuses the input instead of the removed button. Orphaned .emoji-picker-button CSS rule deleted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1590 lines
59 KiB
PHP
1590 lines
59 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
|
|
);
|
|
|
|
// "My Notes" — the main landing submenu (same slug as the parent
|
|
// menu, so clicking either WP Notes or My Notes lands on the same
|
|
// central dashboard: create form + active list + completed list).
|
|
add_submenu_page(
|
|
'wp-notes', // Parent slug
|
|
'My Notes', // Page title (browser tab)
|
|
'My Notes', // Menu label (sidebar)
|
|
'manage_options', // Capability
|
|
'wp-notes', // Menu slug (matches parent → same page)
|
|
'wp_notes_create_page' // Renderer
|
|
);
|
|
|
|
// 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 Notes 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');
|
|
?>
|
|
<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');
|
|
|
|
// Tools → My Notes — quick-access shortcut for users who think of
|
|
// notes as a "tool" rather than navigating WP Notes from the top menu.
|
|
function wp_notes_add_tools_menu() {
|
|
add_management_page(
|
|
'My Notes', // Page title (browser tab)
|
|
'My Notes', // Menu label (Tools submenu)
|
|
'manage_options', // Capability
|
|
'wp-notes-create', // Menu slug
|
|
'wp_notes_create_page' // Renderer (same page as wp-notes)
|
|
);
|
|
}
|
|
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-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 — focus returns to the input
|
|
// (the picker trigger) after Escape now that the dedicated
|
|
// button has been removed.
|
|
$('.emoji-picker-container').on('keydown', function(e) {
|
|
var $dropdown = $(this).find('.emoji-picker-dropdown');
|
|
var $input = $(this).find('.emoji-input');
|
|
|
|
// Toggle dropdown with Enter or Space when the input is focused
|
|
if ((e.key === 'Enter' || e.key === ' ') && e.target === $input[0]) {
|
|
e.preventDefault();
|
|
$dropdown.toggle();
|
|
}
|
|
|
|
// Close with Escape
|
|
if (e.key === 'Escape' && $dropdown.is(':visible')) {
|
|
$dropdown.hide();
|
|
$input.focus();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize on page load
|
|
initEmojiPicker();
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
// Callback function for the "My Notes" page (registered from both the
|
|
// WP Notes submenu and Tools → My Notes; both route here).
|
|
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¬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);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</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>
|
|
<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-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¬e_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
|
|
?>
|