{"id":373418,"date":"2026-09-24T00:20:18","date_gmt":"2026-09-24T00:20:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/fonx-delete-user\/"},"modified":"2026-09-24T00:15:42","modified_gmt":"2026-09-24T00:15:42","slug":"fonx-delete-user","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/fonx-delete-user\/","author":15978607,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"trunk","tested":"7.1.2","requires":"6.7","requires_php":"8.2","requires_plugins":null,"header_name":"FonX Delete User","header_author":"Tabi Idris","header_description":"Customizable user deletion via external API or WordPress database, with form builder, shortcode and block injection.","assets_banners_color":"5e748e","last_updated":"2026-09-24 00:15:42","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/profiles.wordpress.org\/drizy","rating":0,"author_block_rating":0,"active_installs":0,"downloads":44,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"drizy","date":"2026-09-24 00:20:08","revision":3710342}},"upgrade_notice":{"1.1.0":"<p>All option keys, tables, transients, hooks, the REST namespace, shortcode tag, and admin slugs now use the <code>fxldu<\/code> prefix. Existing installs are not migrated and must be re-configured after upgrading; remove leftover <code>du_*<\/code> options and tables manually before reuse.<\/p>","1.0.4":"<p>Review-round hardening: enqueued config scripts, sanitized API headers, secured and rate-limited the external-request proxy, implemented the fxldu_rate_limit filter, moved file logs under uploads, and scoped uninstall cleanup. No migration is required.<\/p>","1.0.3":"<p>Upgrading renames the requests page title and adds a status filter to the requests list table. Existing settings are preserved; no migration is required.<\/p>","1.0.2":"<p>Upgrading adds the Email settings tab and the Show Form option. Existing settings are preserved; no migration is required.<\/p>"},"ratings":[],"assets_icons":{"icon-256x256.png":{"filename":"icon-256x256.png","revision":3710337,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3710337,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3710337,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":{"fxldu\/form":{"apiVersion":3,"name":"fxldu\/form","version":"1.0.0","title":"Delete User Form","category":"widgets","icon":"admin-users","description":"Renders the configurable account deletion form.","textdomain":"fonx-delete-user","attributes":{"pageId":{"type":"string","default":""}},"supports":{"html":false,"multiple":true},"editorScript":"file:.\/index.js","render":"file:.\/render.php"}},"tagged_versions":["1.0.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3710337,"resolution":"1","location":"assets","locale":"","width":1197,"height":674},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3710337,"resolution":"2","location":"assets","locale":"","width":1197,"height":674},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3710337,"resolution":"3","location":"assets","locale":"","width":1197,"height":674},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3710337,"resolution":"4","location":"assets","locale":"","width":1197,"height":674},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3710337,"resolution":"5","location":"assets","locale":"","width":1197,"height":674},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3710337,"resolution":"6","location":"assets","locale":"","width":1197,"height":674},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3710337,"resolution":"7","location":"assets","locale":"","width":1197,"height":674},"screenshot-8.png":{"filename":"screenshot-8.png","revision":3710337,"resolution":"8","location":"assets","locale":"","width":1197,"height":674},"screenshot-9.png":{"filename":"screenshot-9.png","revision":3710337,"resolution":"9","location":"assets","locale":"","width":1197,"height":674}},"screenshots":{"1":"Settings \u2014 General: choose the deletion mode, security limits, and frontend messaging.","2":"Settings \u2014 Integration: connect an existing form with a CSS selector and data-fxldu-field mapping.","3":"Settings \u2014 External API: configure the external endpoint, method, authentication, and header forwarding.","4":"Custom Fields \u2014 define the fields visitors submit alongside their email.","5":"Frontend form \u2014 the account deletion form as visitors see it.","6":"Delete Requests \u2014 review the queue and approve, reject, or delete each request.","7":"Audit Logs \u2014 lifecycle events recorded for every request.","8":"Settings \u2014 Email \u2014 admin alerts and the requester acknowledgement email.","9":"Log viewer \u2014 browse recorded log entries in the admin."}},"plugin_section":[],"plugin_tags":[282376,36350,2253,131785,2461],"plugin_category":[],"plugin_contributors":[282377],"plugin_business_model":[],"class_list":["post-373418","plugin","type-plugin","status-publish","hentry","plugin_tags-account-deletion","plugin_tags-delete-user","plugin_tags-form-builder","plugin_tags-gdpr","plugin_tags-user-management","plugin_contributors-drizy","plugin_committers-drizy"],"banners":{"banner":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/banner-772x250.png?rev=3710337","banner_2x":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/banner-1544x500.png?rev=3710337","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/icon-256x256.png?rev=3710337","icon_2x":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/icon-256x256.png?rev=3710337","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-1.png?rev=3710337","caption":"Settings \u2014 General: choose the deletion mode, security limits, and frontend messaging."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-2.png?rev=3710337","caption":"Settings \u2014 Integration: connect an existing form with a CSS selector and data-fxldu-field mapping."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-3.png?rev=3710337","caption":"Settings \u2014 External API: configure the external endpoint, method, authentication, and header forwarding."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-4.png?rev=3710337","caption":"Custom Fields \u2014 define the fields visitors submit alongside their email."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-5.png?rev=3710337","caption":"Frontend form \u2014 the account deletion form as visitors see it."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-6.png?rev=3710337","caption":"Delete Requests \u2014 review the queue and approve, reject, or delete each request."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-7.png?rev=3710337","caption":"Audit Logs \u2014 lifecycle events recorded for every request."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-8.png?rev=3710337","caption":"Settings \u2014 Email \u2014 admin alerts and the requester acknowledgement email."},{"src":"https:\/\/ps.w.org\/fonx-delete-user\/assets\/screenshot-9.png?rev=3710337","caption":"Log viewer \u2014 browse recorded log entries in the admin."}],"raw_content":"<!--section=description-->\n<p>FonX Delete User turns account erasure into a controlled, reviewed process instead of a one-click auto-delete.<\/p>\n\n<p>Most \"delete my account\" plugins force visitors into their own button or form. FonX Delete User takes the opposite approach: you keep the form you already have and the plugin quietly powers it in whichever way fits your site.<\/p>\n\n<h4>What makes it different<\/h4>\n\n<ul>\n<li>Bring-your-own-form \u2014 integrate any existing HTML form on your site with a CSS selector and data-fxldu-field mapping. The form you styled stays the form your visitors see; the plugin's JavaScript attaches to it without blocking its own handler.<\/li>\n<li>Config-only mode \u2014 toggle the plugin's own markup off entirely. The shortcode or block still emits the per-site request token and configuration, so an existing form can drive submissions with nothing extra rendered.<\/li>\n<li>Admin approval pipeline \u2014 nothing is deleted automatically. Every request waits in the admin queue for you to approve, reject, or delete it.<\/li>\n<li>External API mode \u2014 instead of deleting inside WordPress, forward erasure submissions to your own endpoint for the external system to complete.<\/li>\n<li>Audit logging \u2014 every request and outcome is recorded in the database or a file, with a built-in viewer.<\/li>\n<\/ul>\n\n<h4>Features<\/h4>\n\n<ul>\n<li>Dual deletion modes \u2014 store requests pending admin approval (wordpress_db) or forward submissions immediately to an external endpoint (external_api).<\/li>\n<li>Form builder \u2014 add text, email, password, textarea, checkbox, select, and hidden fields with required toggles, placeholders, options, and validation regexes.<\/li>\n<li>Flexible injection \u2014 the [fxldu_form] shortcode, a native Gutenberg block, or integration with an existing page form via CSS selector and data-* field mapping.<\/li>\n<li>Email notifications \u2014 alert the administrator (or a custom admin email) about new requests, and acknowledge the requester by email with configurable subject\/body and placeholders.<\/li>\n<li>Security hardening \u2014 honeypot anti-bot field, per-site HMAC-compatible request tokens, per-IP rate limiting, capability-gated admin endpoints, prepared SQL, and fully escaped output.<\/li>\n<\/ul>\n\n<h4>Submission flow<\/h4>\n\n<ol>\n<li>A visitor submits the form (shortcode, block, or an integrated existing form).<\/li>\n<li>The frontend checks the honeypot, validates required fields, and posts a JSON request containing a per-site token, the user email, and the collected fields.<\/li>\n<li>The request is stored with status pending (rate-limited per IP).<\/li>\n<li>The administrator is notified; the requester can also receive an acknowledgement email.<\/li>\n<li>On approval, the user is permanently deleted via wp_delete_user() (WordPress Database mode) or the request is marked approved for the external system (External API mode). On rejection, the request is marked rejected and no deletion happens.<\/li>\n<li>The requester is notified of the outcome.<\/li>\n<\/ol>\n\n<h3>Additional Information<\/h3>\n\n<p>This release addresses the points raised during the plugin review and hardens two runtime paths found while testing. Details of what changed and why:<\/p>\n\n<ul>\n<li>Global prefix \u2014 every global identifier now uses the unique <code>fxldu<\/code> prefix instead of the generic <code>du_<\/code> \/ <code>delete_user_<\/code> names: option keys, database tables, transients, the REST namespace (<code>fxldu\/v1<\/code>), action\/filter names, nonces, admin menu slugs, the <code>[fxldu_form]<\/code> shortcode, the <code>fxldu\/form<\/code> block, enqueue handles, JavaScript globals, and frontend CSS classes. PHP constants are <code>FXLDU_*<\/code> and the class namespace is <code>Fxldu\\<\/code>. The text domain <code>fonx-delete-user<\/code> and the plugin folder name are unchanged.<\/li>\n<li>Database cleanup \u2014 the admin list tables (Fields, Requests, Logs) and the REST listing endpoint no longer pass an empty argument list to <code>$wpdb-&gt;prepare()<\/code> when no filter or search is applied; in that case the static count query runs directly instead of emitting the \"the query argument must have a placeholder\" notice.<\/li>\n<li>Existing-form integration \u2014 when the integration selector matches a non-form container (some themes wrap their form in a div that is matched instead of the <code>&lt;form&gt;<\/code> itself), field collection and email resolution now read the named inputs directly instead of throwing inside <code>new FormData()<\/code>. The window integration-guard is <code>__fxlduIntegrationBound<\/code>.<\/li>\n<li>Script output \u2014 the frontend form configuration is no longer printed as raw inline <code>&lt;script&gt;<\/code> tags. The shortcode and block register the <code>fxldu-frontend<\/code> script through <code>wp_enqueue_script()<\/code> and inject the config object with <code>wp_add_inline_script( 'fxldu-frontend', $js, 'before' )<\/code>. The unused <code>print_frontend_config()<\/code> method was removed from the main plugin file, and no <code>&lt;script&gt;<\/code> or <code>&lt;style&gt;<\/code> strings remain in the plugin source.<\/li>\n<li>Sanitization of custom headers \u2014 the admin live-test of the API connection now passes the custom headers through <code>sanitize_textarea_field( wp_unslash( $_POST['api_headers'] ) )<\/code> before building the request, matching the sanitization applied when the headers are saved.<\/li>\n<li>Contributors \u2014 the <code>Contributors:<\/code> header now uses the plugin owner's WordPress.org username.<\/li>\n<li>Log file location \u2014 file logging no longer writes to a hard-coded <code>WP_CONTENT_DIR<\/code> path. Logs now go to a plugin-specific subfolder under the uploads directory (<code>{uploads}\/fxldu\/fxldu-logs.log<\/code>), created with <code>wp_mkdir_p()<\/code>, and each line is appended with <code>error_log( $line, 3, $log_path )<\/code>.<\/li>\n<li>External-request REST proxy \u2014 the <code>\/external-request<\/code> route remains open for anonymous form submissions but is now hardened: it keeps the honeypot check and the per-site token verification, strips internal keys from the forwarded payload, fails closed with HTTP 403 unless External API mode is active, and is rate-limited per IP (its own transient counter so a normal submission still counts only once per endpoint leg).<\/li>\n<li>Uninstall cleanup \u2014 the broad <code>option_name LIKE<\/code> query (which could delete options owned by other plugins sharing the old prefix) and a usermeta <code>LIKE<\/code> cleanup (the plugin writes no user meta) were removed. <code>uninstall.php<\/code> now deletes an explicit list of the 26 options owned by this plugin plus the three plugin-owned database tables.<\/li>\n<li>Rate-limit filter \u2014 the <code>fxldu_rate_limit<\/code> filter documented in the FAQ is now actually applied. Both the <code>\/request<\/code> and <code>\/external-request<\/code> endpoints enforce their per-IP limit through a shared helper whose default is <code>max( 1, Options::get_rate_limit() )<\/code> and which honors <code>apply_filters( 'fxldu_rate_limit', $limit, $ip )<\/code>.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the fonx-delete-user folder to \/wp-content\/plugins\/.<\/li>\n<li>Activate the plugin through the Plugins screen.<\/li>\n<li>Activation creates three database tables \u2014 {prefix}fxldu_fields, {prefix}fxldu_requests, and {prefix}fxldu_logs \u2014 and seeds the default options.<\/li>\n<li>Configure it under FonX Delete User \u2192 Settings, then add fields under Fields.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"how%20do%20i%20put%20the%20form%20on%20a%20page%3F\"><h3>How do I put the form on a page?<\/h3><\/dt>\n<dd><p>Use the [fxldu_form] shortcode anywhere in post content or a widget, or insert the Delete User Form block in the block editor. Both render server-side, so they always reflect your current fields and settings.<\/p><\/dd>\n<dt id=\"how%20do%20i%20use%20my%20own%20existing%20form%20instead%20of%20the%20plugin%27s%20form%3F\"><h3>How do I use my own existing form instead of the plugin's form?<\/h3><\/dt>\n<dd><p>Enable Integration under Settings, set the CSS selector for your form, and add data-fxldu-field attributes to the inputs you want mapped. Keep the shortcode or block on the same page (it supplies the request token and configuration) and, if you do not want the plugin's form visible, uncheck Show Form in the Form settings tab \u2014 only the configuration script is emitted.<\/p><\/dd>\n<dt id=\"how%20is%20the%20user%20identified%3F\"><h3>How is the user identified?<\/h3><\/dt>\n<dd><p>The plugin resolves the submitter by email. Add an email-type field to the form (or name a field user_email or email). Requests without a valid email are rejected.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20the%20user%27s%20account%3F\"><h3>What happens to the user's account?<\/h3><\/dt>\n<dd><p>In WordPress Database mode the account is deleted only after an administrator approves the pending request. In External API mode the submission is forwarded to your endpoint immediately, and the external system is responsible for erasure.<\/p><\/dd>\n<dt id=\"how%20does%20the%20plugin%20stop%20spam%3F\"><h3>How does the plugin stop spam?<\/h3><\/dt>\n<dd><p>A hidden honeypot field silently discards automated submissions, every request must carry a valid per-site token, and each IP is rate-limited (default 5 requests\/hour). The limit is adjustable via the fxldu_rate_limit filter.<\/p><\/dd>\n<dt id=\"what%20emails%20does%20the%20plugin%20send%3F\"><h3>What emails does the plugin send?<\/h3><\/dt>\n<dd><p>The administrator receives a new-request alert (to the admin email configured in the Email settings tab, falling back to the site admin email). The requester can receive an acknowledgement email on submission and is notified on approval, rejection, and completion. Subject and body for the acknowledgement are configurable and support {{site_name}}, {{user_email}}, and {{request_id}}.<\/p><\/dd>\n<dt id=\"is%20there%20an%20api%3F\"><h3>Is there an API?<\/h3><\/dt>\n<dd><p>Yes. POST \/wp-json\/fxldu\/v1\/request accepts deletion requests (public, token-verified). Administrators can use GET \/wp-json\/fxldu\/v1\/requests, POST \/requests\/{id}\/approve, and POST \/requests\/{id}\/reject.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Renamed every global prefix to the unique <code>fxldu<\/code> prefix to prevent conflicts with other plugins: option keys, database tables, transients, the REST namespace (<code>fxldu\/v1<\/code>), action\/filter names, nonces, the admin menu slugs, the <code>[fxldu_form]<\/code> shortcode, the <code>fxldu\/form<\/code> block, enqueue handles, JavaScript globals, and frontend CSS classes.<\/li>\n<li>PHP constants are now <code>FXLDU_*<\/code> and the class namespace is <code>Fxldu\\<\/code>.<\/li>\n<li>Fixed a \"wpdb::prepare was called incorrectly\" notice on the Fields, Requests, and Logs admin pages and on the REST listing endpoint when no filter or search is applied \u2014 count queries without placeholders no longer go through prepare().<\/li>\n<li>Existing-form integration now tolerates a non-form container as the integration target (e.g. a theme wrapper div): field collection and email resolution read the named inputs directly instead of failing inside new FormData().<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Frontend configuration is now output through the script loader (wp_add_inline_script) instead of raw inline script tags.<\/li>\n<li>Custom API headers are sanitized before test-connection and outbound requests.<\/li>\n<li>The external-request REST proxy is only active in External API mode and is rate-limited per IP.<\/li>\n<li>Implemented the documented fxldu_rate_limit filter for adjusting the per-IP request limit.<\/li>\n<li>File logs now write to the uploads directory (a plugin-specific subfolder) instead of wp-content.<\/li>\n<li>Uninstall removes only this plugin's own options, not any option whose name contains \"fxldu_\".<\/li>\n<li>Corrected the Contributors list to the plugin owner's WordPress.org username.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Renamed the admin page title from \"Deletion Requests\" to \"Delete Requests\".<\/li>\n<li>Added a status filter dropdown (All Statuses \/ Pending \/ Completed \/ Rejected) with a Filter button to the requests list table.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>External API submissions now record server-side and prefer the plugin's own success message.<\/li>\n<li>Added a Show Form option so the shortcode\/block can emit configuration only (for existing-form integration).<\/li>\n<li>Added an Email settings tab for the admin recipient and a configurable requester acknowledgement email.<\/li>\n<li>Frontend success\/error messages now always come from the General settings.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Fixed double rendering on the settings page.<\/li>\n<li>Fixed stale JavaScript state on the settings page.<\/li>\n<li>Improved external API request handling and success-message fallbacks.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release: dual deletion modes, form builder, shortcode and block injection, existing-form integration, admin review pipeline, audit logging, email notifications, and guest-form security.<\/li>\n<\/ul>","raw_excerpt":"Bring-your-own-form account deletion: integrate any existing page form, with admin approval, external API forwarding, and audit logs.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/373418","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=373418"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/drizy"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=373418"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=373418"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=373418"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=373418"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=373418"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=373418"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}