{"id":367845,"date":"2026-09-18T21:54:18","date_gmt":"2026-09-18T21:54:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/postpurge-bulk-delete-posts\/"},"modified":"2026-09-18T21:53:48","modified_gmt":"2026-09-18T21:53:48","slug":"postpurge-bulk-delete-posts","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/postpurge-bulk-delete-posts\/","author":23217191,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.18","stable_tag":"1.0.18","tested":"7.1.1","requires":"5.8","requires_php":"7.4","requires_plugins":null,"header_name":"PostPurge \u2013 Bulk Delete Posts","header_author":"onodev77","header_description":"Deletes WordPress posts and pages, with optional removal of linked media.","assets_banners_color":"","last_updated":"2026-09-18 21:53:48","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":41,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.18":{"tag":"1.0.18","author":"onodev77","date":"2026-09-18 21:53:48","revision":3702800}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3703014,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128}},"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.18"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3702800,"resolution":"1","location":"assets","locale":"","width":769,"height":659}},"screenshots":{"1":"Deleting posts process"}},"plugin_section":[],"plugin_tags":[14810,3786,1487,18097,166],"plugin_category":[],"plugin_contributors":[237523],"plugin_business_model":[],"class_list":["post-367845","plugin","type-plugin","status-publish","hentry","plugin_tags-bulk-delete","plugin_tags-cleanup","plugin_tags-custom-post-types","plugin_tags-delete-posts","plugin_tags-posts","plugin_contributors-onodev77","plugin_committers-onodev77"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/postpurge-bulk-delete-posts\/assets\/icon-128x128.png?rev=3703014","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/postpurge-bulk-delete-posts\/assets\/screenshot-1.png?rev=3702800","caption":"Deleting posts process"}],"raw_content":"<!--section=description-->\n<p>PostPurge \u2013 Bulk Delete Posts is designed for reliable cleanup of both small and very large WordPress sites.<\/p>\n\n<p>Unlike a single bulk database operation, the plugin processes exactly one content item per AJAX request. The administrator can follow every item in a live activity window, pause the operation, resume it later or stop it. Progress is saved for the current administrator.<\/p>\n\n<h4>Main features<\/h4>\n\n<ul>\n<li>Exact result count before any operation.<\/li>\n<li>Preview table with a sample of matched content.<\/li>\n<li>Quick test mode that checks up to 10 matches without changing content.<\/li>\n<li>Optional media test showing each linked file as deletable, shared\/protected or unavailable.<\/li>\n<li>Real deletion to Trash by default, with optional permanent deletion and an extra confirmation.<\/li>\n<li>One post per request to reduce timeouts and peak server load.<\/li>\n<li>Pause, resume and stop controls.<\/li>\n<li>Automatic recovery of unfinished jobs after reloading the page.<\/li>\n<li>Filters can be combined using AND logic.<\/li>\n<li>Posts, pages and registered custom post types.<\/li>\n<li>Published, draft, pending, private, scheduled and Trash statuses.<\/li>\n<li>Date range or content older than a selected number of days.<\/li>\n<li>Categories, tags and registered custom taxonomies by term slug.<\/li>\n<li>Comment-count range.<\/li>\n<li>View-count range using the views column registered by the site\u2019s active statistics plugin.<\/li>\n<li>URL, slug, numeric post ID, title and WordPress keyword search.<\/li>\n<li>Custom fields with equals, differs, contains, existence and numeric operators.<\/li>\n<li>Author, featured-image, include-ID and exclude-ID filters.<\/li>\n<li>Six built-in interface languages: English, Italian, Spanish, French, German and Portuguese.<\/li>\n<li>Automatic site-language selection with a manual language switcher.<\/li>\n<li>Optional deletion of media attached exclusively to permanently deleted posts, with shared-media protection.<\/li>\n<li>Media paths must be verified inside the local WordPress uploads directory; unsafe, non-local and symlinked paths are kept.<\/li>\n<li>Permanent expandable audit log, paginated in groups of 50 entries.<\/li>\n<li>Local-only media existence verification with no HTTP checks.<\/li>\n<\/ul>\n\n<h3>Safe workflow<\/h3>\n\n<ol>\n<li>Choose the content types and statuses, then add any optional filters.<\/li>\n<li>Click Preview results and verify the exact count and sample.<\/li>\n<li>Click Quick test (10), choose whether to verify linked media, and inspect the detailed checks.<\/li>\n<li>Start the real deletion and keep Move to Trash selected when possible.<\/li>\n<li>Watch the activity log, or pause and resume later.<\/li>\n<\/ol>\n\n<!--section=installation-->\n<ol>\n<li>In WordPress, open Plugins &gt; Add New &gt; Upload Plugin.<\/li>\n<li>Select the ZIP file and click Install Now.<\/li>\n<li>Activate the plugin.<\/li>\n<li>Open PostPurge \u2013 Bulk Delete Posts from the main WordPress admin sidebar.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20plugin%20run%20a%20large%20delete%20query%3F\"><h3>Does the plugin run a large DELETE query?<\/h3><\/dt>\n<dd><p>No. It deliberately resolves and processes one post per HTTP request. This lowers peak load and makes progress visible and controllable.<\/p><\/dd>\n<dt id=\"can%20i%20recover%20deleted%20posts%3F\"><h3>Can I recover deleted posts?<\/h3><\/dt>\n<dd><p>Items moved to Trash can normally be restored from WordPress. Permanent deletion cannot be undone and requires an additional confirmation.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20i%20close%20the%20browser%3F\"><h3>What happens if I close the browser?<\/h3><\/dt>\n<dd><p>The current progress is stored for your administrator account. Return to PostPurge \u2013 Bulk Delete Posts in the main admin sidebar and continue the unfinished operation.<\/p><\/dd>\n<dt id=\"does%20it%20support%20custom%20post%20types%20and%20custom%20taxonomies%3F\"><h3>Does it support custom post types and custom taxonomies?<\/h3><\/dt>\n<dd><p>Yes, when they are registered with a WordPress admin interface.<\/p><\/dd>\n<dt id=\"how%20are%20views%20detected%3F\"><h3>How are views detected?<\/h3><\/dt>\n<dd><p>The plugin checks the columns registered in the WordPress Posts screen and shows their actual admin labels. Known database sources are filtered directly; other plugins are handled through the value generated by their registered admin column. If no views column exists, the views filter is disabled.<\/p><\/dd>\n<dt id=\"can%20media%20deletion%20remove%20arbitrary%20server%20files%3F\"><h3>Can media deletion remove arbitrary server files?<\/h3><\/dt>\n<dd><p>PostPurge does not directly unlink files or remove directories. It only asks WordPress to delete attachment records that were direct children of the deleted post, after verifying every known attachment path is a regular path inside the local uploads directory. Non-local, shared, symlinked or unverifiable media are kept and logged.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.18<\/h4>\n\n<ul>\n<li>Replaced the transient check-and-set with an atomic per-user processing lock.<\/li>\n<li>Validates the current job sequence only after the lock is acquired.<\/li>\n<li>Uses ownership tokens, atomic stale-lock takeover and conditional release, with shutdown cleanup.<\/li>\n<\/ul>\n\n<h4>1.0.17<\/h4>\n\n<ul>\n<li>Reads both WordPress admin-column hooks used by posts, pages and custom post types.<\/li>\n<li>Prefers a valid non-zero value when the same statistics column is registered on both hooks.<\/li>\n<li>Shows an unavailable value instead of a misleading zero when a column cannot be evaluated.<\/li>\n<li>Keeps output buffering isolated and guaranteed to close for every individual hook callback.<\/li>\n<\/ul>\n\n<h4>1.0.16<\/h4>\n\n<ul>\n<li>Fixed view filters for admin columns whose callbacks depend on the current global post.<\/li>\n<li>Preserves case-sensitive admin-column keys registered by themes and statistics plugins.<\/li>\n<li>Refreshes detected view sources after the compatibility update.<\/li>\n<\/ul>\n\n<h4>1.0.15<\/h4>\n\n<ul>\n<li>Adopted the unique postpurge_bdp prefix for classes, constants, AJAX actions, options and stored data.<\/li>\n<li>Sanitizes the submitted JSON filter payload before decoding and validates all decoded values.<\/li>\n<li>Guarantees closure of the temporary admin-column output buffer even if a third-party hook fails.<\/li>\n<li>Places nonce verification directly at the start of every AJAX endpoint.<\/li>\n<li>Reworked prepared database queries and documented the few safe, intentional direct queries.<\/li>\n<li>Removed the unused translation domain-path header.<\/li>\n<\/ul>\n\n<h4>1.0.14<\/h4>\n\n<ul>\n<li>Added a visible [OPTIONAL] prefix to the linked-media deletion option in every supported language.<\/li>\n<\/ul>\n\n<h4>1.0.13<\/h4>\n\n<ul>\n<li>Protected media now report the exact reason and reference that caused them to be kept.<\/li>\n<li>Logs identify the attachment ID and link to the post that reuses it when available.<\/li>\n<li>Added localized explanations for content, custom fields, galleries, taxonomy terms, site settings, logos and icons.<\/li>\n<\/ul>\n\n<h4>1.0.12<\/h4>\n\n<ul>\n<li>Fixed false shared-media detections caused by attachment metadata and broad option\/cache scans.<\/li>\n<li>Shared-media checks now focus on other active content, galleries, taxonomies and relevant display settings.<\/li>\n<li>Displays every media outcome as a separate activity-log row.<\/li>\n<li>Added a Not contain condition to the Title filter.<\/li>\n<\/ul>\n\n<h4>1.0.11<\/h4>\n\n<ul>\n<li>Warns that optional media deletion takes longer because of additional safety checks.<\/li>\n<li>Requires a recent preview with the exact same filters before a test or deletion can start.<\/li>\n<li>Restricts media removal to direct child attachments with verified paths inside the WordPress uploads directory.<\/li>\n<li>Conservatively keeps non-local, shared, symlinked or otherwise unverifiable media and records the reason.<\/li>\n<\/ul>\n\n<h4>1.0.10<\/h4>\n\n<ul>\n<li>Added a distinctive vector PostPurge logo to the admin header.<\/li>\n<li>Corrected vertical alignment of icons inside action buttons.<\/li>\n<\/ul>\n\n<h4>1.0.9<\/h4>\n\n<ul>\n<li>Renamed the plugin and slug to PostPurge \u2013 Bulk Delete Posts.<\/li>\n<li>Updated the short description and WordPress.org metadata.<\/li>\n<\/ul>\n\n<h4>1.0.8<\/h4>\n\n<ul>\n<li>Added a permanent, expandable, paginated activity log for previews, tests, job controls and item results.<\/li>\n<li>Media verification now uses local filesystem checks only, with no HTTP requests.<\/li>\n<li>Distinguishes verified deletion, files already missing, files still present, protected files and non-local media.<\/li>\n<\/ul>\n\n<h4>1.0.7<\/h4>\n\n<ul>\n<li>Made date range and older-than filters mutually exclusive in both the interface and server validation.<\/li>\n<li>Added the original URL and outcome for every deleted or protected media file in the activity log.<\/li>\n<li>Clarified the preview sample size as shown rows versus total matches.<\/li>\n<li>Queries now include all languages by default with Polylang and WPML compatibility.<\/li>\n<\/ul>\n\n<h4>1.0.6<\/h4>\n\n<ul>\n<li>Fixed publication date range filtering with explicit site-timezone boundaries.<\/li>\n<li>Fixed the \"older than X days\" filter and made it combine safely with date ranges.<\/li>\n<li>Added validation for reversed date ranges.<\/li>\n<li>Isolated plugin SQL conditions from unrelated WordPress queries.<\/li>\n<\/ul>\n\n<h4>1.0.5<\/h4>\n\n<ul>\n<li>Reduced the quick-test sample from 20 to 10 posts.<\/li>\n<li>Closing an active progress window now stops the current operation.<\/li>\n<li>Added an optional maximum number of posts to delete in one operation.<\/li>\n<\/ul>\n\n<h4>1.0.4<\/h4>\n\n<ul>\n<li>Removed the empty placeholder space after loading a preview.<\/li>\n<li>Replaced the full safe test with a fast test limited to the first 20 matches.<\/li>\n<li>Added a test setup dialog with optional linked-media verification.<\/li>\n<li>Added per-post database and per-file media diagnostics to the activity log.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<ul>\n<li>Made views-column detection independent of a specific statistics plugin.<\/li>\n<li>Uses the exact label registered in the WordPress Posts screen.<\/li>\n<li>Added direct meta-field mapping and a generic admin-column adapter.<\/li>\n<li>Disables views filtering when no compatible admin column exists.<\/li>\n<li>Removed the fixed Post Views explanatory text.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Views now match the Post Views admin column and total-view calculation.<\/li>\n<li>Removed guessed view-field choices.<\/li>\n<li>Clarified the WordPress text search and added a custom-field example.<\/li>\n<li>Added protected deletion of media used exclusively by permanently deleted posts.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Added a top-level Bulk Delete Post admin menu.<\/li>\n<li>Added automatic detection of the real Post Views admin data.<\/li>\n<li>Removed the all-content safety checkbox.<\/li>\n<li>Improved sequential processing speed by removing the delay between requests and caching source detection.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"Deletes WordPress posts and pages, with optional removal of linked media.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/367845","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=367845"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/onodev77"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=367845"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=367845"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=367845"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=367845"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=367845"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=367845"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}