-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #77 from tott/feature/45-auto-retry
Feature/45 auto retry
- Loading branch information
Showing
9 changed files
with
689 additions
and
398 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
<?php | ||
|
||
/** | ||
* Class to handle Admin notices for dismissable user notifications | ||
*/ | ||
class Syndication_Logger_Admin_Notice { | ||
|
||
private static $notice_option = 'syn_notices'; | ||
private static $notice_bundles_option = 'syn_notice_bundles'; | ||
|
||
private static $dismiss_parameter = 'syn_dismiss'; | ||
|
||
public function __construct() { | ||
add_action( 'admin_init', array( __CLASS__, 'handle_dismiss_syndication_notice' ) ); | ||
add_action( 'admin_notices', array( __CLASS__, 'display_valid_notices' ) ); | ||
} | ||
|
||
/** | ||
* Create a admin notice | ||
* @param text $message_text The message you would like to show | ||
* @param string $message_type A message type, shown alongside with your message and used to categorize and bundle messages of the same type | ||
* @param string $class css class applied to the notice eg. 'updated', 'error', 'update-nag' | ||
* @param boolean $summarize_multiple setting this to true allows summarizing messages of the same message_type into one message. The message text is then passed through the syn_message_text_multiple filter and all messages of this type can be dismissed at once | ||
*/ | ||
public static function add_notice( $message_text, $message_type = 'Syndication', $class = 'updated', $summarize_multiple = false ) { | ||
$notices = get_option( self::$notice_option ); | ||
|
||
$changed = false; | ||
$message_key = md5( $message_type . $message_text ); | ||
if ( ! is_array( $notices ) || ! isset( $notices[$message_type] ) || ! isset( $notices[$message_type][$message_key] ) ) { | ||
$notices[$message_type][$message_key] = array( | ||
'message_text' => sanitize_text_field( $message_text ), | ||
'summarize_multiple' => (boolean) $summarize_multiple, | ||
'message_type' => sanitize_text_field( $message_type ), | ||
'class' => sanitize_text_field( $class ), | ||
); | ||
$changed = true; | ||
} | ||
|
||
if ( true === $changed ) { | ||
update_option( self::$notice_option, $notices ); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Evaluate and display valid notices | ||
*/ | ||
public static function display_valid_notices() { | ||
$capability = apply_filters( 'syn_syndicate_cap', 'manage_options' ); | ||
|
||
$messages = get_option( self::$notice_option ); | ||
$notice_bundles = get_option( self::$notice_bundles_option ); | ||
$messages_to_display = array(); | ||
$notice_bundles_changed = false; | ||
|
||
if ( !is_array( $messages ) || empty( $messages ) ) { | ||
return; | ||
} | ||
|
||
foreach( $messages as $message_type => $message_values ) { | ||
foreach( $message_values as $message_key => $message_data ) { | ||
if ( isset( $message_data['summarize_multiple'] ) && true === $message_data['summarize_multiple'] ) { | ||
$message_text = apply_filters( 'syn_message_text_multiple', $message_data['message_text'], $message_data ); | ||
} else { | ||
$message_text = apply_filters( 'syn_message_text', $message_data['message_text'], $message_data ); | ||
} | ||
|
||
$new_message_key = md5( $message_type . $message_text ); | ||
$new_message_data = array( | ||
'message_text' => sanitize_text_field( $message_text ), | ||
'summarize_multiple' => (boolean) $message_data['summarize_multiple'], | ||
'message_type' => sanitize_text_field( $message_data['message_type'] ), | ||
'class' => sanitize_text_field( $message_data['class'] ) | ||
); | ||
|
||
if ( $new_message_key != $message_key ) { | ||
if ( ! isset( $notice_bundles[$new_message_key] ) || ! in_array( $message_key, $notice_bundles[$new_message_key] ) ) { | ||
$notice_bundles[$new_message_key][] = $message_key; | ||
$notice_bundles_changed = true; | ||
} | ||
} | ||
|
||
if ( current_user_can( $capability ) ) { | ||
$messages_to_display[$message_type][$new_message_key] = $new_message_data; | ||
} | ||
} | ||
} | ||
|
||
if ( true === $notice_bundles_changed ) { | ||
update_option( self::$notice_bundles_option, $notice_bundles ); | ||
} | ||
|
||
foreach( $messages_to_display as $message_type => $message_values ) { | ||
foreach( $message_values as $message_key => $message_data ) { | ||
$dismiss_nonce = wp_create_nonce( esc_attr( $message_key ) ); | ||
printf( '<div class="%s"><p>', esc_attr( $message_data['class'] ) ); | ||
printf( __('%1$s : %2$s <a href="%3$s">Hide Notice</a>'), esc_html( $message_type ), wp_kses_post( $message_data['message_text'] ), add_query_arg( array( self::$dismiss_parameter => esc_attr( $message_key ), 'syn_dismiss_nonce' => esc_attr( $dismiss_nonce ) ) ) ); | ||
printf( '</p></div>' ); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Handle dismissing of notices | ||
*/ | ||
public static function handle_dismiss_syndication_notice() { | ||
$capability = apply_filters( 'syn_syndicate_cap', 'manage_options' ); | ||
|
||
// add nonce | ||
if ( isset( $_GET[self::$dismiss_parameter] ) && current_user_can( $capability ) ) { | ||
|
||
$dismiss_key = esc_attr( $_GET[self::$dismiss_parameter] ); | ||
$dismiss_nonce = esc_attr( $_GET['syn_dismiss_nonce'] ); | ||
if ( ! wp_verify_nonce( $dismiss_nonce, $dismiss_key ) ) { | ||
wp_die( __( "Invalid security check" ) ); | ||
} | ||
$messages = get_option( self::$notice_option ); | ||
$notice_bundles = get_option( self::$notice_bundles_option ); | ||
|
||
$dismiss_items = array(); | ||
if ( isset( $notice_bundles[$dismiss_key] ) ) { | ||
$dismiss_items = $notice_bundles[$dismiss_key]; | ||
} else { | ||
$dismiss_items = array( $dismiss_key ); | ||
} | ||
|
||
foreach( $messages as $message_type => $message_values ) { | ||
$message_keys = array_keys( $message_values ); | ||
$dismiss_it = array_intersect( $message_keys, $dismiss_items ); | ||
foreach( $dismiss_it as $dismiss_it_key ) { | ||
unset( $messages[$message_type][$dismiss_it_key] ); | ||
} | ||
} | ||
|
||
if ( isset( $notice_bundles[$dismiss_key] ) ) { | ||
unset( $notice_bundles[$dismiss_key] ); | ||
} | ||
|
||
update_option( self::$notice_option, $messages ); | ||
update_option( self::$notice_bundles_option, $notice_bundles ); | ||
|
||
} | ||
} | ||
} | ||
|
||
add_filter( 'syn_message_text_multiple', 'syn_handle_multiple_error_notices', 10, 2 ); | ||
function syn_handle_multiple_error_notices( $message, $message_data ) { | ||
return __( 'There have been multiple errors. Please validate your syndication logs' ); | ||
} | ||
|
||
add_action( 'push_syndication_site_disabled', 'syn_add_site_disabled_notice', 10, 2 ); | ||
function syn_add_site_disabled_notice( $site_id, $count ) { | ||
Syndication_Logger_Admin_Notice::add_notice( $message_text = sprintf( __( 'Site %d disabled after %d pull failure(s).', 'push-syndication' ), (int) $site_id, (int) $count ), $message_type = 'Syndication site disabled', $class = 'error', $summarize_multiple = false ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<?php | ||
|
||
/** | ||
* Failed Syndication Auto Retry | ||
* | ||
* Watches syndication events and handles site-related failures. | ||
* | ||
* There is a cron setup in class-wp-push-syndication-server.php:1266 which will | ||
* retry a failed pull X times (set in admin settings) and when that limit is met | ||
* the site becomes 'disabled'. The retry interval could be anything from 5min to 1hr | ||
* or longer. | ||
* | ||
* After each failed retry we don't want to wait the retry interval to try again | ||
* if the remote server was simply unreachable for a brief spell. This auto retry | ||
* bypasses the retry interval and retries the pull once a minute up to 3 tries. | ||
* | ||
* @uses Syndication_Logger | ||
*/ | ||
|
||
class Failed_Syndication_Auto_Retry { | ||
|
||
/** | ||
* Hook into WordPress | ||
*/ | ||
public function __construct() { | ||
|
||
// Watch the push_syndication_event action for site pull failures | ||
add_action( 'push_syndication_after_event_pull_failure', array( $this, 'handle_pull_failure_event' ), 10, 2 ); | ||
|
||
// Watch the push_syndication_event action for site pull successes | ||
add_action( 'push_syndication_after_event_pull_success', array( $this, 'handle_pull_success_event' ), 10, 2 ); | ||
} | ||
|
||
/** | ||
* Handle a site pull failure event | ||
* | ||
* @param $site_id int The post id of the site we need to retry | ||
* @param $failed_attempts int The number of pull failures this site has experienced | ||
* | ||
* @return null | ||
*/ | ||
public function handle_pull_failure_event( $site_id = 0, $failed_attempts = 0 ) { | ||
|
||
$site_auto_retry_count = 0; | ||
$site_id = (int) $site_id; | ||
$failed_attempts = (int) $failed_attempts; | ||
$cleanup = false; | ||
|
||
// Fetch the allowable number of max pull attempts before the site is marked as 'disabled' | ||
$max_pull_attempts = (int) get_option( 'push_syndication_max_pull_attempts', 0 ); | ||
|
||
// Bail if we've already met the max pull attempt count | ||
if ( ! $max_pull_attempts ) { | ||
return; | ||
} | ||
|
||
// Only proceed if we have a valid site id | ||
if ( 0 !== $site_id ) { | ||
|
||
// Fetch the site post | ||
$site = get_post( $site_id ); | ||
|
||
// Fetch the site url | ||
$site_url = get_post_meta( $site->ID, 'syn_feed_url', true ); | ||
|
||
// Fetch the number of times we've tried to auto-retry | ||
$site_auto_retry_count = (int) get_post_meta( $site_id, 'syn_failed_auto_retry_attempts', true ); | ||
|
||
// Only proceed if we haven't hit the pull attempt ceiling | ||
if ( $failed_attempts < $max_pull_attempts ) { | ||
|
||
// Allow the default auto retry to be filtered | ||
// By default, only auto retry 3 times | ||
$auto_retry_limit = apply_filters( 'pull_syndication_failure_auto_retry_limit', 3 ); | ||
|
||
// Store the current time for repeated use below | ||
$time_now = time(); | ||
|
||
// Create a string time to be sent to the logger | ||
// Add 1 so our log items appear to occur a second later | ||
// and hence order better in the log viewer | ||
// without this, sometimes when the pull occurs quickly | ||
// these log items appear to occur at the same time as the failure | ||
$log_time = date( 'Y-m-d H:i:s', $time_now + 1 ); | ||
|
||
// Are we still below the auto retry limit? | ||
if ( $site_auto_retry_count < $auto_retry_limit ) { | ||
|
||
// Yes we are.. | ||
|
||
// Run in one minute by default | ||
$auto_retry_interval = apply_filters( 'syndication_failure_auto_retry_interval', $time_now + MINUTE_IN_SECONDS ); | ||
|
||
Syndication_Logger::log_post_info( $site->ID, $status = 'start_auto_retry', $message = sprintf( __( 'Connection retry %d of %d to %s in %s..', 'push-syndication' ), $site_auto_retry_count + 1, $auto_retry_limit, $site_url, human_time_diff( $time_now, $auto_retry_interval ) ), $log_time, $extra = array() ); | ||
|
||
// Schedule a pull retry for one minute in the future | ||
wp_schedule_single_event( | ||
$auto_retry_interval, // retry in X time | ||
'syn_pull_content', // fire the syndication_auto_retry hook | ||
array( array( $site ) ) // the site which failed to pull | ||
); | ||
|
||
// Increment our auto retry counter | ||
$site_auto_retry_count++; | ||
|
||
// And update the post meta auto retry count | ||
update_post_meta( $site->ID, 'syn_failed_auto_retry_attempts', $site_auto_retry_count ); | ||
|
||
} else { | ||
|
||
// Auto Retry limit met | ||
// Let's cleanup after ourselves | ||
$cleanup = true ; | ||
} | ||
} else { | ||
|
||
// Retry attempt limit met | ||
// The site has been disabled, let's cleanup after ourselves | ||
$cleanup = true; | ||
} | ||
|
||
// Should we cleanup after ourselves? | ||
if ( $cleanup ) { | ||
|
||
// Remove the auto retry if there was one | ||
delete_post_meta( $site->ID, 'syn_failed_auto_retry_attempts' ); | ||
|
||
Syndication_Logger::log_post_error( $site->ID, $status = 'end_auto_retry', $message = sprintf( __( 'Failed %d times to reconnect to %s', 'push-syndication' ), $site_auto_retry_count, $site_url ), $log_time, $extra = array() ); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Handle a site pull success event | ||
* | ||
* @param $site_id int The post id of the site which just successfully pulled | ||
* @param $failed_attempts int The number of pull failures this site has experienced | ||
* @return null | ||
*/ | ||
public function handle_pull_success_event( $site_id = 0, $failed_attempts = 0 ) { | ||
|
||
// Remove the auto retry if there was one | ||
delete_post_meta( $site_id, 'syn_failed_auto_retry_attempts' ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.