mirror of
https://gh.llkk.cc/https://github.com/Tetrakern/fictioneer-email-notifications.git
synced 2025-10-03 18:11:01 +08:00
436 lines
13 KiB
PHP
436 lines
13 KiB
PHP
<?php
|
|
|
|
// =============================================================================
|
|
// FRONTEND
|
|
// =============================================================================
|
|
|
|
/**
|
|
* AJAX callback to retrieve the modal content
|
|
*
|
|
* Note: The "fictioneer_ajax_" prefix enables the plugin skipping
|
|
* in the theme's must-use-plugin.
|
|
*
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
function fictioneer_ajax_fcnen_get_form_content() {
|
|
// Verify
|
|
if ( ! wp_doing_ajax() ) {
|
|
wp_send_json_error( __( 'Invalid request.', 'fcnen' ) );
|
|
}
|
|
|
|
// Get form
|
|
$html = fictioneer_minify_html( fcnen_get_modal_content() );
|
|
|
|
// Response
|
|
wp_send_json_success( array( 'html' => $html ) );
|
|
}
|
|
add_action( 'wp_ajax_fictioneer_ajax_fcnen_get_form_content', 'fictioneer_ajax_fcnen_get_form_content' );
|
|
add_action( 'wp_ajax_nopriv_fictioneer_ajax_fcnen_get_form_content', 'fictioneer_ajax_fcnen_get_form_content' );
|
|
|
|
/**
|
|
* AJAX callback to subscribe
|
|
*
|
|
* Note: The "fictioneer_ajax_" prefix enables the plugin skipping
|
|
* in the theme's must-use-plugin.
|
|
*
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
function fictioneer_ajax_fcnen_subscribe_or_update() {
|
|
// Verify
|
|
if ( ! wp_doing_ajax() ) {
|
|
wp_send_json_error( __( 'Invalid request.', 'fcnen' ) );
|
|
}
|
|
|
|
if ( ! check_ajax_referer( 'fcnen-subscribe', 'nonce', false ) ) {
|
|
wp_send_json_error(
|
|
array( 'notice' => __( 'Nonce verification failed. Please reload and try again.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
// Setup
|
|
$email = sanitize_email( $_POST['email'] ?? '' );
|
|
$code = sanitize_text_field( $_POST['code'] ?? '' );
|
|
$scope_everything = boolval( absint( $_POST['scope-everything'] ?? 1 ) );
|
|
$scope_posts = boolval( absint( $_POST['scope-posts'] ?? 0 ) );
|
|
$scope_stories = boolval( absint( $_POST['scope-stories'] ?? 0 ) );
|
|
$scope_chapters = boolval( absint( $_POST['scope-chapters'] ?? 0 ) );
|
|
$post_ids = fcnen_get_array_from_post_string( 'post_id' );
|
|
$categories = fcnen_get_array_from_post_string( 'categories' );
|
|
$tags = fcnen_get_array_from_post_string( 'tags' );
|
|
$taxonomies = fcnen_get_array_from_post_string( 'taxonomies' );
|
|
$default_notice = __( 'Submission successful. If everything is in order, you will get an email.', 'fcnen' );
|
|
$result = false;
|
|
|
|
// Validate email
|
|
if ( empty( $email ) || ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
|
|
wp_send_json_error( array( 'notice' => __( 'Invalid email address.', 'fcnen' ) ) );
|
|
}
|
|
|
|
// Sanitize
|
|
if ( get_option( 'fcnen_flag_subscribe_to_stories' ) ) {
|
|
$post_ids = fcnen_prepare_id_array( $post_ids );
|
|
} else {
|
|
$post_ids = [];
|
|
}
|
|
|
|
if ( get_option( 'fcnen_flag_subscribe_to_taxonomies' ) ) {
|
|
$categories = fcnen_prepare_id_array( $categories );
|
|
$tags = fcnen_prepare_id_array( $tags );
|
|
$taxonomies = fcnen_prepare_id_array( $taxonomies );
|
|
} else {
|
|
$categories = [];
|
|
$tags = [];
|
|
$taxonomies = [];
|
|
}
|
|
|
|
// Arguments
|
|
$args = array(
|
|
'scope-everything' => $scope_everything,
|
|
'scope-posts' => $scope_posts,
|
|
'scope-stories' => $scope_stories,
|
|
'scope-chapters' => $scope_chapters,
|
|
'post_ids' => $post_ids,
|
|
'categories' => $categories,
|
|
'tags' => $tags,
|
|
'taxonomies' => $taxonomies
|
|
);
|
|
|
|
// New or update?
|
|
$is_new_subscriber = ! fcnen_subscriber_exists( $email );
|
|
|
|
// New subscriber!
|
|
if ( $is_new_subscriber ) {
|
|
$result = fcnen_add_subscriber( $email, $args );
|
|
}
|
|
|
|
// Update subscriber!
|
|
if ( ! $is_new_subscriber ) {
|
|
// Code?
|
|
if ( ! $code ) {
|
|
$notice = WP_DEBUG ? __( 'Code missing.', 'fcnen' ) : $default_notice;
|
|
wp_send_json_error( array( 'notice' => $notice ) );
|
|
}
|
|
|
|
// Query subscriber
|
|
$subscriber = fcnen_get_subscriber_by_email_and_code( $email, $code );
|
|
|
|
// Code did not match email
|
|
if ( empty( $subscriber ) ) {
|
|
$notice = WP_DEBUG ? __( 'Code did not match email.', 'fcnen' ) : $default_notice;
|
|
wp_send_json_error( array( 'notice' => $notice ) );
|
|
}
|
|
|
|
// Update
|
|
$result = fcnen_update_subscriber( $email, $args );
|
|
}
|
|
|
|
// Response
|
|
if ( $result ) {
|
|
wp_send_json_success( array( 'notice' => $default_notice ) );
|
|
} else {
|
|
$notice = WP_DEBUG ? __( 'Could not create or update subscription.', 'fcnen' ) : $default_notice;
|
|
|
|
// Do not expose informative errors to strangers
|
|
wp_send_json_success( array( 'notice' => $notice ) );
|
|
}
|
|
}
|
|
add_action( 'wp_ajax_fictioneer_ajax_fcnen_subscribe_or_update', 'fictioneer_ajax_fcnen_subscribe_or_update' );
|
|
add_action( 'wp_ajax_nopriv_fictioneer_ajax_fcnen_subscribe_or_update', 'fictioneer_ajax_fcnen_subscribe_or_update' );
|
|
|
|
/**
|
|
* AJAX callback to unsubscribe
|
|
*
|
|
* Note: The "fictioneer_ajax_" prefix enables the plugin skipping
|
|
* in the theme's must-use-plugin.
|
|
*
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
function fictioneer_ajax_fcnen_unsubscribe() {
|
|
// Verify
|
|
if ( ! wp_doing_ajax() ) {
|
|
wp_send_json_error( __( 'Invalid request.', 'fcnen' ) );
|
|
}
|
|
|
|
if ( ! check_ajax_referer( 'fcnen-subscribe', 'nonce', false ) ) {
|
|
wp_send_json_error(
|
|
array( 'notice' => __( 'Nonce verification failed. Please reload and try again.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
// Setup
|
|
$email = sanitize_email( $_POST['email'] ?? '' );
|
|
$code = sanitize_text_field( $_POST['code'] ?? '' );
|
|
$default_notice = __( 'Successfully unsubscribed.', 'fcnen' );
|
|
$result = false;
|
|
|
|
// Validate email
|
|
if ( empty( $email ) || ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
|
|
wp_send_json_error( array( 'notice' => __( 'Invalid email address.', 'fcnen' ) ) );
|
|
}
|
|
|
|
// Email and code present?
|
|
if ( empty( $email ) || empty( $code ) ) {
|
|
wp_send_json_error( array( 'notice' => __( 'Email or code missing.', 'fcnen' ) ) );
|
|
}
|
|
|
|
// Query subscriber
|
|
$subscriber = fcnen_get_subscriber_by_email_and_code( $email, $code );
|
|
|
|
// Match found...
|
|
if ( ! $subscriber ) {
|
|
// ... no match
|
|
$notice = WP_DEBUG ? __( 'No matching subscription found.', 'fcnen' ) : $default_notice;
|
|
|
|
// Do not expose informative errors to strangers
|
|
wp_send_json_success( array( 'notice' => $notice ) );
|
|
} else {
|
|
// ... found
|
|
$result = fcnen_delete_subscriber( $email );
|
|
}
|
|
|
|
// Response
|
|
if ( $result ) {
|
|
wp_send_json_success( array( 'notice' => $default_notice ) );
|
|
} else {
|
|
$notice = WP_DEBUG ? __( 'Could not delete subscription.', 'fcnen' ) : $default_notice;
|
|
|
|
// Do not expose informative errors to strangers
|
|
wp_send_json_success( array( 'notice' => $notice ) );
|
|
}
|
|
}
|
|
add_action( 'wp_ajax_fictioneer_ajax_fcnen_unsubscribe', 'fictioneer_ajax_fcnen_unsubscribe' );
|
|
add_action( 'wp_ajax_nopriv_fictioneer_ajax_fcnen_unsubscribe', 'fictioneer_ajax_fcnen_unsubscribe' );
|
|
|
|
/**
|
|
* AJAX callback to search content
|
|
*
|
|
* Note: The "fictioneer_ajax_" prefix enables the plugin skipping
|
|
* in the theme's must-use-plugin.
|
|
*
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
function fictioneer_ajax_fcnen_search_content() {
|
|
// Verify
|
|
if ( ! wp_doing_ajax() ) {
|
|
wp_send_json_error( __( 'Invalid request.', 'fcnen' ) );
|
|
}
|
|
|
|
if ( ! check_ajax_referer( 'fcnen-subscribe', 'nonce', false ) ) {
|
|
wp_send_json_error(
|
|
array( 'notice' => __( 'Nonce verification failed. Please reload and try again.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
// Setup
|
|
$filter = sanitize_text_field( $_REQUEST['filter'] ?? '' );
|
|
$search = sanitize_text_field( $_REQUEST['search'] ?? '' );
|
|
$page = absint( $_REQUEST['page'] ?? 1 );
|
|
$stories = null;
|
|
$terms = null;
|
|
$output = [];
|
|
|
|
// Query stories
|
|
if ( $filter === 'story' && get_option( 'fcnen_flag_subscribe_to_stories' ) ) {
|
|
$search_args = array(
|
|
'post_type' => 'fcn_story',
|
|
'post_status' => 'publish',
|
|
'orderby' => 'relevance modified',
|
|
'order' => 'desc',
|
|
'posts_per_page' => 25,
|
|
'paged' => $page,
|
|
's' => $search,
|
|
'update_post_meta_cache' => true, // We might need that
|
|
'update_post_term_cache' => false // Improve performance
|
|
);
|
|
|
|
if ( ! $search ) {
|
|
$search_args['orderby'] = 'modified';
|
|
}
|
|
|
|
$stories = new WP_Query( $search_args );
|
|
|
|
// Build and add items
|
|
foreach ( $stories->posts as $item ) {
|
|
// Add to output
|
|
$output[] = fcnen_get_source_node(
|
|
array(
|
|
'name' => 'post_id',
|
|
'type' => 'fcn_story',
|
|
'id' => $item->ID,
|
|
'label' => _x( 'Story', 'List item label.', 'fcnen' ),
|
|
'title' => fictioneer_get_safe_title( $item, 'fcnen-search-stories' )
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Query taxonomies
|
|
if ( $filter === 'taxonomies' && get_option( 'fcnen_flag_subscribe_to_taxonomies' ) ) {
|
|
$terms = get_terms(
|
|
array(
|
|
'taxonomy' => ['category', 'post_tag', 'fcn_genre', 'fcn_fandom', 'fcn_character', 'fcn_content_warning'],
|
|
'name__like' => $search,
|
|
'hide_empty' => false,
|
|
'number' => 51, // Paginate (if >50, this means there are still terms to query)
|
|
'offset' => ( $page - 1 ) * 50, // Paginate
|
|
'update_term_meta_cache' => false // Improve performance
|
|
)
|
|
);
|
|
|
|
// Build and add items
|
|
foreach ( $terms as $term ) {
|
|
$taxonomy = fcnen_get_term_html_attribute( $term->taxonomy );
|
|
|
|
// Add to output
|
|
$output[] = fcnen_get_source_node(
|
|
array(
|
|
'name' => $taxonomy,
|
|
'type' => $taxonomy,
|
|
'id' => $term->term_id,
|
|
'label' => fcnen_get_term_label( $term->taxonomy ),
|
|
'title' => $term->name
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add observer?
|
|
if (
|
|
( $stories && $page < $stories->max_num_pages ) ||
|
|
( $terms && count( $terms ) > 50 )
|
|
) {
|
|
$page++;
|
|
|
|
$observer = '<li class="fcnen-dialog-modal__advanced-li _observer" data-target="fcnen-observer-item" data-page="' . $page . '"><i class="fa-solid fa-spinner fa-spin" style="--fa-animation-duration: .8s;"></i> ' . __( 'Loading…', 'fcnen' ) . '<span></span></li>';
|
|
|
|
$output[] = $observer;
|
|
}
|
|
|
|
// No results?
|
|
if ( empty( $output ) ) {
|
|
$no_matches = '<li class="fcnen-dialog-modal__advanced-li _disabled _no-match"><span>' . __( 'No matches found.', 'fcnen' ) . '</span></li>';
|
|
|
|
$output[] = $no_matches;
|
|
}
|
|
|
|
// Response
|
|
wp_send_json_success(
|
|
array(
|
|
'html' => implode( '', $output )
|
|
)
|
|
);
|
|
}
|
|
add_action( 'wp_ajax_fictioneer_ajax_fcnen_search_content', 'fictioneer_ajax_fcnen_search_content' );
|
|
add_action( 'wp_ajax_nopriv_fictioneer_ajax_fcnen_search_content', 'fictioneer_ajax_fcnen_search_content' );
|
|
|
|
// =============================================================================
|
|
// ADMIN
|
|
// =============================================================================
|
|
|
|
/**
|
|
* AJAX callback to process email queue
|
|
*
|
|
* Note: The "fictioneer_ajax_" prefix enables the plugin skipping
|
|
* in the theme's must-use-plugin.
|
|
*
|
|
* @since 0.1.0
|
|
*/
|
|
|
|
function fictioneer_ajax_fcnen_process_email_queue() {
|
|
// Verify
|
|
if ( ! wp_doing_ajax() ) {
|
|
wp_send_json_error(
|
|
array( 'error' => __( 'Invalid request.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
if ( ! check_ajax_referer( 'fcnen-process-email-queue', 'fcnen_queue_nonce', false ) ) {
|
|
wp_send_json_error(
|
|
array( 'error' => __( 'Nonce verification failed. Please reload and try again.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
wp_send_json_error(
|
|
array( 'error' => __( 'Insufficient permissions.', 'fcnen' ) )
|
|
);
|
|
}
|
|
|
|
// Setup
|
|
$index = absint( $_REQUEST['index'] ?? 0 );
|
|
$new = absint( $_REQUEST['new'] ?? 0 );
|
|
|
|
// Process
|
|
$result = fcnen_process_email_queue( $index, $new );
|
|
|
|
// Response
|
|
wp_send_json_success( $result );
|
|
}
|
|
add_action( 'wp_ajax_fictioneer_ajax_fcnen_process_email_queue', 'fictioneer_ajax_fcnen_process_email_queue' );
|
|
|
|
// =============================================================================
|
|
// FOLLOWS
|
|
// =============================================================================
|
|
|
|
/**
|
|
* AJAX Hook: Toggle story subscription by Follow
|
|
*
|
|
* @since 0.1.0
|
|
* @global wpdb $wpdb The WordPress database object.
|
|
*
|
|
* @param int $story_id ID of the toggled story Follow.
|
|
* @param bool $force Whether the Follow was added or removed.
|
|
*/
|
|
|
|
function fcnen_subscribe_by_follow( $story_id, $force ) {
|
|
global $wpdb;
|
|
|
|
// Setup
|
|
$table_name = $wpdb->prefix . 'fcnen_subscribers';
|
|
$user = wp_get_current_user();
|
|
$auth_email = get_user_meta( $user->ID, 'fcnen_subscription_email', true );
|
|
$auth_code = get_user_meta( $user->ID, 'fcnen_subscription_code', true );
|
|
|
|
// Subscription linked in profile?
|
|
if ( ! $auth_email || ! $auth_code || ! get_user_meta( $user->ID, 'fcnen_enable_subscribe_by_follow', true ) ) {
|
|
return;
|
|
}
|
|
|
|
// Valid subscriber?
|
|
$subscriber = fcnen_get_subscriber_by_email_and_code( $auth_email, $auth_code );
|
|
|
|
if ( ! $subscriber ) {
|
|
return;
|
|
}
|
|
|
|
// Update subscription post IDs
|
|
$stories = maybe_unserialize( $subscriber->post_ids );
|
|
|
|
if ( $force ) {
|
|
// Add story
|
|
$stories[] = strval( $story_id );
|
|
$stories = array_unique( $stories );
|
|
} else {
|
|
// Remove story (if set)
|
|
if ( ( $key = array_search( $story_id, $stories ) ) !== false ) {
|
|
unset( $stories[ $key ] );
|
|
}
|
|
}
|
|
|
|
// Update database
|
|
$wpdb->update(
|
|
$table_name,
|
|
array( 'post_ids' => serialize( $stories ) ),
|
|
array( 'email' => $auth_email ),
|
|
['%s'],
|
|
['%s']
|
|
);
|
|
}
|
|
|
|
if ( get_option( 'fictioneer_enable_follows' ) ) {
|
|
add_action( 'fictioneer_toggled_follow', 'fcnen_subscribe_by_follow', 10, 2 );
|
|
}
|