From bc7d6986a8f384491d6c7986bfbd5fd14c5aa7c4 Mon Sep 17 00:00:00 2001 From: David Keane Date: Mon, 25 May 2026 08:13:52 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Leave=20Feedback=20form=20=E2=80=94=20m?= =?UTF-8?q?ulti-select=20checkboxes=20+=20actually-working=20submit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The right-column "Leave Feedback" form on the About page has been expanded from two radio buttons (user picked one of two) to seven checkboxes (user can pick any), plus an optional message textarea. The submit button — which previously called toggleSection() on element IDs that never existed and did nothing — now AJAX-posts to a new server-side 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 - Client-side: at least one checkbox OR a message is required; an inline hint shows otherwise. - AJAX POST wp_notes_submit_feedback with topics[] + message + nonce. - Server handler (manage_options capability + nonce checked) sanitizes input, allow-lists the seven topic keys, builds a plain-text email body (sender + site URL + plugin version + checked topics with pretty labels + message), and ships it to get_option('admin_email') via wp_mail() with Reply-To set to the submitting user. - Inline success message replaces the form on success; inline error lets the user retry on failure. The toggleSection() helper that the old broken handler used is kept defined for now — it's no longer referenced anywhere on the About page, so it's flagged in the changelog for a future Tier-2 removal pass. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 38 ++++++++++++++++++++++ inc/wp-notes-about.php | 72 ++++++++++++++++++++++++++++++++++++------ wp-notes.php | 68 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 9 deletions(-) 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