plugin = $plugin; $this->hooks(); } /** * Initiate our hooks. * * @since 0.1.0 */ public function hooks() { } /** * Call & Return cached notice if set, otherwise call for it and cache it * * @return string */ public function get_the_one_notice_to_display() { $cached_notice = get_transient( $this->top_notice_transient_key ); if( empty( $cached_notice ) ) { $cached_notice = $this->get_appropriate_notice(); set_transient( $this->top_notice_transient_key , $cached_notice, HOUR_IN_SECONDS ); } return $cached_notice === 'none' ? '' : $cached_notice; } /** * Invoke the chain of filters to run * Return the notice's id to display for the current site | 'none' * * @return string */ public function get_appropriate_notice(){ $valid_notices = $this->get_appropriate_notices_for_this_site(); $filtered_by_pinned_notices = $this->filter_by_pinned_notices( $valid_notices ); $notice_to_display = $this->filter_by_priority( $filtered_by_pinned_notices ); return $notice_to_display ? $notice_to_display['id'] : 'none'; } /** * Get the max priority from a list of notices then return the first that matches * If two notices have their priority set to 1 (most important) * We would return the first one found and discard all other notices * * @param array $input * @return array */ public function filter_by_priority( $input = array() ) { if ( empty( $input ) ) { return []; } $min_priority = min(array_column($input, 'priority')); foreach( $input as $notice ) { if ( $min_priority === $notice['priority'] ) { $output = $notice; break; } } return $output; } /** * Get all notices, then filter out all inapproriate for the current site * Returns an array of all valid notices to display * * @return array */ public function get_appropriate_notices_for_this_site() { $all_notices_list = $this->get_all_notices(); $filtered_by_active_state = $this->filter_by_active_status( $all_notices_list ); $filtered_by_dissmissed_notices = $this->filter_by_dismissed_notices( $filtered_by_active_state ); $filtered_by_edition = $this->filter_by_edition( $filtered_by_dissmissed_notices ); $filtered_by_active_plugin = $this->filter_by_active_plugin_any( $filtered_by_edition ); $filtered_by_installed_features = $this->filter_by_installed_feature_any( $filtered_by_active_plugin ); $filtered_by_enabled_features = $this->filter_by_enabled_feature_any( $filtered_by_installed_features ); $filtered_by_activated_features = $this->filter_by_activated_feature_any( $filtered_by_enabled_features ); $filtered_by_not_installed_features = $this->filter_by_not_installed_feature_any( $filtered_by_activated_features ); $filtered_by_not_enabled_features = $this->filter_by_not_enabled_feature_any( $filtered_by_not_installed_features ); $filtered_by_not_activated_features = $this->filter_by_not_activated_feature_any( $filtered_by_not_enabled_features ); $filtered_by_current_user_can = $this->filter_by_current_user_can( $filtered_by_not_activated_features ); $filtered_by_min_activated_days = $this->filter_by_min_activated_days( $filtered_by_current_user_can ); $filtered_by_activation_date_after = $this->filter_by_activation_date_after( $filtered_by_min_activated_days ); $filtered_by_min_appt_count = $this->filter_by_min_appt_count( $filtered_by_activation_date_after ); return $filtered_by_min_appt_count; } /** * Get ALL_NOTICES_LIST * * @return array */ public function get_all_notices() { return SSA_Notices_Data::get_notices_list(); } /** * Each notice has and active property set to true by default * In case set to false, will allow us to eliminate this notice early * Without the need to remove it from the list entirely * * @return array */ public function filter_by_active_status( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { return $notice['active'] === true; }); return $output; } /** * Filter out notices that got dissmissed by the user * * @param array $input * @return array */ public function filter_by_dismissed_notices( $input = array() ) { $dismissed_notices = $this->get_dismissed_notices(); if ( empty( $dismissed_notices ) || empty( $input ) ){ return $input; } $output = array_filter( $input, function( $notice ) use ( $dismissed_notices ) { return !in_array( $notice['id'], $dismissed_notices); }); return $output; } /** * Get dismissed notices stored in the database if any * An empty option dissmissed notices is stored: a:0:{} * * @return array */ public function get_dismissed_notices() { $dismissed_notices = get_option( 'ssa_dismissed_notices', array() ); return $dismissed_notices; } /** * Get pinned notices stored in the database if any * * @return array */ public function get_pinned_notices() { $pinned_notices = get_option( 'ssa_pinned_notices', array() ); return $pinned_notices; } /** * Check if the user's edition matches with the required edition for the notices * * @param array $input * @return array */ public function filter_by_edition( $input = array() ) { if ( empty( $input ) ) { return []; } $edition_detected = $this->plugin->get_current_edition(); $output = array_filter( $input, function( $notice ) use ( $edition_detected ) { $required_editions = $notice['requires']['current_edition_any']; if ( empty( $required_editions ) ) { // No specific edition required for this notice -> keep it return true; } else { // If the notice requires specific edition(s); check if the user's edition matches return in_array( $edition_detected, $required_editions ); } }); return $output; } /** * Filter by ['active_plugin_any'] field * * @param array $input * @return array */ public function filter_by_active_plugin_any( $input = array() ){ if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $plugins = $notice['requires']['active_plugin_any']; if ( empty( $plugins ) ) { return true; } static $all_active_plugins; foreach( $plugins as $plugin ) { if( empty( $all_active_plugins ) ) { $all_active_plugins = $this->get_active_plugins(); } if ( in_array( $plugin, $all_active_plugins ) ) { return true; } } return false; }); return $output; } /** * Get all active plugins * * @return array */ public function get_active_plugins(){ $active_plugins = get_option( 'active_plugins' ); $output = array(); foreach ($active_plugins as $active_plugin ) { if ( strpos( $active_plugin, '/' ) ) { $active_plugin = substr( $active_plugin, 0, strpos( $active_plugin, '/' ) ); } $output[] = $active_plugin; } return $output; } /** * Check if any notice requires a specific installed feature * By checking the ['installed_feature_any'] field * * @param array $input * @return array */ public function filter_by_installed_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { // For every notice in the array check the field below $features = $notice['requires']['installed_feature_any']; if ( empty( $features ) ) { // No feature required to be installed for this notice -> Nothing to check -> keep it return true; } else { // Else a feature is required to be installed, so we need to check foreach( $features as $feature) { if ( $this->check_if_feature_installed( $feature ) ) { return true; } } return false; } }); return $output; } /** * Check if any notice requires a NOT installed feature * * @param array $input * @return array */ public function filter_by_not_installed_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { // For every notice check if it requires a NOT installed feature $features = $notice['requires']['not_installed_feature_any']; if ( empty( $features ) ) { // No feature required to be NOT installed for this notice -> Nothing to check -> keep it return true; } else { // Else a feature is required to be NOT installed, so we need to check foreach( $features as $feature) { if ( ! $this->check_if_feature_installed( $feature ) ) { return true; } } return false; } }); return $output; } /** * Custom function to call out the is_installed built in method * Return true if installed, false otherwise * * @param string $feature * @return boolean */ public function check_if_feature_installed( $feature = '' ) { if ( empty( $feature ) ){ return false; } return $this->plugin->settings_installed->is_installed( $feature ); } /** * Check if any notice requires an enabled feature * If no enabled feature is required then let it pass; keep the notice * Otherwise check for the feature: * If it is enabled; keep the notice * If it is not then filter the notice out * * @param array $input * @return array */ public function filter_by_enabled_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { // For every notice in the array check the field below $features = $notice['requires']['enabled_feature_any']; if ( empty( $features ) ) { // No feature required to be enabled for this notice -> Nothing to check -> keep it return true; } else { // Else a feature is required to be enabled, so we need to check foreach( $features as $feature) { if ( $this->check_if_feature_enabled( $feature ) ) { return true; } } return false; } }); return $output; } /** * Check if any notice requires an NOT enabled feature * * @param array $input * @return array */ public function filter_by_not_enabled_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { // For every notice check for required feature to be NOT enabled $features = $notice['requires']['not_enabled_feature_any']; if ( empty( $features ) ) { // No feature required to be NOT enabled for this notice -> Nothing to check -> keep it return true; } else { // Else a feature is required to be NOT enabled, so we need to check foreach( $features as $feature) { if ( ! $this->check_if_feature_enabled( $feature ) ) { return true; } } return false; } }); return $output; } /** * Custom function to call out the is_enabled built in method * Return true if enabled, false otherwise * * @param string $feature * @return boolean */ public function check_if_feature_enabled( $feature = '' ) { if ( empty( $feature ) ){ return false; } return $this->plugin->settings_installed->is_enabled( $feature ); } /** * Check if any notice requires an activated feature * If any has been found call out the check_if_feature_activated * * @param array $input * @return array */ public function filter_by_activated_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $features = $notice['requires']['activated_feature_any']; if ( empty( $features ) ) { // No feature required to be activated for this notice -> keep it return true; } else { foreach( $features as $feature) { if ( $this->check_if_feature_activated( $feature ) ) { return true; } } return false; } }); return $output; } /** * Check if any notice requires a NOT activated feature * If any has been found call out the check_if_feature_activated * * @param array $input * @return array */ public function filter_by_not_activated_feature_any( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $features = $notice['requires']['not_activated_feature_any']; if ( empty( $features ) ) { // No feature required to be not activated for this notice -> keep it return true; } else { // the `!` is needed since it requires the feature to be NOT activated foreach( $features as $feature) { if ( ! $this->check_if_feature_activated( $feature ) ) { return true; } } return false; } }); return $output; } /** * Custom function to call out the is_activated built in method * * @param string $feature * @return boolean */ public function check_if_feature_activated( $feature = '' ) { if ( empty( $feature ) ){ return false; } return $this->plugin->settings_installed->is_activated( $feature ); } public function filter_by_pinned_notices( $input = array() ) { $pinned_notices = $this->get_pinned_notices(); if ( empty( $pinned_notices ) || empty( $input ) ){ return $input; } // Assign a priority of 22 for every pinned notices $output = array_map( function( $notice ) use ( $pinned_notices ) { // Check for pinned notices if ( in_array( $notice['id'], $pinned_notices ) ){ $notice['priority'] = '22'; } return $notice; }, $input ); return $output; } /** * Filter by ['requires']['current_user_can'] field * Example: ['requires']['current_user_can'] => array( 'ssa_manage_site_settings' ), * * @param array $input * @return array */ public function filter_by_current_user_can( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $permissions = $notice['requires']['current_user_can']; if ( empty( $permissions ) ) { return true; } else { // Loop over the permissions since it's an array foreach( $permissions as $permission ) { // If any evaluated to false break & return false to filter the notice out if ( ! current_user_can( $permission ) ) { return false; } } return true; } }); return $output; } /** * Check and filter notices by min_activated_days * * @param array $input * @return array */ public function filter_by_min_activated_days( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $required_activated_days = $notice['requires']['min_activated_days']; if ( empty( $required_activated_days ) ) { return true; } else { static $ssa_activated_days; if( empty( $ssa_activated_days ) && $ssa_activated_days !== 0 ) { $ssa_activated_days = $this->ssa_activated_days(); } return (int) $required_activated_days <= (int) $ssa_activated_days; } }); return $output; } /** * Check and filter notices by activation_date_after * * @param array $input * @return array */ public function filter_by_activation_date_after( $input = array() ) { if ( empty( $input ) ) return []; $output = array_filter( $input, function( $notice ) { $required_activation_date_after = $notice['requires']['activation_date_after']; if ( empty( $required_activation_date_after ) ) { return true; } else { static $ssa_activation_date; if( empty( $ssa_activation_date ) ) { $ssa_activation_date = $this->get_ssa_activation_date(); } $activation_date = $ssa_activation_date; // DateTimeImmutable $min_date_required = ssa_datetime( $required_activation_date_after ); return $activation_date > $min_date_required; } }); return $output; } /** * In class-upgrade.php -> record_version() stores the date of each migration running * Hence the first migration date indicates the activation date of the plugin * * @return int */ public function ssa_activated_days() { $ssa_versions = get_option( 'ssa_versions', json_encode( array() ) ); $ssa_versions = json_decode( $ssa_versions, true ); if( empty( $ssa_versions ) ) { return 0; } $activation_date = array_keys($ssa_versions)[0]; $activation_date = ssa_datetime( $activation_date ); $now = ssa_datetime(); $diff = $now->diff( $activation_date ); return $diff->days; } /** * In class-upgrade.php -> record_version() stores the date of each migration running * Hence the first migration date indicates the activation date of the plugin * * @return int */ public function get_ssa_activation_date() { $ssa_versions = get_option( 'ssa_versions', json_encode( array() ) ); $ssa_versions = json_decode( $ssa_versions, true ); if( empty( $ssa_versions ) || ! is_array( $ssa_versions ) ) { return ssa_datetime(); // Let's assume it's today in case is empty } if ( empty( array_keys( $ssa_versions ) ) || empty( array_keys( $ssa_versions )[0] ) ) { return ssa_datetime(); } return ssa_datetime( array_keys($ssa_versions)[0] ); } /** * Check and filter notices that require minimum booked appointment count * * @param array $input * @return array */ public function filter_by_min_appt_count( $input = array() ) { if ( empty( $input ) ) { return []; } $output = array_filter( $input, function( $notice ) { $required_appt_count = $notice['requires']['min_appt_count']; if ( empty( $required_appt_count ) ) { return true; } else { static $appointments_count; if( empty( $appointments_count ) && $appointments_count !== 0 ) { $appointments_count = $this->get_completed_appointments_count(); } return (int) $required_appt_count <= (int) $appointments_count; } }); return $output; } /** * Query COUNT of all booked appointments that have their end_date before today * * @return integer */ public function get_completed_appointments_count(){ $appointments_count = $this->plugin->appointment_model->count( array( 'status' => array( 'booked' ), 'end_date_max' => gmdate( 'Y-m-d H:i:s' ), )); return $appointments_count; } /** * Delete cached transient * * @return boolean */ public function delete_top_notice_cached_transient(){ $transient = $this->top_notice_transient_key; return delete_transient( $transient ); } }