=== Teydea Content Expiry - Auto-Expire Posts & Scheduled Status Changes ===
Contributors: teydeastudio, bartoszgadomski
Tags: content expiry, expiration date, scheduled content, post expiry, content management
Requires at least: 6.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.2.0
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html

Set expiry dates on posts and pages to automatically change status, notify editors, and display countdowns.

== Description ==

**Teydea Content Expiry lets you set an expiry date on any post type and automatically take action when that date arrives.**

Whether you manage time-sensitive promotions, seasonal content, legal notices with review deadlines, or news articles that lose relevance, Teydea Content Expiry gives you the tools to handle content lifecycle directly within WordPress.

**Key benefits:**

* Automatically move expired content to draft, private, or trash - no manual cleanup needed.
* Get notified by email before and after content expires, so nothing slips through the cracks.
* Show visitors countdown timers and timed content blocks that respond to expiry dates.
* Manage expiring content at scale with bulk edit, quick edit, and a dashboard widget.

Teydea Content Expiry works with posts, pages, and custom post types. It integrates with both the Block Editor (Gutenberg) and the Classic Editor.

== Features ==

* **Expiry Date and Action** - Set an expiry date and time on any supported post type, and choose what happens: Move to Draft, Make Private, or Move to Trash.
* **Block Editor Integration** - Sidebar panel in Gutenberg for configuring expiry date, time, and action.
* **Classic Editor Integration** - Metabox in the sidebar for configuring expiry on sites using the Classic Editor.
* **Posts List Column** - Sortable expiry column in the classic posts and pages lists showing scheduled or past expiry dates, completed actions, and previous statuses.
* **Restore Previous Status** - Undo an automated status change from the classic posts and pages lists, including Trash. Restoring clears the old expiry so it does not run again.
* **Expired Posts Filter** - Dedicated "Expired" view in the posts list with a count of expired posts.
* **Quick Edit and Bulk Edit** - Edit expiry settings inline or for multiple posts at once.
* **Dashboard Widget** - "Expiring Soon" widget showing posts approaching their expiry date within a configurable time window.
* **Email Notifications** - Get notified when content expires, with optional pre-expiry warnings (1, 3, or 7 days before).
* **Configurable Recipients** - Send notifications to specific user roles or individual users.
* **Expiry Countdown Block** - Display a live countdown timer or static date on the front end, with customizable colors and thresholds. Also available as a `[stalepilot_countdown]` shortcode.
* **Timed Content Block** - Show or hide content based on a date, relative time from publish, or the post's expiry date. Also available as a `[stalepilot_timed_content]` shortcode.
* **Automatic Scheduling** - Expiry is automatically canceled on trash/delete and rescheduled on restore.
* **Developer Hooks** - Extensible via actions and filters for custom integrations.

== Screenshots ==

1. Block Editor: set an expiry date and choose what happens when it arrives - move to draft, make private, or move to trash.
2. Posts list: a sortable Expiry column, plus a dedicated "Expired" view showing how many posts have already passed their date.
3. Settings: choose which post types support expiry and set the default expiry action.
4. Settings: email notifications on expiry, with optional pre-expiry warnings and recipients by role or individual user.
5. Dashboard: the "Expiring Soon" widget lists content approaching its expiry date within a configurable window.
6. Front end: the Expiry Countdown block shows a live countdown, with color thresholds as the deadline approaches.

== Installation ==

1. Upload the `stalepilot` directory to `/wp-content/plugins/`, or install it directly through the WordPress plugin screen.
2. Activate the plugin through the "Plugins" screen in WordPress.
3. Go to **Settings > Content Expiry** to choose which post types support expiry and configure default actions and notifications.
4. Edit any post or page and use the "Content Expiry" panel in the sidebar to set an expiry date and action.

== Frequently Asked Questions ==

= Which post types are supported? =

By default, Teydea Content Expiry is enabled for posts and pages. You can enable it for any public custom post type in the plugin settings under **Settings > Content Expiry**.

= What happens when a post expires? =

The plugin performs the action you selected for that post: it moves the post to draft, makes it private, or moves it to trash. You can set a default action in the settings and override it per post.

= Can I undo an automated expiry? =

Yes. In the classic posts or pages list, hover over an expired post and choose **Restore previous status**. For posts expired into Trash, open the Trash view first. The action appears when a valid previous status was recorded and you have permission to restore it.

Making a post private removes its password, so a post that expired into Private is not restored to Published or Scheduled if it may have had a password. The Expiry column says so; publish the post manually and set its password again.

A successful restore clears the expiry record, date, action, and pending expiry schedule. Set a new expiry if you want the content to expire again. The action is available in the classic list tables; it is not available in the block-editor sidebar or Site Editor DataViews.

= Can I display a countdown timer on the front end? =

Yes. The plugin includes an Expiry Countdown block (and a `[stalepilot_countdown]` shortcode) that displays either a live countdown timer or a static formatted date. You can customize colors, threshold warnings, and the expired message.

= What happens if I restore a trashed post that had an expiry date? =

If the expiry date is still in the future, the plugin automatically reschedules the expiry. If the expiry date has already passed, the stale expiry metadata is cleaned up.

= Where can I find the unminified source code? =

All source ships with the plugin: the plugin's own JavaScript is in `src/`, and the bundled `@teydeastudio/components` and `@teydeastudio/utils` libraries are in `deps/js/components/src/` and `deps/js/utils/src/`. To rebuild, install Node.js 24 and run `npm install && npm run build` in the plugin directory.

== Changelog ==

= 1.2.0 (2026-10-30) =
* Editors can undo an automated expiry with a new "Restore previous status" action in the Posts and Pages lists, and the Expiry column shows the status a post had before it expired
* Fixed expiry dates set across a daylight saving change being stored an hour off
* Personal-data exports now include notification recipient memberships, and erasure requests remove the user from the recipients
* Accessibility: the Expires date field in the document sidebar and the Timed Content block now has a label
* A canceled scheduled action now stays canceled when it was already running
* Saving settings now reports an error when WordPress refuses the database write
* The minimum supported WordPress version is now 6.8
* Dependencies updated
* Code improvements

= 1.1.1 (2026-10-12) =
* Fixed roles and users past the tenth not being selectable in the settings
* Editor panel open/closed and enabled/disabled preferences are now remembered between page loads
* Dashboard widget table rows are now announced by screen readers with the post they belong to
* Dependencies updated
* Code improvements

= 1.1.0 (2026-08-28) =
* Plugin renamed to "Teydea Content Expiry", with a new icon and updated links
* Compatibility with WordPress 7.1 confirmed
* Fixed help text not being announced as the description of the field it belongs to on the settings and editor screens
* Fixed a successful settings save reporting itself as failed on WordPress 6.6
* Plugin banners and screenshots added to the WordPress.org listing
* Dependencies updated
* Code improvements

= 1.0.3 (2026-06-22) =
* Added the plugin icon
* Settings page tabs are now reflected in the URL, so a tab can be bookmarked and shared
* Added a clear admin notice for unmet server requirements (minimum PHP/WordPress version, required extensions)
* Dependencies updated
* Code improvements

= 1.0.2 (2026-04-27) =
* An FAQ entry added to readme.txt, explaining where the unminified source code lives in the plugin and how to rebuild it from source

= 1.0.1 (2026-04-24) =
* The "Pre-expiry warning" and "Dashboard widget window" settings now accept any number of days within a reasonable range, with quick-set presets for common values
* Direct loading of wp-includes/version.php in the WordPress version detection fallback removed

= 1.0.0 (2026-04-07) =
* The first stable release
