diff --git a/CHANGELOG.md b/CHANGELOG.md index 0129999..35424d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,44 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) β€” versi ## [Unreleased] +### Changed β€” Leave Feedback form (more options, multi-select, wired to email) +The right-column "Leave Feedback" form on the About page has been +expanded from two radio buttons to **seven checkboxes** (users can +pick more than one), a new optional message textarea, and a submit +button that **actually does something** β€” it AJAX-posts to a new +WP handler that emails the site admin via `wp_mail()`. + +**Form options (checkboxes β€” multi-select):** +- I have ideas to improve this plugin +- I need help with this plugin +- I found a bug +- I'd like to request a new feature +- I'd like to share my use case +- Just saying thanks πŸ€ +- Other + +**Submission flow:** +1. Client-side: at least one checkbox OR a message is required; + otherwise an inline hint shows. +2. AJAX POST `wp_notes_submit_feedback` with topics[] + message + + nonce. +3. Server-side handler (`manage_options` capability + nonce checked) + sanitizes input, allow-lists the topic keys, then builds a plain- + text email and ships it to `get_option('admin_email')` via + `wp_mail()`. Reply-To is set to the submitting user's email so + the admin can reply directly. +4. Email body includes: sender (display name + email + WP login), + site URL, plugin version, the checked topics (pretty-labelled), + and the message. +5. Inline success message replaces the form on success; inline + error message lets the user retry on failure. + +The old radio-button + broken `toggleSection('feedback-form-...')` +logic that pointed at non-existent IDs has been replaced entirely. +The `toggleSection()` helper is kept defined but is now genuinely +unused on the About page β€” flagged for removal in a future Tier-2 +pass. + ### Changed β€” About page rewritten (content + layout) The About page (`Settings β†’ WP Notes β†’ About`) has been rewritten from "wall of nested toggle boxes with outdated content" to diff --git a/inc/wp-notes-about.php b/inc/wp-notes-about.php index c811934..c77d22d 100644 --- a/inc/wp-notes-about.php +++ b/inc/wp-notes-about.php @@ -164,9 +164,24 @@ function wp_notes_about_page() {
-
-
- +

What's on your mind? (pick any)

+ + + + + + + + +

Anything to add? (optional)

+ + + + +

+ +

+


@@ -196,15 +211,54 @@ function wp_notes_about_page() { } } - // JavaScript to handle feedback form toggling + // Submit the feedback form β†’ AJAX β†’ wp_mail() to the site admin. + // No toggleSection() shuffle anymore (the old code targeted IDs + // that never existed). Validates that at least one topic is + // checked, otherwise the submit is a no-op with a hint. document.getElementById('wp-notes-feedback-options').addEventListener('submit', function(e) { e.preventDefault(); - var feedbackType = document.querySelector('input[name="feedback"]:checked').value; - if (feedbackType === 'improve') { - toggleSection('feedback-form-improve'); - } else if (feedbackType === 'help') { - toggleSection('feedback-form-help'); + var form = e.target; + var btn = document.getElementById('wp-notes-feedback-submit'); + var statusEl = document.getElementById('wp-notes-feedback-status'); + var topics = Array.from(form.querySelectorAll('input[name="feedback_topics[]"]:checked')).map(function (i) { return i.value; }); + var message = (form.querySelector('textarea[name="feedback_message"]').value || '').trim(); + var nonce = (form.querySelector('input[name="wp_notes_feedback_nonce"]').value || ''); + + if (topics.length === 0 && message === '') { + statusEl.textContent = 'Pick at least one option (or write a message) before submitting.'; + statusEl.style.color = '#8a2424'; + return; } + + btn.disabled = true; + btn.value = 'Sending…'; + statusEl.textContent = ''; + statusEl.style.color = ''; + + var fd = new FormData(); + fd.append('action', 'wp_notes_submit_feedback'); + fd.append('nonce', nonce); + topics.forEach(function (t) { fd.append('topics[]', t); }); + fd.append('message', message); + + fetch(ajaxurl, { method: 'POST', credentials: 'same-origin', body: fd }) + .then(function (r) { return r.json(); }) + .then(function (res) { + if (res && res.success) { + form.innerHTML = '

