'absint', 'default' => 0, ) ); register_setting( 'aitepc_notifications_group', 'aitepc_email_notification_days', array( 'sanitize_callback' => 'aitepc_sanitize_notification_days', 'default' => array( 7, 3, 1 ), ) ); register_setting( 'aitepc_notifications_group', 'aitepc_email_from_name', array( 'sanitize_callback' => 'sanitize_text_field', 'default' => get_bloginfo( 'name' ), ) ); register_setting( 'aitepc_notifications_group', 'aitepc_email_from_address', array( 'sanitize_callback' => 'sanitize_email', 'default' => get_option( 'admin_email' ), ) ); } add_action( 'admin_init', 'aitepc_register_email_notification_settings' ); /** * Sanitize notification day intervals — only allow known values. * * @param mixed $input Raw input (expected array of integers). * @return array Validated array of allowed day intervals. */ function aitepc_sanitize_notification_days( $input ) { $allowed = array( 1, 3, 7, 14, 30 ); if ( ! is_array( $input ) ) { return array(); } return array_values( array_intersect( array_map( 'absint', $input ), $allowed ) ); } // --------------------------------------------------------------------------- // Cron: send notifications // --------------------------------------------------------------------------- /** * Query posts whose expiry date is exactly X days away and send email alerts. * Runs daily via the 'aitepc_check_expiry_notifications_event' cron hook. * A post meta log prevents duplicate emails per interval. */ function aitepc_check_expiry_notifications() { if ( ! get_option( 'aitepc_email_notifications_enabled', 0 ) ) { return; } $days_before = get_option( 'aitepc_email_notification_days', array( 7, 3, 1 ) ); if ( empty( $days_before ) || ! is_array( $days_before ) ) { return; } $post_types = get_option( 'aitepc_expiry_date_post_types', array( 'post' ) ); if ( empty( $post_types ) ) { return; } foreach ( $days_before as $interval ) { $interval = absint( $interval ); $target_date = gmdate( 'Y-m-d', strtotime( '+' . $interval . ' days' ) ); $post_ids = get_posts( array( 'post_type' => $post_types, 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, 'meta_query' => array( array( 'key' => '_aitepc_expiry_date', 'value' => $target_date, 'compare' => '=', 'type' => 'DATE', ), ), ) ); foreach ( $post_ids as $post_id ) { // Skip if we already sent this interval's notification for this post. $already_sent = get_post_meta( $post_id, '_aitepc_email_sent_days', true ); if ( ! is_array( $already_sent ) ) { $already_sent = array(); } if ( in_array( $interval, $already_sent, true ) ) { continue; } // Respect per-user unsubscribe preference. $author_id = (int) get_post_field( 'post_author', $post_id ); if ( get_user_meta( $author_id, 'aitepc_email_unsubscribe', true ) === '1' ) { continue; } $sent = aitepc_send_expiry_notification_email( $post_id, $interval ); if ( $sent ) { $already_sent[] = $interval; update_post_meta( $post_id, '_aitepc_email_sent_days', $already_sent ); } } } } add_action( 'aitepc_check_expiry_notifications_event', 'aitepc_check_expiry_notifications' ); // --------------------------------------------------------------------------- // Email sending // --------------------------------------------------------------------------- /** * Send a single expiry notification email to the post author. * * @param int $post_id Post ID. * @param int $days_before Days until the post expires. * @return bool True if wp_mail() accepted the message. */ function aitepc_send_expiry_notification_email( $post_id, $days_before ) { $post = get_post( $post_id ); if ( ! $post ) { return false; } $author = get_userdata( (int) $post->post_author ); if ( ! $author || ! is_email( $author->user_email ) ) { return false; } $from_name = get_option( 'aitepc_email_from_name', get_bloginfo( 'name' ) ); $from_email = get_option( 'aitepc_email_from_address', get_option( 'admin_email' ) ); if ( ! is_email( $from_email ) ) { $from_email = get_option( 'admin_email' ); } $days_before = absint( $days_before ); $site_name = get_bloginfo( 'name' ); $post_title = get_the_title( $post_id ); /* translators: 1: site name, 2: post title, 3: days before expiry */ $subject = sprintf( __( '[%1$s] Your post "%2$s" expires in %3$d day(s)', 'ait-easy-post-customization' ), $site_name, $post_title, $days_before ); $body = aitepc_get_email_template( $post_id, $days_before, $author ); $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: ' . sanitize_text_field( $from_name ) . ' <' . sanitize_email( $from_email ) . '>', ); /** * Filter the email data before sending a notification. * * @param array $data Keys: to, subject, body, headers. * @param int $post_id Post ID. * @param int $days_before Days before expiry. */ $data = apply_filters( 'aitepc_notification_email_data', array( 'to' => $author->user_email, 'subject' => $subject, 'body' => $body, 'headers' => $headers, ), $post_id, $days_before ); return wp_mail( sanitize_email( $data['to'] ), $data['subject'], $data['body'], $data['headers'] ); } /** * Build the HTML email body for an expiry notification. * Uses inline CSS only — no external fonts or images — for maximum * email client compatibility. * * @param int $post_id Post ID. * @param int $days_before Days before expiry. * @param WP_User $author Post author user object. * @return string Full HTML email body. */ function aitepc_get_email_template( $post_id, $days_before, $author ) { $site_name = esc_html( get_bloginfo( 'name' ) ); $site_url = esc_url( home_url( '/' ) ); $post_title = esc_html( get_the_title( $post_id ) ); $expiry_date = esc_html( get_post_meta( $post_id, '_aitepc_expiry_date', true ) ); $edit_url = esc_url( get_edit_post_link( $post_id, 'raw' ) ); $post_url = esc_url( get_permalink( $post_id ) ); $author_name = esc_html( $author->display_name ); $days_before = absint( $days_before ); $days_text = ( 1 === $days_before ) ? esc_html__( '1 day', 'ait-easy-post-customization' ) : sprintf( /* translators: number of days */ esc_html__( '%d days', 'ait-easy-post-customization' ), $days_before ); // Stateless unsubscribe token using wp_hash — no DB entry needed. $token = wp_hash( $author->ID . $author->user_email ); $unsub_url = esc_url( add_query_arg( array( 'user_id' => $author->ID, 'token' => $token, ), rest_url( 'aitepc/v1/unsubscribe' ) ) ); ob_start(); ?> <?php echo $site_name; ?>

' . $post_title . '', '' . $days_text . '' ); ?>

 

 


' . $site_name . '' ); ?>