registry()->get( 'admin_notices' );
}
/**
* Create class object. A private constructor, so this is used in a singleton context.
*
* @since 1.4.6
* @version 1.5.4 Access changed to public.
* @version 1.8.0 Added the 'render_campaign_builder_notice' notice.
* @version 1.8.1 Added 'render_dashboard_reporting_notice' and 'dismiss_dashboard_reporting_notice'. Removed 'render_campaign_builder_notice' notice.
* @version 1.8.1.12 Added 'render_license_expiring_banner'.
*
* @return void
*/
public function __construct() {
$this->load_notices();
add_action( 'admin_notices', array( $this, 'render_upgrade_banner' ) );
add_action( 'admin_notices', array( $this, 'render_license_expiring_banner' ) );
add_action( 'admin_notices', array( $this, 'render_license_expired_banner' ) );
add_action( 'admin_notices', array( $this, 'render_five_star_rating' ) );
add_action( 'admin_notices', array( $this, 'render_square_connection_error' ) );
add_action( 'admin_notices', array( $this, 'render_paypal_account_type_required_notice' ) );
add_action( 'charitable_dismiss_notice', array( $this, 'dismiss_five_star_notice' ), 10, 1 );
add_action( 'charitable_dismiss_notice', array( $this, 'dismiss_campaign_builder_notice' ), 10, 1 );
add_action( 'charitable_dismiss_notice', array( $this, 'dismiss_dashboard_reporting_notice' ), 10, 1 );
add_action( 'charitable_dismiss_notice', array( $this, 'dismiss_square_connection_error_notice' ), 10, 1 );
add_filter( 'charitable_localized_javascript_vars', array( $this, 'render_campaign_upgrade_banner_html' ), 10, 1 );
}
/**
* Adds a notice message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $type The type of notice. Accepts 'error', 'warning', 'success', 'info', 'version'.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Whether the notice can be dismissed. Defaults to false.
* @return void
*/
public function add_notice( $message, $type, $key = false, $dismissible = false ) {
if ( false === $key ) {
$this->notices[ $type ][] = array(
'message' => $message,
'dismissible' => $dismissible,
);
} else {
$this->notices[ $type ][ $key ] = array(
'message' => $message,
'dismissible' => $dismissible,
);
}
}
/**
* Adds an error message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_error( $message, $key = false, $dismissible = false ) {
$this->add_notice( $message, 'error', $key, $dismissible );
}
/**
* Adds a warning message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_warning( $message, $key = false, $dismissible = false ) {
$this->add_notice( $message, 'warning', $key, $dismissible );
}
/**
* Adds a success message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_success( $message, $key = false, $dismissible = false ) {
$this->add_notice( $message, 'success', $key, $dismissible );
}
/**
* Adds an info message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_info( $message, $key = false, $dismissible = false ) {
$this->add_notice( $message, 'info', $key, $dismissible );
}
/**
* Adds a version update message.
*
* @since 1.4.6
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_version_update( $message, $key = false, $dismissible = true ) {
$this->add_notice( $message, 'version', $key, $dismissible );
}
/**
* Adds a third party warning message.
*
* @since 1.7.0.8
*
* @param string $message The message to display.
* @param string $key Optional. If not set, next numeric key is used.
* @param boolean $dismissible Optional. Set to true by default.
* @return void
*/
public function add_third_party_warning( $message, $key = false, $dismissible = true ) {
$this->add_notice( $message, 'warning', $key, $dismissible );
}
/**
* Render notices.
*
* @since 1.4.6
*
* @return void
*/
public function render() {
foreach ( charitable_get_admin_notices()->get_notices() as $type => $notices ) {
foreach ( $notices as $key => $notice ) {
if ( ! empty( $notice['message'] ) ) {
$this->render_notice( $notice['message'], $type, $notice['dismissible'], $key );
}
}
}
}
/**
* Render a notice.
*
* @since 1.4.6
*
* @param string $notice The notice message.
* @param string $type The type of notice. Accepts 'error', 'warning', 'success', 'info', 'version'.
* @param boolean $dismissible Optional. Whether the notice can be dismissed. Defaults to false.
* @param string $notice_key Optional. If set, the notice will be dismissed when the user dismisses the notice.
* @param boolean $paragraph_tags Optional. Whether to wrap the notice in paragraph tags. Defaults to true.
* @return void
*/
public function render_notice( $notice, $type, $dismissible = false, $notice_key = '', $paragraph_tags = true ) {
if ( ! isset( $this->script_enqueued ) ) {
if ( ! wp_script_is( 'charitable-admin-notice' ) ) {
wp_enqueue_script( 'charitable-admin-notice' );
}
$this->script_enqueued = true;
}
$class = 'notice charitable-notice';
switch ( $type ) {
case 'error':
$class .= ' notice-error';
break;
case 'warning':
$class .= ' notice-warning';
break;
case 'success':
$class .= ' updated';
break;
case 'info':
$class .= ' notice-info';
break;
case 'five-star-review':
$class .= ' notice-info notice-five-star-review';
break;
case 'version':
$class .= ' charitable-upgrade-notice';
break;
}
if ( $dismissible ) {
$class .= ' is-dismissible';
}
$body_text = ( $paragraph_tags ) ? '
%s
' : '%s';
printf(
'
' . wp_kses_post( $body_text ) . '
',
esc_attr( $class ),
strlen( $notice_key ) ? 'data-notice="' . esc_attr( $notice_key ) . '"' : '',
wp_kses_post( $notice )
);
if ( strlen( $notice_key ) ) {
unset( $this->notices[ $type ][ $notice_key ] );
}
}
/**
* Render a lite to pro banner
*
* @since 1.7.0
*/
public function render_upgrade_banner() {
if ( charitable_is_pro() ) {
return;
}
$screen = get_current_screen();
if ( ! is_null( $screen ) && ( in_array( $screen->id, charitable_get_charitable_screens() ) || ( isset( $screen->taxonomy ) && 'campaign_category' === $screen->taxonomy ) || ( isset( $screen->taxonomy ) && 'campaign_tag' === $screen->taxonomy ) ) ) {
$banner = get_transient( 'charitable_charitablelitetopro_banner' );
if ( ! $banner ) {
$utm_link = charitable_pro_upgrade_url( 'Upgrade From Lite Top Banner Link', 'To unlock more features consider upgrading to Pro.' );
$this->render_banner(
'You\'re using Charitable Lite! To unlock more features consider upgrading to Pro.
',
'top-of-page',
true
);
}
}
}
/**
* Render a license soon-to-be-expired banner.
*
* @since 1.8.1.12
*/
public function render_license_expiring_banner() {
if ( ! charitable_is_pro() || ! class_exists( 'Charitable_Licenses_Settings' ) || ! class_exists( 'Charitable_Addons_Directory' ) ) {
// This should only show if the user has a license already entered.
return;
}
$is_legacy = Charitable_Addons_Directory::is_current_plan_legacy();
if ( $is_legacy ) {
return;
}
// Is the license expiring within 14 days?
$is_license_expiring = Charitable_Licenses_Settings::get_instance()->is_license_expiring( 1209600 );
if ( $is_license_expiring ) {
$screen = get_current_screen();
if ( ! is_null( $screen ) && ( in_array( $screen->id, charitable_get_charitable_screens(), true ) || ( isset( $screen->taxonomy ) && 'campaign_category' === $screen->taxonomy ) || ( isset( $screen->taxonomy ) && 'campaign_tag' === $screen->taxonomy ) ) ) {
$banner = get_transient( 'charitable_expiringlicense_banner' );
if ( ! $banner ) {
$this->render_banner(
'Your Charitable license may be expiring soon. View license settings to ensure support and updates are not interrupted.
',
'top-of-page',
true,
'expiringlicense',
'expiringlicense',
false,
'charitable-license-expiring-banner'
);
}
}
}
}
/**
* Render a license soon-to-be-expired banner.
*
* @since 1.8.1.12
*/
public function render_license_expired_banner() {
if ( ! charitable_is_pro() || ! class_exists( 'Charitable_Licenses_Settings' ) || ! class_exists( 'Charitable_Addons_Directory' ) ) {
// This should only show if the user has a license already entered.
return;
}
$is_legacy = Charitable_Addons_Directory::is_current_plan_legacy();
if ( $is_legacy ) {
return;
}
// Is the license expiring within 14 days?
$is_license_expired = Charitable_Licenses_Settings::get_instance()->is_license_expired();
if ( $is_license_expired ) {
$screen = get_current_screen();
if ( ! is_null( $screen ) && ( in_array( $screen->id, charitable_get_charitable_screens(), true ) || ( isset( $screen->taxonomy ) && 'campaign_category' === $screen->taxonomy ) || ( isset( $screen->taxonomy ) && 'campaign_tag' === $screen->taxonomy ) ) ) {
$banner = get_transient( 'charitable_expiredlicense_banner' );
if ( ! $banner ) {
$this->render_banner(
'Your Charitable license may have expired. View license settings to ensure support and updates are not interrupted.
',
'top-of-page',
true,
'expiredlicense',
'expiredlicense',
false,
'charitable-license-expiring-banner'
);
}
}
}
}
/**
* Render a lite to pro banner in campaign lists, add it to the charitable admin vars so it can be injected via JS.
*
* @param array $strings The localized strings.
*
* @since 1.8.0
*/
public function render_campaign_upgrade_banner_html( $strings = false ) {
if ( charitable_is_pro() ) {
return $strings;
}
$screen = get_current_screen();
if ( ! is_null( $screen ) && isset( $screen->base ) && 'edit' === $screen->base && isset( $screen->id ) && 'edit-campaign' === $screen->id ) {
$banner = get_transient( 'charitable_charitable_ltp_lb_list_banner' );
if ( ! $banner ) {
$nonce = wp_create_nonce( 'charitable_dismiss_list_banner' );
$banner_text = '
Unlock More Donations with Peer-to-Peer Fundraising! Harness the power of supporter networks and friends to reach more people and raise more money for your cause.
',
$nonce,
charitable()->get_path( 'assets', false ) . 'images/icons/ambassador.png',
$banner_text
);
} else {
$strings['banner'] = '';
}
}
return $strings;
}
/**
* Render a prompt to ask for a five star rating.
*
* @since 1.7.0
*/
public function render_five_star_rating() {
if ( ! function_exists( 'get_current_screen' ) ) {
return;
}
// Dashboard has its own inline version of this notice — skip the top-bar one.
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'charitable-dashboard' === $page ) {
return;
}
$screen = get_current_screen();
// determine if we are on the current screen.
if ( ! is_null( $screen ) && ( in_array( $screen->id, charitable_get_charitable_screens() ) || ( isset( $screen->taxonomy ) && 'campaign_category' === $screen->taxonomy ) || ( isset( $screen->taxonomy ) && 'campaign_tag' === $screen->taxonomy ) ) ) {
$slug = 'five-star-review';
// determine when to display this message. for now, there should be some sensible boundaries before showing the notification: a minimum of 14 days of use, created one donation form and received at least one donation.
$activated_datetime = ( false !== get_option( 'wpcharitable_activated_datetime' ) ) ? get_option( 'wpcharitable_activated_datetime' ) : false;
$days = 0;
if ( $activated_datetime ) {
$diff = current_time( 'timestamp' ) - $activated_datetime; // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
$days = abs( round( $diff / 86400 ) );
}
$count_campaigns = wp_count_posts( 'campaign' );
$total_campaigns = isset( $count_campaigns->publish ) ? $count_campaigns->publish : 0;
$count_donations = wp_count_posts( 'donation' );
$total_donations = isset( $count_donations->{'charitable-completed'} ) ? $count_donations->{'charitable-completed'} : 0;
if ( $days >= apply_filters( 'charitable_days_since_activated', 14 ) && $total_campaigns >= 1 && $total_donations >= 1 ) {
// check transient.
$star_review = get_transient( 'charitable_' . $slug . '_banner' );
// render five star rating banner/notice.
if ( ! $star_review ) {
$message = charitable_admin_view( 'notices/admin-notice-five-star-review', array(), true );
$key = 'five-star-review';
$this->render_notice( $message, 'five-star-review', true, $key, false );
}
}
}
}
/**
* Render a notice when the Square connection fails.
*
* @since 1.8.7
*/
public function render_square_connection_error() {
$admin_notice = get_option( 'charitable_square_connection_error_notice' );
if ( false === $admin_notice ) {
return;
}
$message = charitable_admin_view( 'notices/admin-notice-square-connection-error', array( 'message' => $admin_notice['message'], 'status_code' => $admin_notice['status_code'] ), true ); // phpcs:ignore
$key = 'square-connection-error';
$this->render_notice( $message, 'error', true, $key, false );
}
/**
* Render a persistent banner across all Charitable admin screens when
* the PayPal Commerce gateway is connected but the merchant has not
* yet chosen an Account Type (Nonprofit vs Business).
*
* Per the 1.8.12 spec (B-strict default-handling) this banner is
* intentionally NOT dismissible — the underlying state blocks
* donations at validate_donation(), so dismissing the banner without
* resolving the setting would mask broken donation processing from
* the merchant. The banner persists until the setting is saved.
*
* @since 1.8.12
*
* @return void
*/
public function render_paypal_account_type_required_notice() {
// Gate behind manage_options. The notice exposes gateway
// configuration state and links to the settings page; only users
// who can actually fix it should see it.
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Only fire on Charitable admin screens to avoid leaking into
// unrelated plugins' admin contexts.
$screen = get_current_screen();
if ( is_null( $screen ) || ! in_array( $screen->id, charitable_get_charitable_screens(), true ) ) {
return;
}
if ( ! class_exists( 'Charitable_Gateway_Paypal_Commerce' ) ) {
return;
}
// Only fire when PayPal Commerce is actually an enabled gateway.
// Stale merchant ID meta can survive from a prior connected session
// even after the gateway is disabled in Settings > Payment Gateways;
// without this check the notice would scold merchants who are not
// using the gateway at all, and the deep-link target (the
// gateways_paypal_commerce settings group) is not even registered
// when the gateway is off, so clicking the notice's CTA would
// bounce them to the gateway list page instead.
$gateways = charitable_get_helper( 'gateways' );
if ( ! $gateways || ! $gateways->is_active_gateway( Charitable_Gateway_Paypal_Commerce::ID ) ) {
return;
}
$gateway = new Charitable_Gateway_Paypal_Commerce();
// Only relevant for sites that have actually connected PayPal
// Commerce. A disconnected gateway has nothing to gate.
if ( ! $gateway->is_seller_connected() ) {
return;
}
if ( $gateway->is_account_type_configured() ) {
return;
}
$settings_url = admin_url( 'admin.php?page=charitable-settings&tab=gateways&group=gateways_paypal_commerce' );
$message = sprintf(
/* translators: %s: settings URL */
__( 'PayPal Commerce: Action Required. Donations are currently blocked because your Account Type has not been set. Choose Nonprofit or Business on the PayPal Commerce settings page to resume accepting donations.', 'charitable' ),
esc_url( $settings_url )
);
echo '