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.
1.9.0
Scheduling reliability
Fixed: bundled Action Scheduler was not initialising on fresh installs because it was loaded inside a plugins_loaded priority-0 callback, which prevented Action Scheduler’s own bootstrap hooks (added during the same priority) from firing. Result: as_schedule_single_action() was undefined when posts were saved, so expiries were silently never queued. Action Scheduler is now loaded at plugin-file load time.
Fixed: the “Action Scheduler could not be loaded” admin notice was being shown spuriously because the availability check ran at plugins_loaded priority 0 — before Action Scheduler defines its helper functions at priority 1. The check now runs at priority 20.
Fixed: timezone handling treated the WordPress timezone as UTC whenever the site was configured with a UTC offset (gmt_offset) instead of a named zone (timezone_string). Local times like “12:44 PM” were being saved as 12:44 UTC, causing actions to fire hours late. Both the meta-box save path and the Quick/Bulk Edit save path now use wp_timezone(), which handles named zones and UTC offsets correctly.
Custom post types & WooCommerce products
Added: full support for any public custom post type once it’s enabled in Content Expiry > Settings. Bulk-edit “Expiry” column hooks now register dynamically per post type (manage_{post_type}_posts_columns / manage_{post_type}_posts_custom_column) instead of the legacy blanket hooks, so the column only appears on post types you actually opted into.
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.
Added: smcexp_supported_post_types_choices filter, letting developers add private CPTs to the settings checkbox list.
Changed: the Media (attachment) post type is no longer offered in the settings list (rarely useful for content expiry; can be re-added via the new filter).
Admin UX
Changed: Content Expiry is now a top-level menu item in the admin sidebar (clock icon) with two children — Settings and Scheduled Actions — instead of living under Tools.
Added: a custom Scheduled Actions 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 Run Now / Cancel buttons for pending jobs. A footer link still opens the raw Action Scheduler queue for power users.
Improved: the Content Expiry meta box has been redesigned to match the Gutenberg sidebar’s visual language — 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.
Improved: clicking anywhere inside the date/time field opens the date picker (previously only the calendar icon was clickable). Uses HTMLInputElement.showPicker() with a graceful fallback.
1.0.0
Initial release
Post, page, and custom post type expiry with date/time picker
Four expiry actions: Draft, Redirect (301/302), Swap Content, Hide
Audit log with custom database table
Quick Edit and Bulk Edit support
Dashboard widget for upcoming expirations
REST API endpoints for developers
WP-CLI commands
Action Scheduler bundled (no WooCommerce dependency)