Thanks β€” your feedback has been sent. πŸ€

'; + } else { + statusEl.textContent = (res && res.data) ? String(res.data) : 'Sorry, something went wrong. Please try again.'; + statusEl.style.color = '#8a2424'; + btn.disabled = false; + btn.value = 'Submit Feedback'; + } + }) + .catch(function () { + statusEl.textContent = 'Network error β€” please try again in a moment.'; + statusEl.style.color = '#8a2424'; + btn.disabled = false; + btn.value = 'Submit Feedback'; + }); }); "; } +// Receive the "Leave Feedback" form from the About page and forward +// it to the site admin via wp_mail(). Topics is an array of short +// keys (improve / help / bug / feature / use-case / thanks / other); +// message is an optional free-text textarea. Sender info is taken +// from wp_get_current_user() so we never trust client-supplied +// identity. Capability-gated to manage_options because the About +// page is admin-only. +add_action('wp_ajax_wp_notes_submit_feedback', 'wp_notes_ajax_submit_feedback'); +function wp_notes_ajax_submit_feedback() { + if (!current_user_can('manage_options')) { + wp_send_json_error('Insufficient permissions.', 403); + } + check_ajax_referer('wp_notes_feedback_submit', 'nonce'); + + $allowed_topics = ['improve', 'help', 'bug', 'feature', 'use-case', 'thanks', 'other']; + $raw_topics = isset($_POST['topics']) && is_array($_POST['topics']) ? wp_unslash($_POST['topics']) : []; + $topics = array_values(array_intersect($allowed_topics, array_map('sanitize_key', $raw_topics))); + + $message = isset($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : ''; + + if (empty($topics) && $message === '') { + wp_send_json_error('Pick at least one topic or write a message.', 400); + } + + $user = wp_get_current_user(); + $site = get_bloginfo('name'); + $to = get_option('admin_email'); + + $topic_labels = [ + 'improve' => 'Ideas to improve the plugin', + 'help' => 'Needs help with the plugin', + 'bug' => 'Reporting a bug', + 'feature' => 'New feature request', + 'use-case' => 'Sharing a use case', + 'thanks' => 'Saying thanks', + 'other' => 'Other', + ]; + $topics_pretty = array_map(function ($t) use ($topic_labels) { return $topic_labels[$t] ?? $t; }, $topics); + + $subject = sprintf('[%s] WP Notes feedback from %s', $site, $user->display_name ?: $user->user_login); + $body = "Feedback received via WP Notes β†’ About page\n"; + $body .= str_repeat('-', 48) . "\n\n"; + $body .= 'From: ' . ($user->display_name ?: $user->user_login) . ' <' . $user->user_email . ">\n"; + $body .= 'Site: ' . home_url() . "\n"; + $body .= 'Plugin: v' . WP_NOTES_VERSION . "\n\n"; + $body .= "Topics:\n"; + if (!empty($topics_pretty)) { + foreach ($topics_pretty as $label) { $body .= ' - ' . $label . "\n"; } + } else { + $body .= " (none selected)\n"; + } + $body .= "\nMessage:\n"; + $body .= $message !== '' ? $message . "\n" : "(no message provided)\n"; + + $headers = [ + 'Content-Type: text/plain; charset=UTF-8', + 'Reply-To: ' . sanitize_email($user->user_email), + ]; + + $sent = wp_mail($to, $subject, $body, $headers); + + if (!$sent) { + wp_send_json_error('Email could not be sent. Check the site mail configuration.', 500); + } + + wp_send_json_success(['delivered_to' => $to]); +} + // Persist a user's dismissal of the "No active/completed notes found" // empty-state notice so it doesn't reappear on the next page load. // Triggered by inline JS in wp_notes_add_inline_scripts() when the