Automated New England Amber Alert Banner
/**
* Plugin Name: Automated New England Amber Alert Banner
* Description: Automatically polls official RSS feeds and emergency bulletins across New England, displaying a high-priority sticky broadcast banner on active alerts.
* Version: 2.0.0
* Author: Michael McStay Designs
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class NE_Automated_Amber_Alert {
private $transient_key = 'ne_amber_feed_cache';
private $cache_duration = 300; // Cache check every 5 minutes
public function __construct() {
add_action( 'wp_head', [ $this, 'render_banner_styles' ] );
add_action( 'wp_footer', [ $this, 'render_banner_html' ] );
add_action( 'admin_menu', [ $this, 'register_admin_menu' ] );
add_action( 'admin_init', [ $this, 'register_settings' ] );
}
/**
* Register Settings in WordPress Admin
*/
public function register_settings() {
register_setting( 'ne_auto_amber_group', 'ne_amber_auto_mode' );
register_setting( 'ne_auto_amber_group', 'ne_amber_manual_override' );
register_setting( 'ne_auto_amber_group', 'ne_amber_manual_headline' );
register_setting( 'ne_auto_amber_group', 'ne_amber_manual_details' );
register_setting( 'ne_auto_amber_group', 'ne_amber_manual_state' );
register_setting( 'ne_auto_amber_group', 'ne_amber_manual_link' );
}
/**
* Add Settings Page to Dashboard
*/
public function register_admin_menu() {
add_options_page(
'Auto Amber Alerts',
'Auto Amber Alerts',
'manage_options',
'ne-auto-amber',
[ $this, 'render_admin_page' ]
);
}
/**
* Admin Dashboard UI
*/
public function render_admin_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$feed_data = $this->fetch_new_england_alerts();
?>
<div class="wrap">
<h1>New England Automated Amber Alert Feed & Controls</h1>
<div class="card" style="max-width: 100%; margin-top: 20px; padding: 15px; background: #fff; border-left: 4px solid <?php echo !empty($feed_data) ? '#b30000' : '#46b450'; ?>;">
<h3>Live Feed Status: <?php echo !empty($feed_data) ? 'ACTIVE ALERT DETECTED' : 'No Active Alerts Found in Regional Feed'; ?></h3>
<?php if (!empty($feed_data)) : ?>
<p><strong>Headline:</strong> <?php echo esc_html($feed_data['headline']); ?></p>
<p><strong>State:</strong> <?php echo esc_html($feed_data['state']); ?></p>
<p><strong>Details:</strong> <?php echo esc_html($feed_data['details']); ?></p>
<?php else: ?>
<p>Monitoring RSS feeds for Massachusetts, Rhode Island, Connecticut, New Hampshire, Vermont, and Maine...</p>
<?php endif; ?>
</div>
<form method="post" action="options.php" style="margin-top: 20px;">
<?php settings_fields( 'ne_auto_amber_group' ); ?>
<?php do_settings_sections( 'ne_auto_amber_group' ); ?>
<table class="form-table">
<tr valign="top">
<th scope="row">Operation Mode</th>
<td>
<label>
<input type="checkbox" name="ne_amber_auto_mode" value="1" <?php checked( 1, get_option( 'ne_amber_auto_mode', 1 ), true ); ?> />
Enable Fully Automated RSS Feed Polling
</label>
<p class="description">When enabled, the banner triggers automatically when state/federal feeds push a missing child / amber alert for New England.</p>
</td>
</tr>
<tr valign="top">
<th scope="row">Manual Override / Emergency Broadcast</th>
<td>
<label>
<input type="checkbox" name="ne_amber_manual_override" value="1" <?php checked( 1, get_option( 'ne_amber_manual_override' ), true ); ?> />
Force Manual Alert Override (Bypasses RSS feed)
</label>
</td>
</tr>
<tr valign="top">
<th scope="row">Manual Target State</th>
<td>
<select name="ne_amber_manual_state">
<?php
$current_state = get_option( 'ne_amber_manual_state', 'All New England' );
$states = [ 'All New England', 'Massachusetts', 'Connecticut', 'Rhode Island', 'New Hampshire', 'Vermont', 'Maine' ];
foreach ( $states as $st ) {
echo '<option value="' . esc_attr( $st ) . '" ' . selected( $current_state, $st, false ) . '>' . esc_html( $st ) . '</option>';
}
?>
</select>
</td>
</tr>
<tr valign="top">
<th scope="row">Manual Headline</th>
<td>
<input type="text" name="ne_amber_manual_headline" value="<?php echo esc_attr( get_option( 'ne_amber_manual_headline', 'EMERGENCY AMBER ALERT' ) ); ?>" style="width: 100%;" />
</td>
</tr>
<tr valign="top">
<th scope="row">Manual Details</th>
<td>
<textarea name="ne_amber_manual_details" rows="3" style="width: 100%;"><?php echo esc_textarea( get_option( 'ne_amber_manual_details', '' ) ); ?></textarea>
</td>
</tr>
<tr valign="top">
<th scope="row">Official Link</th>
<td>
<input type="text" name="ne_amber_manual_link" value="<?php echo esc_attr( get_option( 'ne_amber_manual_link', '' ) ); ?>" style="width: 100%;" placeholder="https://..." />
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
/**
* Fetch and Parse Public RSS/XML Alert Data for New England
*/
private function fetch_new_england_alerts() {
// Check transient cache first
$cached = get_transient( $this->transient_key );
if ( false !== $cached ) {
return $cached;
}
// Public RSS endpoints aggregating missing person / emergency data
$feed_urls = [
'https://www.missingkids.org/content/dam/missingkids/getinvolved/rss/amber-alert.xml', // National fallback / official feed
];
$alert_data = [];
foreach ( $feed_urls as $url ) {
$rss = fetch_feed( $url );
if ( is_wp_error( $rss ) ) {
continue;
}
$max_items = $rss->get_item_quantity( 5 );
$rss_items = $rss->get_items( 0, $max_items );
foreach ( $rss_items as $item ) {
$title = $item->get_title();
$description = strip_tags( $item->get_description() );
$content = $title . ' ' . $description;
// Scan content for New England states or keywords
$ne_states = [ 'Massachusetts', 'MA', 'Connecticut', 'CT', 'Rhode Island', 'RI', 'New Hampshire', 'NH', 'Vermont', 'VT', 'Maine', 'ME' ];
$matched_state = '';
foreach ( $ne_states as $st ) {
if ( stripos( $content, $st ) !== false ) {
$matched_state = $st;
break;
}
}
// If an active alert matches New England region
if ( ! empty( $matched_state ) || stripos( $content, 'AMBER ALERT' ) !== false ) {
$alert_data = [
'headline' => 'EMERGENCY AMBER ALERT: ' . sanitize_text_field( $title ),
'details' => sanitize_text_field( wp_trim_words( $description, 35, '...' ) ),
'state' => 'New England Region',
'link' => esc_url( $item->get_permalink() )
];
break 2;
}
}
}
// Save to transient cache to prevent server slowdowns
set_transient( $this->transient_key, $alert_data, $this->cache_duration );
return $alert_data;
}
/**
* Get Current Active Alert Payload (Manual or Automated)
*/
public function get_active_alert() {
// Check manual override first
if ( get_option( 'ne_amber_manual_override' ) ) {
return [
'active' => true,
'headline' => get_option( 'ne_amber_manual_headline', 'EMERGENCY AMBER ALERT' ),
'details' => get_option( 'ne_amber_manual_details', '' ),
'state' => get_option( 'ne_amber_manual_state', 'All New England' ),
'link' => get_option( 'ne_amber_manual_link', '' ),
];
}
// Check automated feed mode
if ( get_option( 'ne_amber_auto_mode', 1 ) ) {
$feed_alert = $this->fetch_new_england_alerts();
if ( ! empty( $feed_alert ) ) {
$feed_alert['active'] = true;
return $feed_alert;
}
}
return [ 'active' => false ];
}
/**
* Render Emergency Styles
*/
public function render_banner_styles() {
$alert = $this->get_active_alert();
if ( empty( $alert['active'] ) ) {
return;
}
?>
<style>
#ne-amber-emergency-banner {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #b30000;
color: #ffffff;
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
border-bottom: 3px solid #ffcc00;
box-sizing: border-box;
animation: ne-flash-border 1.5s infinite;
}
@keyframes ne-flash-border {
0% { border-bottom-color: #ffcc00; }
50% { border-bottom-color: #b30000; }
100% { border-bottom-color: #ffcc00; }
}
.ne-amber-container {
max-width: 1200px;
margin: 0 auto;
padding: 12px 20px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
}
.ne-amber-content {
display: flex;
align-items: center;
gap: 15px;
flex: 1;
}
.ne-amber-badge {
background: #ffcc00;
color: #111111;
font-weight: 800;
padding: 4px 10px;
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
border-radius: 3px;
}
.ne-amber-text h4 {
margin: 0 0 2px 0;
font-size: 15px;
color: #ffffff;
font-weight: 700;
}
.ne-amber-text p {
margin: 0;
font-size: 13px;
color: #fce8e6;
line-height: 1.4;
}
.ne-amber-text a {
color: #ffcc00;
text-decoration: underline;
margin-left: 8px;
font-weight: 600;
}
.ne-amber-actions {
display: flex;
align-items: center;
gap: 10px;
}
.ne-amber-dismiss {
background: transparent;
border: 1px solid #ffffff;
color: #ffffff;
padding: 6px 12px;
font-size: 12px;
cursor: pointer;
border-radius: 3px;
font-weight: 600;
transition: background 0.2s;
}
.ne-amber-dismiss:hover {
background: rgba(255,255,255,0.2);
}
body.has-ne-amber-banner {
padding-top: 55px !important;
}
@media(max-width: 768px) {
.ne-amber-container {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.ne-amber-actions {
width: 100%;
justify-content: flex-end;
}
body.has-ne-amber-banner {
padding-top: 95px !important;
}
}
</style>
<?php
}
/**
* Render Banner HTML & Dismissal Script
*/
public function render_banner_html() {
$alert = $this->get_active_alert();
if ( empty( $alert['active'] ) ) {
return;
}
if ( isset( $_COOKIE['ne_amber_dismissed'] ) && $_COOKIE['ne_amber_dismissed'] === '1' ) {
return;
}
?>
<div id="ne-amber-emergency-banner" role="region" aria-label="Emergency Amber Alert">
<div class="ne-amber-container">
<div class="ne-amber-content">
<span class="ne-amber-badge"><?php echo esc_html( $alert['state'] ); ?></span>
<div class="ne-amber-text">
<h4><?php echo esc_html( $alert['headline'] ); ?></h4>
<p>
<?php echo esc_html( $alert['details'] ); ?>
<?php if ( ! empty( $alert['link'] ) ) : ?>
<a href="<?php echo esc_url( $alert['link'] ); ?>" target="_blank" rel="noopener">Official Bulletin »</a>
<?php endif; ?>
</p>
</div>
</div>
<div class="ne-amber-actions">
<button class="ne-amber-dismiss" id="ne-dismiss-btn">Dismiss</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.body.classList.add('has-ne-amber-banner');
const dismissBtn = document.getElementById('ne-dismiss-btn');
if (dismissBtn) {
dismissBtn.addEventListener('click', function() {
const banner = document.getElementById('ne-amber-emergency-banner');
if (banner) {
banner.style.display = 'none';
document.body.classList.remove('has-ne-amber-banner');
const d = new Date();
d.setTime(d.getTime() + (4 * 60 * 60 * 1000));
document.cookie = "ne_amber_dismissed=1; expires=" + d.toUTCString() + "; path=/";
}
});
}
});
</script>
<?php
}
}
new NE_Automated_Amber_Alert();