From 81bfab87057e6944e287238225fc5260fa450bcf Mon Sep 17 00:00:00 2001 From: Nikola Date: Thu, 5 Oct 2017 16:27:48 +0200 Subject: [PATCH] Add updater code --- includes/CMB2/includes/CMB2_Utils.php | 2 +- includes/class-sm-background-updater.php | 107 ++++ includes/class-sm-dates-wp.php | 6 +- includes/class-sm-error-recovery.php | 16 +- includes/class-sm-install.php | 195 +++++++ includes/libraries/wp-async-request.php | 158 ++++++ includes/libraries/wp-background-process.php | 502 +++++++++++++++++++ includes/options.php | 59 +-- includes/podcast-functions.php | 2 +- includes/sm-deprecated-functions.php | 7 + includes/sm-update-functions.php | 98 ++++ includes/template-tags.php | 21 +- includes/types-taxonomies.php | 2 +- readme.txt | 3 +- sermons.php | 141 +----- 15 files changed, 1116 insertions(+), 203 deletions(-) create mode 100644 includes/class-sm-background-updater.php create mode 100644 includes/class-sm-install.php create mode 100644 includes/libraries/wp-async-request.php create mode 100644 includes/libraries/wp-background-process.php create mode 100644 includes/sm-update-functions.php diff --git a/includes/CMB2/includes/CMB2_Utils.php b/includes/CMB2/includes/CMB2_Utils.php index be3d32a..9abaae8 100755 --- a/includes/CMB2/includes/CMB2_Utils.php +++ b/includes/CMB2/includes/CMB2_Utils.php @@ -260,7 +260,7 @@ public static function array_insert( &$array, $new, $position ) { */ public static function url( $path = '' ) { // SM: Seems like CMB2 doesn't return a correct URL. This should fix it. - return str_replace( get_site_url(), '', SERMON_MANAGER_URL ) . 'includes/CMB2/' . $path; + return str_replace( get_site_url(), '', SM_URL ) . 'includes/CMB2/' . $path; if ( self::$url ) { return self::$url . $path; diff --git a/includes/class-sm-background-updater.php b/includes/class-sm-background-updater.php new file mode 100644 index 0000000..ebe6189 --- /dev/null +++ b/includes/class-sm-background-updater.php @@ -0,0 +1,107 @@ +is_process_running() ) { + // Background process already running. + return; + } + + if ( $this->is_queue_empty() ) { + // No data to process. + $this->clear_scheduled_event(); + + return; + } + + $this->handle(); + } + + /** + * Schedule fallback event. + */ + protected function schedule_event() { + if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) { + wp_schedule_event( time() + 10, $this->cron_interval_identifier, $this->cron_hook_identifier ); + } + } + + /** + * Is the updater running? + * + * @return boolean + */ + public function is_updating() { + return false === $this->is_queue_empty(); + } + + /** + * Task + * + * Override this method to perform any actions required on each + * queue item. Return the modified item for further processing + * in the next pass through. Or, return false to remove the + * item from the queue. + * + * @param string $callback Update callback function + * + * @return mixed + */ + protected function task( $callback ) { + if ( ! defined( 'SM_UPDATING' ) ) { + define( 'SM_UPDATING', true ); + } + + include_once( dirname( __FILE__ ) . '/sm-update-functions.php' ); + + if ( is_callable( $callback ) ) { + call_user_func( $callback ); + } + + return false; + } + + /** + * Complete + * + * Override if applicable, but ensure that the below actions are + * performed, or, call parent::complete(). + */ + protected function complete() { + SM_Install::update_db_version(); + parent::complete(); + } +} diff --git a/includes/class-sm-dates-wp.php b/includes/class-sm-dates-wp.php index c1fab5e..7a470b4 100644 --- a/includes/class-sm-dates-wp.php +++ b/includes/class-sm-dates-wp.php @@ -110,8 +110,10 @@ public static function update_series_date() { } } - arsort( $dates ); - update_term_meta( $term->term_id, 'sermon_date', $dates[0] ); + if ( ! empty( $dates ) ) { + arsort( $dates ); + update_term_meta( $term->term_id, 'sermon_date', $dates[0] ); + } } } } diff --git a/includes/class-sm-error-recovery.php b/includes/class-sm-error-recovery.php index 86b7546..6e83d6c 100644 --- a/includes/class-sm-error-recovery.php +++ b/includes/class-sm-error-recovery.php @@ -1,4 +1,5 @@ urlencode( str_replace( ABSPATH, '~/', get_option( '_sm_recovery_last_fatal_error' ) ) ), - 'environment_info' => 'WordPress: ' . $GLOBALS['wp_version'] . '; Server: ' . ( function_exists( 'apache_get_version' ) ? apache_get_version() : 'N/A' ) . '; PHP: ' . PHP_VERSION . '; Sermon Manager:' . SERMON_MANAGER_VERSION . ';', + 'environment_info' => 'WordPress: ' . $GLOBALS['wp_version'] . '; Server: ' . ( function_exists( 'apache_get_version' ) ? apache_get_version() : 'N/A' ) . '; PHP: ' . PHP_VERSION . '; Sermon Manager:' . SM_VERSION . ';', 'plugin_name' => $plugin_data['Name'], ) ); - wp_enqueue_style( 'sm-error-recovery', SERMON_MANAGER_URL . 'assets/css/error-recovery.css', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_style( 'sm-error-recovery', SM_URL . 'assets/css/error-recovery.css', array(), SM_VERSION ); } /** @@ -281,14 +282,9 @@ public static function disable_send_report_button() { */ public static function upgrade_check() { $db_version = get_option( 'sm_version' ); - if ( empty( $db_version ) || $db_version != SERMON_MANAGER_VERSION ) { + if ( empty( $db_version ) || $db_version != SM_VERSION ) { update_option( '_sm_recovery_do_not_catch', 0 ); update_option( '_sm_recovery_disable', 0 ); - update_option( 'sm_version', SERMON_MANAGER_VERSION ); - - // Flush rewrite rules after update - add_action( 'sm_after_register_post_type', array( 'SM_Post_Types', 'flush_rewrite_rules_hard' ) ); - add_action( 'sm_after_register_taxonomy', array( 'SM_Post_Types', 'flush_rewrite_rules_hard' ) ); } } diff --git a/includes/class-sm-install.php b/includes/class-sm-install.php new file mode 100644 index 0000000..c0a69d1 --- /dev/null +++ b/includes/class-sm-install.php @@ -0,0 +1,195 @@ + array( + 'sm_update_28_revert_old_dates', + 'sm_update_28_convert_dates_to_unix', + 'sm_update_28_fill_out_empty_dates', + 'sm_update_28_fill_out_series_dates', + 'sm_update_28_save_sermon_render_into_post_content', + 'sm_update_28_reset_recovery', + ) + ); + + /** @var object Background update class */ + private static $background_updater; + + public static function init() { + add_action( 'init', array( __CLASS__, 'init_background_updater' ), 3 ); + add_action( 'init', array( __CLASS__, 'check_version' ), 8 ); + add_filter( 'plugin_action_links_' . SM_BASENAME, array( __CLASS__, 'plugin_action_links' ) ); + add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 ); + add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) ); + } + + /** + * Check Sermon Manager version and run the updater is required + * + * This check is done on all requests and runs if the versions do not match + */ + public static function check_version() { + if ( ! defined( 'IFRAME_REQUEST' ) && get_option( 'sm_version' ) !== SM_VERSION ) { + self::_install(); + do_action( 'sm_updated' ); + } + } + + /** + * Install Sermon Manager + */ + private static function _install() { + global $wpdb; + + if ( ! is_blog_installed() ) { + return; + } + + if ( ! defined( 'SM_INSTALLING' ) ) { + define( 'SM_INSTALLING', true ); + } + + //self::_create_options(); todo: will be done in future versions, every option will have its own field in the database, so we will just use `add_option()` - it won't overwrite the field + //self::_create_roles(); todo: will be done in future versions + + // Register post types + SM_Post_types::register_post_types(); + SM_Post_types::register_taxonomies(); + + // do update + self::_update(); + + // Update version just in case + self::update_db_version(); + + // Flush rules after install + do_action( 'sm_flush_rewrite_rules' ); + + /* + * Deletes all expired transients. The multi-table delete syntax is used + * to delete the transient record from table a, and the corresponding + * transient_timeout record from table b. + * + * Based on code inside core's upgrade_network() function. + */ + /** @noinspection SqlNoDataSourceInspection */ + $sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b + WHERE a.option_name LIKE %s + AND a.option_name NOT LIKE %s + AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) + AND b.option_value < %d"; + $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) ); + + // Trigger action + do_action( 'sm_installed' ); + } + + /** + * Push all needed DB updates to the queue for processing. + */ + private static function _update() { + $current_db_version = get_option( 'sm_version' ); + $update_queued = false; + + foreach ( self::_get_db_update_callbacks() as $version => $update_callbacks ) { + if ( version_compare( $current_db_version, $version, '<' ) ) { + foreach ( $update_callbacks as $update_callback ) { + self::$background_updater->push_to_queue( $update_callback ); + $update_queued = true; + } + } + } + + if ( $update_queued ) { + self::$background_updater->save()->dispatch(); + } + } + + /** + * Get list of DB update callbacks. + * + * @return array + */ + private static function _get_db_update_callbacks() { + return self::$db_updates; + } + + /** + * Init background updates + */ + public static function init_background_updater() { + include_once 'class-sm-background-updater.php'; + self::$background_updater = new SM_Background_Updater(); + } + + /** + * Update DB version to current. + * + * @param string $version (optional) + */ + public static function update_db_version( $version = null ) { + delete_option( 'sm_version' ); + add_option( 'sm_version', is_null( $version ) ? SM_VERSION : $version ); + } + + /** + * Add more cron schedules + * + * @param array $schedules + * + * @return array + */ + public static function cron_schedules( $schedules ) { + $schedules['monthly'] = array( + 'interval' => 2635200, + 'display' => __( 'Monthly', 'sermon-manager-for-wordpress' ), + ); + + return $schedules; + } + + /** + * Show action links on the plugin screen. + * + * @param mixed $links Plugin Action links + * + * @return array + */ + public static function plugin_action_links( $links ) { + $action_links = array( + 'settings' => '' . esc_html__( 'Settings' ) . '', + ); + + return array_merge( $action_links, $links ); + } + + /** + * Show row meta on the plugin screen. + * + * @param mixed $links Plugin Row Meta + * @param mixed $file Plugin Base file + * + * @return array + */ + public static function plugin_row_meta( $links, $file ) { + /** @noinspection PhpUndefinedConstantInspection */ + if ( SM_BASENAME == $file ) { + $row_meta = array( + 'support' => '' . esc_html__( 'Premium support', 'sermon-manager-for-wordpress' ) . '', + ); + + return array_merge( $links, $row_meta ); + } + + return (array) $links; + } +} + +SM_Install::init(); diff --git a/includes/libraries/wp-async-request.php b/includes/libraries/wp-async-request.php new file mode 100644 index 0000000..a49a5a2 --- /dev/null +++ b/includes/libraries/wp-async-request.php @@ -0,0 +1,158 @@ +identifier = $this->prefix . '_' . $this->action; + + add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) ); + add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) ); + } + + /** + * Set data used during the request + * + * @param array $data Data. + * + * @return $this + */ + public function data( $data ) { + $this->data = $data; + + return $this; + } + + /** + * Dispatch the async request + * + * @return array|WP_Error + */ + public function dispatch() { + $url = add_query_arg( $this->get_query_args(), $this->get_query_url() ); + $args = $this->get_post_args(); + + return wp_remote_post( esc_url_raw( $url ), $args ); + } + + /** + * Get query args + * + * @return array + */ + protected function get_query_args() { + if ( property_exists( $this, 'query_args' ) ) { + return $this->query_args; + } + + return array( + 'action' => $this->identifier, + 'nonce' => wp_create_nonce( $this->identifier ), + ); + } + + /** + * Get query URL + * + * @return string + */ + protected function get_query_url() { + if ( property_exists( $this, 'query_url' ) ) { + return $this->query_url; + } + + return admin_url( 'admin-ajax.php' ); + } + + /** + * Get post args + * + * @return array + */ + protected function get_post_args() { + if ( property_exists( $this, 'post_args' ) ) { + return $this->post_args; + } + + return array( + 'timeout' => 0.01, + 'blocking' => false, + 'body' => $this->data, + 'cookies' => $_COOKIE, + 'sslverify' => apply_filters( 'https_local_ssl_verify', false ), + ); + } + + /** + * Maybe handle + * + * Check for correct nonce and pass to handler. + */ + public function maybe_handle() { + // Don't lock up other requests while processing + session_write_close(); + + check_ajax_referer( $this->identifier, 'nonce' ); + + $this->handle(); + + wp_die(); + } + + /** + * Handle + * + * Override this method to perform any actions required + * during the async request. + */ + abstract protected function handle(); +} diff --git a/includes/libraries/wp-background-process.php b/includes/libraries/wp-background-process.php new file mode 100644 index 0000000..1323ee5 --- /dev/null +++ b/includes/libraries/wp-background-process.php @@ -0,0 +1,502 @@ +cron_hook_identifier = $this->identifier . '_cron'; + $this->cron_interval_identifier = $this->identifier . '_cron_interval'; + + add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) ); + add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) ); + } + + /** + * Dispatch + * + * @access public + * @return array|WP_Error + */ + public function dispatch() { + // Schedule the cron healthcheck. + $this->schedule_event(); + + // Perform remote post. + return parent::dispatch(); + } + + /** + * Push to queue + * + * @param mixed $data Data. + * + * @return $this + */ + public function push_to_queue( $data ) { + $this->data[] = $data; + + return $this; + } + + /** + * Save queue + * + * @return $this + */ + public function save() { + $key = $this->generate_key(); + + if ( ! empty( $this->data ) ) { + update_site_option( $key, $this->data ); + } + + return $this; + } + + /** + * Update queue + * + * @param string $key Key. + * @param array $data Data. + * + * @return $this + */ + public function update( $key, $data ) { + if ( ! empty( $data ) ) { + update_site_option( $key, $data ); + } + + return $this; + } + + /** + * Delete queue + * + * @param string $key Key. + * + * @return $this + */ + public function delete( $key ) { + delete_site_option( $key ); + + return $this; + } + + /** + * Generate key + * + * Generates a unique key based on microtime. Queue items are + * given a unique key so that they can be merged upon save. + * + * @param int $length Length. + * + * @return string + */ + protected function generate_key( $length = 64 ) { + $unique = md5( microtime() . rand() ); + $prepend = $this->identifier . '_batch_'; + + return substr( $prepend . $unique, 0, $length ); + } + + /** + * Maybe process queue + * + * Checks whether data exists within the queue and that + * the process is not already running. + */ + public function maybe_handle() { + // Don't lock up other requests while processing + session_write_close(); + + if ( $this->is_process_running() ) { + // Background process already running. + wp_die(); + } + + if ( $this->is_queue_empty() ) { + // No data to process. + wp_die(); + } + + check_ajax_referer( $this->identifier, 'nonce' ); + + $this->handle(); + + wp_die(); + } + + /** + * Is queue empty + * + * @return bool + */ + protected function is_queue_empty() { + global $wpdb; + + $table = $wpdb->options; + $column = 'option_name'; + + if ( is_multisite() ) { + $table = $wpdb->sitemeta; + $column = 'meta_key'; + } + + $key = $this->identifier . '_batch_%'; + + $count = $wpdb->get_var( $wpdb->prepare( " + SELECT COUNT(*) + FROM {$table} + WHERE {$column} LIKE %s + ", $key ) ); + + return ( $count > 0 ) ? false : true; + } + + /** + * Is process running + * + * Check whether the current process is already running + * in a background process. + */ + protected function is_process_running() { + if ( get_site_transient( $this->identifier . '_process_lock' ) ) { + // Process already running. + return true; + } + + return false; + } + + /** + * Lock process + * + * Lock the process so that multiple instances can't run simultaneously. + * Override if applicable, but the duration should be greater than that + * defined in the time_exceeded() method. + */ + protected function lock_process() { + $this->start_time = time(); // Set start time of current process. + + $lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute + $lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration ); + + set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration ); + } + + /** + * Unlock process + * + * Unlock the process so that other instances can spawn. + * + * @return $this + */ + protected function unlock_process() { + delete_site_transient( $this->identifier . '_process_lock' ); + + return $this; + } + + /** + * Get batch + * + * @return stdClass Return the first batch from the queue + */ + protected function get_batch() { + global $wpdb; + + $table = $wpdb->options; + $column = 'option_name'; + $key_column = 'option_id'; + $value_column = 'option_value'; + + if ( is_multisite() ) { + $table = $wpdb->sitemeta; + $column = 'meta_key'; + $key_column = 'meta_id'; + $value_column = 'meta_value'; + } + + $key = $this->identifier . '_batch_%'; + + $query = $wpdb->get_row( $wpdb->prepare( " + SELECT * + FROM {$table} + WHERE {$column} LIKE %s + ORDER BY {$key_column} ASC + LIMIT 1 + ", $key ) ); + + $batch = new stdClass(); + $batch->key = $query->$column; + $batch->data = maybe_unserialize( $query->$value_column ); + + return $batch; + } + + /** + * Handle + * + * Pass each queue item to the task handler, while remaining + * within server memory and time limit constraints. + */ + protected function handle() { + $this->lock_process(); + + do { + $batch = $this->get_batch(); + + foreach ( $batch->data as $key => $value ) { + $task = $this->task( $value ); + + if ( false !== $task ) { + $batch->data[ $key ] = $task; + } else { + unset( $batch->data[ $key ] ); + } + + if ( $this->time_exceeded() || $this->memory_exceeded() ) { + // Batch limits reached. + break; + } + } + + // Update or delete current batch. + if ( ! empty( $batch->data ) ) { + $this->update( $batch->key, $batch->data ); + } else { + $this->delete( $batch->key ); + } + } while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() ); + + $this->unlock_process(); + + // Start next batch or complete process. + if ( ! $this->is_queue_empty() ) { + $this->dispatch(); + } else { + $this->complete(); + } + } + + /** + * Memory exceeded + * + * Ensures the batch process never exceeds 90% + * of the maximum WordPress memory. + * + * @return bool + */ + protected function memory_exceeded() { + $memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory + $current_memory = memory_get_usage( true ); + $return = false; + + if ( $current_memory >= $memory_limit ) { + $return = true; + } + + return apply_filters( $this->identifier . '_memory_exceeded', $return ); + } + + /** + * Get memory limit + * + * @return int + */ + protected function get_memory_limit() { + if ( function_exists( 'ini_get' ) ) { + $memory_limit = ini_get( 'memory_limit' ); + } else { + // Sensible default. + $memory_limit = '128M'; + } + + if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) { + // Unlimited, set to 32GB. + $memory_limit = '32000M'; + } + + return intval( $memory_limit ) * 1024 * 1024; + } + + /** + * Time exceeded. + * + * Ensures the batch never exceeds a sensible time limit. + * A timeout limit of 30s is common on shared hosting. + * + * @return bool + */ + protected function time_exceeded() { + $finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds + $return = false; + + if ( time() >= $finish ) { + $return = true; + } + + return apply_filters( $this->identifier . '_time_exceeded', $return ); + } + + /** + * Complete. + * + * Override if applicable, but ensure that the below actions are + * performed, or, call parent::complete(). + */ + protected function complete() { + // Unschedule the cron healthcheck. + $this->clear_scheduled_event(); + } + + /** + * Schedule cron healthcheck + * + * @access public + * + * @param mixed $schedules Schedules. + * + * @return mixed + */ + public function schedule_cron_healthcheck( $schedules ) { + $interval = apply_filters( $this->identifier . '_cron_interval', 5 ); + + if ( property_exists( $this, 'cron_interval' ) ) { + $interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval_identifier ); + } + + // Adds every 5 minutes to the existing schedules. + $schedules[ $this->identifier . '_cron_interval' ] = array( + 'interval' => MINUTE_IN_SECONDS * $interval, + 'display' => sprintf( __( 'Every %d minutes', 'sermon-manager' ), $interval ), + ); + + return $schedules; + } + + /** + * Handle cron healthcheck + * + * Restart the background process if not already running + * and data exists in the queue. + */ + public function handle_cron_healthcheck() { + if ( $this->is_process_running() ) { + // Background process already running. + exit; + } + + if ( $this->is_queue_empty() ) { + // No data to process. + $this->clear_scheduled_event(); + exit; + } + + $this->handle(); + + exit; + } + + /** + * Schedule event + */ + protected function schedule_event() { + if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) { + wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier ); + } + } + + /** + * Clear scheduled event + */ + protected function clear_scheduled_event() { + $timestamp = wp_next_scheduled( $this->cron_hook_identifier ); + + if ( $timestamp ) { + wp_unschedule_event( $timestamp, $this->cron_hook_identifier ); + } + } + + /** + * Cancel Process + * + * Stop processing queue items, clear cronjob and delete batch. + * + */ + public function cancel_process() { + if ( ! $this->is_queue_empty() ) { + $batch = $this->get_batch(); + + $this->delete( $batch->key ); + + wp_clear_scheduled_hook( $this->cron_hook_identifier ); + } + + } + + /** + * Task + * + * Override this method to perform any actions required on each + * queue item. Return the modified item for further processing + * in the next pass through. Or, return false to remove the + * item from the queue. + * + * @param mixed $item Queue item to iterate over. + * + * @return mixed + */ + abstract protected function task( $item ); + +} diff --git a/includes/options.php b/includes/options.php index fe7bfe8..f5b8cb1 100755 --- a/includes/options.php +++ b/includes/options.php @@ -7,7 +7,6 @@ class Sermon_Manager_Settings { - /* Construct */ public function __construct() { // Flush rewrite rules before everything else (if required) add_action( 'init', array( $this, 'maybe_flush_rewrite_rules' ) ); @@ -15,10 +14,6 @@ public function __construct() { add_action( 'admin_init', array( $this, 'wpfc_init' ) ); // Settings Menu Page add_action( 'admin_menu', array( $this, 'wpfc_add_options_page' ) ); - // Link for Settings on Plugin Page - add_filter( 'plugin_action_links', array( $this, 'wpfc_plugin_action_links' ), 10, 2 ); - // Plugin Meta Links - add_filter( 'plugin_row_meta', array( $this, 'wpfc_sermon_manager_plugin_row_meta' ), 10, 2 ); } static function wpfc_validate_options( $input ) { @@ -42,8 +37,6 @@ static function wpfc_validate_options( $input ) { return $input; } - // Init plugin options to white list our options - /** * Checks if archive slug has changed and flushes rewrite rules if necessary * @@ -56,8 +49,6 @@ function maybe_flush_rewrite_rules() { } } - // Add menu page - function wpfc_init() { global $wp_version; @@ -76,8 +67,6 @@ function wpfc_init() { } } - // Plugin Meta Links. - function wpfc_add_options_page() { $page = add_submenu_page( 'edit.php?post_type=wpfc_sermon', __( 'Sermon Manager Settings', 'sermon-manager-for-wordpress' ), __( 'Settings', 'sermon-manager-for-wordpress' ), 'manage_options', __FILE__, array( $this, @@ -86,31 +75,6 @@ function wpfc_add_options_page() { add_action( 'admin_print_styles-' . $page, array( $this, 'wpfc_sermon_admin_styles' ) ); } - // Settings Page Link. - - function wpfc_sermon_manager_plugin_row_meta( $links, $file ) { - static $plugin_name = ''; - - if ( empty( $plugin_name ) ) { - $plugin_name = plugin_basename( __FILE__ ); - } - - if ( $plugin_name != $file ) { - return $links; - } - - $link = wpfc_sermon_manager_settings_page_link( __( 'Settings', 'sermon-manager-for-wordpress' ) ); - if ( ! empty( $link ) ) { - $links[] = $link; - } - - $links[] = '' . esc_html__( 'Support', 'sermon-manager-for-wordpress' ) . ''; - - return $links; - } - - // Add scripts - function wpfc_sermon_manager_settings_page_link( $link_text = '' ) { if ( empty( $link_text ) ) { $link_text = __( 'Manage Settings', 'sermon-manager-for-wordpress' ); @@ -118,14 +82,12 @@ function wpfc_sermon_manager_settings_page_link( $link_text = '' ) { $link = ''; if ( current_user_can( 'manage_options' ) ) { - $link = '' . esc_html( $link_text ) . ''; + $link = '' . esc_html( $link_text ) . ''; } return $link; } - // Render the Plugin options form - function wpfc_sermon_admin_styles() { wp_enqueue_script( 'media-upload' ); wp_enqueue_script( 'jquery-ui-tabs' ); @@ -134,8 +96,6 @@ function wpfc_sermon_admin_styles() { wp_enqueue_script( 'jquery-ui-droppable' ); } - // Sanitize and validate input. Accepts an array, return a sanitized array. - function wpfc_sermon_options_render_form() { if ( ! isset( $_REQUEST['settings-updated'] ) ) { $_REQUEST['settings-updated'] = false; @@ -396,13 +356,13 @@ function wpfc_sermon_options_render_form() { } ?>/>
- + + value=""/> @@ -753,19 +713,6 @@ class="itunes_cover_image_field" ' . esc_html__( 'Settings', 'sermon-manager-for-wordpress' ) . ''; - // make the 'Settings' link appear first - array_unshift( $links, $wpfc_links ); - } - - return $links; - } } $Sermon_Manager_Settings = new Sermon_Manager_Settings(); diff --git a/includes/podcast-functions.php b/includes/podcast-functions.php index fae7f85..594286d 100644 --- a/includes/podcast-functions.php +++ b/includes/podcast-functions.php @@ -73,7 +73,7 @@ function wpfc_podcast_render() { if ( $overridden_template = locate_template( 'wpfc-podcast-feed.php' ) ) { load_template( $overridden_template ); } else { - load_template( SERMON_MANAGER_PATH . 'views/wpfc-podcast-feed.php' ); + load_template( SM_PATH . 'views/wpfc-podcast-feed.php' ); } } diff --git a/includes/sm-deprecated-functions.php b/includes/sm-deprecated-functions.php index 3c68f3b..fd5d3bb 100644 --- a/includes/sm-deprecated-functions.php +++ b/includes/sm-deprecated-functions.php @@ -7,6 +7,13 @@ defined( 'ABSPATH' ) or die; // exit if accessed directly +// deprecated +define( 'SM___FILE__', __FILE__ ); +define( 'SERMON_MANAGER_PATH', SM_PATH ); +define( 'SERMON_MANAGER_URL', SM_URL ); +define( 'SERMON_MANAGER_VERSION', SM_VERSION ); + + /** * Outputs Sermon date. Wrapper for sm_the_date() * diff --git a/includes/sm-update-functions.php b/includes/sm-update-functions.php new file mode 100644 index 0000000..21b60f0 --- /dev/null +++ b/includes/sm-update-functions.php @@ -0,0 +1,98 @@ +get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('auto-draft', 'inherit')", 'wpfc_sermon' ) ) as $sermon ) { + if ( get_post_meta( $sermon->ID, 'sermon_date', true ) === '' && + $date = get_post_meta( $sermon->ID, 'sermon_date_old', true ) !== '' ) { + update_post_meta( $sermon->ID, 'sermon_date', is_numeric( $date ) ?: strtotime( $date ) ); + delete_post_meta( $sermon->ID, 'sermon_date_old' ); + } + } + + // clear all cached data + wp_cache_flush(); +} + +/** + * Final dates conversion for users who skipped converters in previous SM versions + * + * Basically, converts "sermon_date" value to Unix time if it's not numeric + */ +function sm_update_28_convert_dates_to_unix() { + global $wpdb; + + // All sermons + $sermons = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('auto-draft', 'inherit')", 'wpfc_sermon' ) ); + + foreach ( $sermons as $sermon ) { + if ( $date = get_post_meta( $sermon->ID, 'sermon_date', true ) ) { + if ( ! is_numeric( $date ) ) { + update_post_meta( $sermon->ID, 'sermon_date', strtotime( $date ) ); + } + } + } + + // clear all cached data + wp_cache_flush(); +} + +/** + * Fills out dates of sermons that don't have `sermon_date` set. Takes "Published" date for them and marks + * them as auto-filled, so they get updated when Published date gets updated + */ +function sm_update_28_fill_out_empty_dates() { + global $wpdb; + + // All sermons + $sermons = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('auto-draft', 'inherit')", 'wpfc_sermon' ) ); + + foreach ( $sermons as $sermon ) { + if ( get_post_meta( $sermon->ID, 'sermon_date', true ) === '' ) { + update_post_meta( $sermon->ID, 'sermon_date', strtotime( $sermon->post_date ) ); + update_post_meta( $sermon->ID, 'sermon_date_auto', '1' ); + } + } + + // clear all cached data + wp_cache_flush(); +} + +/** + * For enabling sorting by series date + * + * @see SM_Dates_WP::update_series_date() + */ +function sm_update_28_fill_out_series_dates() { + SM_Dates_WP::update_series_date(); +} + +function sm_update_28_save_sermon_render_into_post_content() { + global $wpdb; + + // All sermons + $sermons = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s", 'wpfc_sermon' ) ); + + foreach ( $sermons as $sermon ) { + wp_update_post( array( + 'ID' => $sermon->ID, + 'post_content' => wp_filter_post_kses( wpfc_sermon_single( true, $sermon ) ) + ) ); + } + + // clear all cached data + wp_cache_flush(); +} diff --git a/includes/template-tags.php b/includes/template-tags.php index ab1dcbe..540df0f 100755 --- a/includes/template-tags.php +++ b/includes/template-tags.php @@ -26,13 +26,13 @@ function sermon_template_include( $template ) { return get_stylesheet_directory() . '/archive-wpfc_sermon.php'; } - return SERMON_MANAGER_PATH . 'views/archive-wpfc_sermon.php'; + return SM_PATH . 'views/archive-wpfc_sermon.php'; else : if ( file_exists( get_stylesheet_directory() . '/single-wpfc_sermon.php' ) ) { return get_stylesheet_directory() . '/single-wpfc_sermon.php'; } - return SERMON_MANAGER_PATH . 'views/single-wpfc_sermon.php'; + return SM_PATH . 'views/single-wpfc_sermon.php'; endif; } @@ -46,7 +46,7 @@ function sermon_topics_template_include( $template ) { return get_stylesheet_directory() . '/taxonomy-wpfc_sermon_topics.php'; } - return SERMON_MANAGER_PATH . 'views/taxonomy-wpfc_sermon_topics.php'; + return SM_PATH . 'views/taxonomy-wpfc_sermon_topics.php'; } return $template; @@ -59,7 +59,7 @@ function preacher_template_include( $template ) { return get_stylesheet_directory() . '/taxonomy-wpfc_preacher.php'; } - return SERMON_MANAGER_PATH . 'views/taxonomy-wpfc_preacher.php'; + return SM_PATH . 'views/taxonomy-wpfc_preacher.php'; } return $template; @@ -72,7 +72,7 @@ function series_template_include( $template ) { return get_stylesheet_directory() . '/taxonomy-wpfc_sermon_series.php'; } - return SERMON_MANAGER_PATH . 'views/taxonomy-wpfc_sermon_series.php'; + return SM_PATH . 'views/taxonomy-wpfc_sermon_series.php'; } return $template; @@ -85,7 +85,7 @@ function service_type_template_include( $template ) { return get_stylesheet_directory() . '/taxonomy-wpfc_service_type.php'; } - return SERMON_MANAGER_PATH . 'views/taxonomy-wpfc_service_type.php'; + return SM_PATH . 'views/taxonomy-wpfc_service_type.php'; } return $template; @@ -98,7 +98,7 @@ function bible_book_template_include( $template ) { return get_stylesheet_directory() . '/taxonomy-wpfc_bible_book.php'; } - return SERMON_MANAGER_PATH . 'views/taxonomy-wpfc_bible_book.php'; + return SM_PATH . 'views/taxonomy-wpfc_bible_book.php'; } return $template; @@ -380,8 +380,11 @@ function render_wpfc_sermon_single() { } // single sermon action -function wpfc_sermon_single( $return = false ) { - global $post; +function wpfc_sermon_single( $return = false, $post = '' ) { + if ( $post === '' ){ + global $post; + } + ob_start(); ?>
diff --git a/includes/types-taxonomies.php b/includes/types-taxonomies.php index 21e1ff6..6b9d5d1 100644 --- a/includes/types-taxonomies.php +++ b/includes/types-taxonomies.php @@ -75,7 +75,7 @@ function create_wpfc_sermon_types() { 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, - 'menu_icon' => SERMON_MANAGER_URL . 'includes/img/sm-icon.svg', + 'menu_icon' => SM_URL . 'includes/img/sm-icon.svg', 'capability_type' => 'post', 'has_archive' => true, 'rewrite' => generate_wpfc_slug(), diff --git a/readme.txt b/readme.txt index 9cbfb92..53ca833 100755 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: church, sermon, sermons, preaching, podcasting, manage, managing, podcasts Requires at least: 4.5 Tested up to: 4.8.2 Requires PHP: 5.3 -Stable tag: 2.7.2 +Stable tag: 2.7.2 License: GPLv2 License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -96,6 +96,7 @@ Visit the [plugin homepage](https://wpforchurch.com/wordpress-plugins/sermon-man ### 2.8 ### * Add better support for templating plugins (Elementor and Visual Composer, to mention some) * Add better compatibility with themes and search +* Add more stable code for updating database structure * Add support for PHP 5.5, 5.4 and 5.3, but we still recommend to use at least PHP 5.6 * Modify: Show all options in filtering dropdown - even if they don't have associated sermon * Modify: All language strings rewritten (huge thanks to @GITNE) diff --git a/sermons.php b/sermons.php index 237585a..ccf5c8b 100755 --- a/sermons.php +++ b/sermons.php @@ -49,10 +49,11 @@ class SermonManager { */ public function __construct() { // Define constants (PATH and URL are with a trailing slash) - define( 'SM___FILE__', __FILE__ ); - define( 'SERMON_MANAGER_PATH', plugin_dir_path( __FILE__ ) ); - define( 'SERMON_MANAGER_URL', plugin_dir_url( __FILE__ ) ); - define( 'SERMON_MANAGER_VERSION', preg_match( '/^.*Version: (.*)$/m', file_get_contents( __FILE__ ), $version ) ? trim( $version[1] ) : 'N/A' ); + define( 'SM_PLUGIN_FILE', __FILE__ ); + define( 'SM_BASENAME', plugin_basename( __FILE__ ) ); + define( 'SM_PATH', plugin_dir_path( __FILE__ ) ); + define( 'SM_URL', plugin_dir_url( __FILE__ ) ); + define( 'SM_VERSION', preg_match( '/^.*Version: (.*)$/m', file_get_contents( __FILE__ ), $version ) ? trim( $version[1] ) : 'N/A' ); // Register error handlers before continuing include_once 'includes/class-sm-error-recovery.php'; @@ -68,13 +69,13 @@ public function __construct() { if ( is_admin() && ! get_option( 'dismissed-render_php_version_warning', 0 ) ) { add_action( 'admin_notices', array( $this, 'render_php_version_warning' ) ); add_action( 'admin_enqueue_scripts', function () { - wp_enqueue_script( 'wpfc-php-notice-handler', SERMON_MANAGER_URL . 'assets/js/admin/dismiss-php.js', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_script( 'wpfc-php-notice-handler', SM_URL . 'assets/js/admin/dismiss-php.js', array(), SM_VERSION ); } ); } } // Include required items - $this->includes(); + $this->_includes(); // Add defaults on activation register_activation_hook( __FILE__, array( $this, 'set_default_options' ) ); @@ -99,15 +100,9 @@ public function __construct() { // Render sermon HTML for search compatibility add_action( 'save_post_wpfc_sermon', array( $this, 'render_sermon_into_content' ), 10 ); - // new dates fix for 2.6 - $this->restore_dates(); - - // Fill empty sermon dates - $this->fill_out_empty_dates(); - if ( is_admin() ) { add_action( 'admin_enqueue_scripts', function () { - wp_enqueue_style( 'sm-icon', SERMON_MANAGER_URL . 'assets/css/admin-icon.css', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_style( 'sm-icon', SM_URL . 'assets/css/admin-icon.css', array(), SM_VERSION ); } ); } } @@ -117,7 +112,7 @@ public function __construct() { * * @return void */ - private function includes() { + private function _includes() { /** * Files to include on frontend and backend */ @@ -126,6 +121,7 @@ private function includes() { 'includes/class-sm-dates-wp.php', // Attach to WP filters 'includes/class-sm-api.php', // API 'includes/class-sm-post-types.php', // Register post type, taxonomies, etc + 'includes/class-sm-install.php', // Install and update functions 'includes/sm-deprecated-functions.php', // Deprecated SM functions 'includes/sm-core-functions.php', // Deprecated SM functions 'includes/sm-legacy-php-functions.php', // Old PHP compatibility fixes @@ -150,118 +146,19 @@ private function includes() { // Load files foreach ( $includes as $file ) { - if ( file_exists( SERMON_MANAGER_PATH . $file ) ) { - require_once SERMON_MANAGER_PATH . $file; + if ( file_exists( SM_PATH . $file ) ) { + require_once SM_PATH . $file; } } // Load admin files if ( is_admin() ) { foreach ( $admin_includes as $file ) { - if ( file_exists( SERMON_MANAGER_PATH . $file ) ) { - require_once SERMON_MANAGER_PATH . $file; - } - } - } - } - - /** - * Renames all "sermon_date_old" fields to "sermon_date", or converts published date to Unix time and - * saves as "sermon_date". - * - * @since 2.6 - */ - private function restore_dates() { - // If not admin, bail - if ( ! is_admin() ) { - return; - } - - // Allow forcing restoration, just append "?sm_restore_dates" to URL - if ( ! isset( $_GET['sm_restore_dates'] ) ) { - // If we have not even done conversion in previous versions, bail - if ( get_option( 'wpfc_sm_dates_convert_done', 0 ) == 0 ) { - return; - } - - // If we have already done restoration, bail - if ( get_option( 'wpfc_sm_dates_restore_done', 0 ) == 1 ) { - return; - } - } - - - try { - global $wpdb; - - // WP sermon dates - $wp_dates = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s", 'wpfc_sermon' ) ); - - foreach ( $wp_dates as $post ) { - if ( get_post_meta( $post->ID, 'sermon_date', true ) === '' || - ! is_numeric( get_post_meta( $post->ID, 'sermon_date', true ) ) ) { - // Remove all if for some reason we have multiple - delete_post_meta( $post->ID, 'sermon_date' ); - - if ( $date = get_post_meta( $post->ID, 'sermon_date_old', true ) ) { - update_post_meta( $post->ID, 'sermon_date', is_numeric( $date ) ?: strtotime( $date ) ); - } else { - update_post_meta( $post->ID, 'sermon_date', strtotime( $post->post_date ) ); - } - } else { - continue; - } - } - - // clear all cached data - wp_cache_flush(); - } catch ( Exception $e ) { - print_r( $e ); - } - - update_option( 'wpfc_sm_dates_restore_done', 1 ); - } - - /** - * Fills out dates of sermons that don't have `sermon_date` set. Takes "Published" date for them and marks - * them as auto-filled, so they get updated when Published date gets updated - * - * @since 2.7 - */ - private function fill_out_empty_dates() { - // If not admin, bail - if ( ! is_admin() ) { - return; - } - - // Allow forcing, just append "?sm_fill_out_dates" to URL - if ( ! isset( $_GET['sm_fill_out_dates'] ) ) { - // If we have already done restoration, bail - if ( get_option( 'wpfc_sm_dates_fill_done', 0 ) == 1 ) { - return; - } - } - - try { - global $wpdb; - - // WP sermon dates - $wp_dates = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_date FROM $wpdb->posts WHERE post_type = %s", 'wpfc_sermon' ) ); - - foreach ( $wp_dates as $post ) { - if ( get_post_meta( $post->ID, 'sermon_date', true ) === '' ) { - update_post_meta( $post->ID, 'sermon_date', strtotime( $post->post_date ) ); - update_post_meta( $post->ID, 'sermon_date_auto', '1' ); + if ( file_exists( SM_PATH . $file ) ) { + require_once SM_PATH . $file; } } - - // clear all cached data - wp_cache_flush(); - } catch ( Exception $e ) { - print_r( $e ); } - - update_option( 'wpfc_sm_dates_fill_done', 1 ); } /** @@ -327,19 +224,19 @@ public static function enqueue_scripts_styles() { } if ( ! \SermonManager::getOption( 'css' ) ) { - wp_enqueue_style( 'wpfc-sm-styles', SERMON_MANAGER_URL . 'assets/css/sermon.css', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_style( 'wpfc-sm-styles', SM_URL . 'assets/css/sermon.css', array(), SM_VERSION ); wp_enqueue_style( 'dashicons' ); if ( ! \SermonManager::getOption( 'use_old_player' ) ) { - wp_enqueue_script( 'wpfc-sm-plyr', SERMON_MANAGER_URL . 'assets/js/plyr.js', array(), SERMON_MANAGER_VERSION ); - wp_enqueue_style( 'wpfc-sm-plyr-css', SERMON_MANAGER_URL . 'assets/css/plyr.css', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_script( 'wpfc-sm-plyr', SM_URL . 'assets/js/plyr.js', array(), SM_VERSION ); + wp_enqueue_style( 'wpfc-sm-plyr-css', SM_URL . 'assets/css/plyr.css', array(), SM_VERSION ); wp_add_inline_script( 'wpfc-sm-plyr', 'window.onload=function(){plyr.setup(document.querySelectorAll(\'.wpfc-sermon-player, #wpfc_sermon audio\'));}' ); } } if ( ! \SermonManager::getOption( 'bibly' ) ) { - wp_enqueue_script( 'wpfc-sm-bibly-script', SERMON_MANAGER_URL . 'assets/js/bibly.min.js', array(), SERMON_MANAGER_VERSION ); - wp_enqueue_style( 'wpfc-sm-bibly-style', SERMON_MANAGER_URL . 'assets/css/bibly.min.css', array(), SERMON_MANAGER_VERSION ); + wp_enqueue_script( 'wpfc-sm-bibly-script', SM_URL . 'assets/js/bibly.min.js', array(), SM_VERSION ); + wp_enqueue_style( 'wpfc-sm-bibly-style', SM_URL . 'assets/css/bibly.min.css', array(), SM_VERSION ); // get options for JS $bible_version = \SermonManager::getOption( 'bibly_version' );