=== Creotec Content Expiration Manager ===
Contributors: Creotec Limited, Michael Gbadebo
Tags: expiration, expire posts, redirect, unpublish, scheduled content
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Donate link: https://sowandnurture.com/

Add expiration dates and lifecycle actions to posts, pages, and public custom post types.

== Description ==

Creotec Content Expiration Manager adds simple content lifecycle management to WordPress:

* Add an expiration date/time to posts, pages, and public custom post types.
* Choose what happens when content expires:
  * Change status to Draft
  * Make Private/Password Protect (unpublish)
  * Redirect to another URL (301 or 302)
  * Replace content with a custom "expired" message
  * Display an "Expired" banner at the top of the post
  * Move content to the bin/trash
* Optional pre-expiration reminder email to the site admin address.
* Reliable processing:
  * Runs on WP-Cron every 10 minutes
  * Includes a lightweight fallback check on normal WordPress requests (throttled)
* Admin overview page:
  * Expiring Soon (next 30 days)
  * Already Expired
  * Bulk actions (extend date, disable expiration)
* Reusable templates for redirect, banner, and replacement-content actions.
* Per-post-type enabling/disabling under Settings > Post Expiration.
* List-table indicator icons show which posts have expiration enabled.

No external services, no analytics, no licensing system.

== Installation ==

1. Upload the `creotec-content-expiration-manager` folder to the `/wp-content/plugins/` directory, or install via the WordPress Plugins screen.
2. Activate the plugin through the "Plugins" screen in WordPress.
3. Edit a post/page (or enabled custom post type), open the "Expiration" box in the sidebar, and configure the expiration.

== Usage ==

1. Edit any supported post type.
2. In the "Expiration" box:
   * Check "Enable expiration".
   * Set an "Expiration date & time" (site timezone).
   * Optionally choose a reminder email (1/3/7 days before).
   * Choose what happens when it expires.
3. Update/publish the post.

To manage expirations site-wide:
* Go to WP Admin > Expirations
  * Use tabs:
    * Expiring Soon (next 30 days)
    * Already Expired
  * Use bulk actions:
    * Extend by 7 days
    * Extend by 30 days
    * Disable expiration

To choose which post types support expiration:
* Go to Settings > Expirations.

To create reusable expiration templates:
* Go to Settings > Expirations.
* Create templates for redirects, banners, or replacement content.
* When editing a post, choose a template in the relevant expiration action. Template edits are reflected anywhere that template is selected.

== Restore original content (Replace action) ==

When the action is "Replace content with a message", the plugin saves the original content when the expiration is processed.
After that, a "Restore original content" button appears in the Expiration meta box, allowing you to restore the saved content and disable expiration for that item.

== Expiration processing & reliability ==

This plugin does not rely solely on WP-Cron.

* Primary: WP-Cron runs every 10 minutes to process reminders and expirations.
* Fallback: A lightweight check runs on WordPress requests (throttled via transient) to ensure expirations still trigger even if cron is delayed.

Notes about caching:
* Some full-page caches may serve pages without executing PHP. In those cases, expirations will still process when WordPress runs (admin requests, REST requests, cron, etc.).
* On expiration, the plugin triggers an action hook so caching plugins/site code can purge caches:
  `do_action( 'creocem_post_expired', $post_id, $action );`

== Hooks ==

* `creocem_post_expired` - Fires after a post is processed as expired.
  * Parameters: (int) $post_id, (string) $action

== Frequently Asked Questions ==

= Does it work with custom post types? =
Yes. It supports all public post types. You can enable/disable per post type under Settings > Post Expiration.

= Does it require WP-Cron? =
It uses WP-Cron, but also includes a fallback check on normal requests (throttled) so expiration still triggers if cron is late.

= What happens for redirects? =
The post remains accessible in WordPress, but visitors are redirected on the frontend when the content is expired. Choose 301 or 302.

= Can I reuse redirect, banner, or replacement content? =
Yes. Create templates under Settings > Expirations, then select a template from the post Expiration box. Templates in use cannot be deleted.

= Does "Replace content" permanently overwrite my content? =
No. The plugin filters the frontend output for expired content to show the "expired" message. It also stores the original content snapshot on first processing so you can restore it.

= Can I restore the original content after "Replace"? =
Yes. Once the post has been processed as expired with the Replace action, the Expiration box shows a "Restore original content" button.

== Screenshots ==

1. Expiration meta box in the editor.
2. Expiration admin overview (Expiring Soon).
3. Expiration admin overview (Already Expired).
4. Post type settings under Settings > Post Expiration.

== Changelog ==

= 1.0.0 =
* Initial public release.

== Upgrade Notice ==

= 1.0.0 =
Initial public release

== License ==

This plugin is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This plugin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

== Credits ==

Developed and maintained by [Michael Gbadebo @ Creotec Limited](https://www.creotec.com/).
