{"id":301337,"date":"2026-05-05T20:17:05","date_gmt":"2026-05-05T20:17:05","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/smart-content-expiry-manager\/"},"modified":"2026-05-09T03:58:44","modified_gmt":"2026-05-09T03:58:44","slug":"raptor-scheduled-content-expiry","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/raptor-scheduled-content-expiry\/","author":14711667,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.9.1","stable_tag":"1.9.1","tested":"7.0","requires":"6.4","requires_php":"7.4","requires_plugins":null,"header_name":"Raptor Scheduled Content Expiry","header_author":"Amr Issa","header_description":"Schedule content expiry with auto-unpublish, redirect, content swap, and logging for posts, pages, and custom post types.","assets_banners_color":"536d9b","last_updated":"2026-05-09 03:58:44","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/www.linkedin.com\/in\/amr-issa\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":115,"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":"Amr267","date":"2026-05-05 20:15:31"},"1.9.0":{"tag":"1.9.0","author":"Amr267","date":"2026-05-09 03:34:43"},"1.9.1":{"tag":"1.9.1","author":"Amr267","date":"2026-05-09 03:58:44"}},"upgrade_notice":{"1.9.1":"<p>Fixes a stuck row on the Scheduled Actions screen after clicking &quot;Run now&quot;. Recommended for all 1.9.0 users.<\/p>","1.9.0":"<p>Critical scheduling fixes (Action Scheduler now loads correctly, timezone math is right for sites configured by UTC offset), full WooCommerce product and custom post type support, top-level admin menu, and a new focused Scheduled Actions queue page. Recommended for all users \u2014 if you set expiry rules on 1.0.0 and they never fired, re-save the affected posts after updating.<\/p>","1.0.0":"<p>Initial release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3523801,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3523801,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-772x250.png":{"filename":"banner-772x250.png","revision":3526969,"resolution":"772x250","location":"assets","locale":"","width":1584,"height":513}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.9.0","1.9.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3526969,"resolution":"1","location":"assets","locale":"","width":2880,"height":1800},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3526969,"resolution":"2","location":"assets","locale":"","width":2880,"height":1800},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3526969,"resolution":"3","location":"assets","locale":"","width":2880,"height":1800},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3526969,"resolution":"4","location":"assets","locale":"","width":2880,"height":1800}},"screenshots":{"1":"The Content Expiry meta box in the post editor.","2":"Top-level Content Expiry menu with Settings and Scheduled Actions.","3":"Custom Scheduled Actions queue with Run Now \/ Cancel controls.","4":"Audit log showing expiry events with timestamps and status.","5":"Dashboard widget showing upcoming expirations in the next 7 days.","6":"Bulk Edit interface for setting or clearing expiry dates."}},"plugin_section":[262246],"plugin_tags":[23987,56514,242017,727,50254],"plugin_category":[],"plugin_contributors":[252221],"plugin_business_model":[],"class_list":["post-301337","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-auto-unpublish","plugin_tags-content-expiry","plugin_tags-content-scheduling","plugin_tags-redirect","plugin_tags-schedule-posts","plugin_contributors-amr267","plugin_committers-amr267"],"banners":{"banner":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/banner-772x250.png?rev=3526969","banner_2x":false,"banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/icon-128x128.png?rev=3523801","icon_2x":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/icon-256x256.png?rev=3523801","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/screenshot-1.png?rev=3526969","caption":"The Content Expiry meta box in the post editor."},{"src":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/screenshot-2.png?rev=3526969","caption":"Top-level Content Expiry menu with Settings and Scheduled Actions."},{"src":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/screenshot-3.png?rev=3526969","caption":"Custom Scheduled Actions queue with Run Now \/ Cancel controls."},{"src":"https:\/\/ps.w.org\/raptor-scheduled-content-expiry\/assets\/screenshot-4.png?rev=3526969","caption":"Audit log showing expiry events with timestamps and status."}],"raw_content":"<!--section=description-->\n<p>Raptor Scheduled Content Expiry gives you full control over what happens to your content when it reaches its expiration date. Set precise expiry dates on posts, pages, and custom post types, then choose from multiple expiry actions: auto-unpublish (move to draft), redirect to any URL, swap the content with a replacement, or simply hide it from visitors.<\/p>\n\n<p><strong>What You Can Do (Free)<\/strong><\/p>\n\n<ul>\n<li>Set expiry dates and times on any post, page, public custom post type, or WooCommerce product<\/li>\n<li>Choose an expiry action per post: move to draft, redirect (301 or 302), swap content, or hide from archive\/search<\/li>\n<li>Content swap lets you prepare a replacement message, archived notice, or seasonal placeholder<\/li>\n<li>Auto-redirect expired URLs to any destination so visitors never hit a dead page<\/li>\n<li>Role-based access control so only Administrators or Editors can manage expiry rules<\/li>\n<li>Timezone-aware scheduling that respects your WordPress timezone setting<\/li>\n<li>Audit log showing every expiry event with timestamp, action taken, and which user configured it<\/li>\n<li>Quick Edit and Bulk Edit support for setting or clearing expiry dates across multiple posts<\/li>\n<li>Dashboard widget showing upcoming expirations in the next 7 days<\/li>\n<li>Clean uninstall that removes all plugin data from your database when you delete the plugin<\/li>\n<li>Runs on Action Scheduler for reliable, precise execution<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin files to <code>\/wp-content\/plugins\/raptor-scheduled-content-expiry<\/code>, or install through the WordPress Plugins screen.<\/li>\n<li>Activate the plugin through the 'Plugins' screen in WordPress.<\/li>\n<li>Go to <strong>Content Expiry &gt; Settings<\/strong> in the admin sidebar to configure defaults and choose which post types to enable.<\/li>\n<li>Edit any post, page, custom post type, or WooCommerce product, and find the <strong>Content Expiry<\/strong> meta box to set expiry rules.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20do%20i%20find%20the%20plugin%3F\"><h3>Where do I find the plugin?<\/h3><\/dt>\n<dd><p>After activation, look for <strong>Content Expiry<\/strong> as a top-level item in the WordPress admin sidebar (clock icon). It contains two screens:<\/p>\n\n<ul>\n<li><strong>Settings<\/strong> \u2014 defaults, post-type opt-in, capability gate, dashboard widget toggle, log retention, and the Audit Log tab.<\/li>\n<li><strong>Scheduled Actions<\/strong> \u2014 a focused queue view showing only this plugin's expiry jobs, with per-row Run Now \/ Cancel controls.<\/li>\n<\/ul><\/dd>\n<dt id=\"does%20this%20work%20with%20custom%20post%20types%3F\"><h3>Does this work with custom post types?<\/h3><\/dt>\n<dd><p>Yes. Any public custom post type can be enabled in <strong>Content Expiry &gt; Settings<\/strong>. Once enabled, the meta box, list-table column, Quick Edit, Bulk Edit, and front-end actions all work on that post type.<\/p>\n\n<p>Developers can add private CPTs to the settings checkbox list via the <code>smcexp_supported_post_types_choices<\/code> filter, and override the runtime list via <code>smcexp_supported_post_types<\/code>.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20woocommerce%20products%3F\"><h3>Does this work with WooCommerce products?<\/h3><\/dt>\n<dd><p>Yes. Enable <strong>Products<\/strong> in <strong>Content Expiry &gt; Settings<\/strong> and the Content Expiry meta box will appear in the WooCommerce product editor alongside the standard product fields. Quick Edit and Bulk Edit on the products list table work too.<\/p><\/dd>\n<dt id=\"how%20do%20i%20see%20what%27s%20scheduled%20to%20expire%3F\"><h3>How do I see what's scheduled to expire?<\/h3><\/dt>\n<dd><p>Open <strong>Content Expiry &gt; Scheduled Actions<\/strong>. The page lists every queued expiry job with the post title, action type (Move to Draft \/ Redirect \/ Swap \/ Hide), the time it fires (relative + absolute, in your WP timezone), and a colour-coded status pill. Pending jobs have <strong>Run now<\/strong> and <strong>Cancel<\/strong> buttons. Filter by Upcoming \/ Completed \/ Failed \/ Cancelled \/ All.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20redirects%20if%20i%20deactivate%20the%20plugin%3F\"><h3>What happens to redirects if I deactivate the plugin?<\/h3><\/dt>\n<dd><p>Deactivating removes the redirect interception hook. Previously expired posts set to redirect will display normally. No data is deleted on deactivation.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20caching%20plugins%3F\"><h3>Does this work with caching plugins?<\/h3><\/dt>\n<dd><p>Yes, but full-page caching plugins may serve stale content briefly after an expiry action runs. The plugin fires a <code>smcexp_cache_purge<\/code> action that caching plugins can hook into.<\/p><\/dd>\n<dt id=\"can%20i%20undo%20an%20expiry%20action%3F\"><h3>Can I undo an expiry action?<\/h3><\/dt>\n<dd><p>Draft: re-publish the post. Redirect: clear the expiry rule. Swap Content: original content is never overwritten \u2014 clear the rule to restore it. Hide: clear the rule and the post becomes visible again immediately.<\/p><\/dd>\n<dt id=\"does%20this%20require%20woocommerce%3F\"><h3>Does this require WooCommerce?<\/h3><\/dt>\n<dd><p>No. Action Scheduler is bundled directly with the plugin.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.9.1<\/h4>\n\n<ul>\n<li>Fixed: clicking \"Run now\" on a pending row in the Scheduled Actions screen executed the expiry but left the Action Scheduler row stuck as \"Pending\". The handler now routes through Action Scheduler's queue runner, which fires the expiry hook and marks the row complete in the AS store, so the row correctly moves to the Completed tab on reload.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<p><strong>Scheduling reliability<\/strong><\/p>\n\n<ul>\n<li>Fixed: bundled Action Scheduler was not initialising on fresh installs because it was loaded inside a <code>plugins_loaded<\/code> priority-0 callback, which prevented Action Scheduler's own bootstrap hooks (added during the same priority) from firing. Result: <code>as_schedule_single_action()<\/code> was undefined when posts were saved, so expiries were silently never queued. Action Scheduler is now loaded at plugin-file load time.<\/li>\n<li>Fixed: the \"Action Scheduler could not be loaded\" admin notice was being shown spuriously because the availability check ran at <code>plugins_loaded<\/code> priority 0 \u2014 before Action Scheduler defines its helper functions at priority 1. The check now runs at priority 20.<\/li>\n<li>Fixed: timezone handling treated the WordPress timezone as UTC whenever the site was configured with a UTC offset (<code>gmt_offset<\/code>) instead of a named zone (<code>timezone_string<\/code>). Local times like \"12:44 PM\" were being saved as 12:44 <em>UTC<\/em>, causing actions to fire hours late. Both the meta-box save path and the Quick\/Bulk Edit save path now use <code>wp_timezone()<\/code>, which handles named zones and UTC offsets correctly.<\/li>\n<\/ul>\n\n<p><strong>Custom post types &amp; WooCommerce products<\/strong><\/p>\n\n<ul>\n<li>Added: full support for any public custom post type once it's enabled in <strong>Content Expiry &gt; Settings<\/strong>. Bulk-edit \"Expiry\" column hooks now register dynamically per post type (<code>manage_{post_type}_posts_columns<\/code> \/ <code>manage_{post_type}_posts_custom_column<\/code>) instead of the legacy blanket hooks, so the column only appears on post types you actually opted into.<\/li>\n<li>Added: WooCommerce product support. The meta box appears in the WooCommerce classic product editor; Quick Edit and Bulk Edit work on the products list table.<\/li>\n<li>Added: <code>smcexp_supported_post_types_choices<\/code> filter, letting developers add private CPTs to the settings checkbox list.<\/li>\n<li>Changed: the <strong>Media (attachment)<\/strong> post type is no longer offered in the settings list (rarely useful for content expiry; can be re-added via the new filter).<\/li>\n<\/ul>\n\n<p><strong>Admin UX<\/strong><\/p>\n\n<ul>\n<li>Changed: <strong>Content Expiry<\/strong> is now a top-level menu item in the admin sidebar (clock icon) with two children \u2014 <strong>Settings<\/strong> and <strong>Scheduled Actions<\/strong> \u2014 instead of living under Tools.<\/li>\n<li>Added: a custom <strong>Scheduled Actions<\/strong> screen replaces the raw Action Scheduler queue UI for end users. It shows only this plugin's jobs with friendly action labels, post links, relative + absolute times, status pills, tab filters (Upcoming \/ Completed \/ Failed \/ Cancelled \/ All) with accurate counts, and per-row <strong>Run Now<\/strong> \/ <strong>Cancel<\/strong> buttons for pending jobs. A footer link still opens the raw Action Scheduler queue for power users.<\/li>\n<li>Improved: the Content Expiry meta box has been redesigned to match the Gutenberg sidebar's visual language \u2014 uppercase 11px field labels, polished inputs with focus rings, a custom select arrow, a colour-coded status pill (Pending \/ Expired \/ Cancelled), grouped subgroup panels for the Redirect and Swap Content fields, and a separated, de-emphasised \"Clear expiry rule\" row.<\/li>\n<li>Improved: clicking anywhere inside the date\/time field opens the date picker (previously only the calendar icon was clickable). Uses <code>HTMLInputElement.showPicker()<\/code> with a graceful fallback.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release<\/li>\n<li>Post, page, and custom post type expiry with date\/time picker<\/li>\n<li>Four expiry actions: Draft, Redirect (301\/302), Swap Content, Hide<\/li>\n<li>Audit log with custom database table<\/li>\n<li>Quick Edit and Bulk Edit support<\/li>\n<li>Dashboard widget for upcoming expirations<\/li>\n<li>REST API endpoints for developers<\/li>\n<li>WP-CLI commands<\/li>\n<li>Action Scheduler bundled (no WooCommerce dependency)<\/li>\n<li>Full i18n support with .pot file<\/li>\n<\/ul>","raw_excerpt":"Schedule content expiry with auto-unpublish, redirect, content swap, and logging for posts, pages, custom post types, and WooCommerce products.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/301337","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=301337"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/amr267"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=301337"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=301337"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=301337"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=301337"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=301337"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=301337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}