| 1 |
<?php |
| 2 |
|
| 3 |
namespace BetterLinks\Admin; |
| 4 |
if ( ! defined( 'ABSPATH' ) ) { exit; } |
| 5 |
|
| 6 |
use BetterLinks\Admin\WPDev\PluginUsageTracker; |
| 7 |
use Exception; |
| 8 |
use PriyoMukul\WPNotice\Notices; |
| 9 |
use PriyoMukul\WPNotice\Utils\CacheBank; |
| 10 |
use PriyoMukul\WPNotice\Utils\NoticeRemover; |
| 11 |
|
| 12 |
class Notice { |
| 13 |
/** |
| 14 |
* @var CacheBank |
| 15 |
*/ |
| 16 |
private static $cache_bank; |
| 17 |
|
| 18 |
/** |
| 19 |
* @var PluginUsageTracker |
| 20 |
*/ |
| 21 |
private $opt_in_tracker; |
| 22 |
|
| 23 |
/** |
| 24 |
* @var bool Flag to prevent duplicate notice display |
| 25 |
*/ |
| 26 |
private static $black_friday_notice_displayed = false; |
| 27 |
|
| 28 |
const ASSET_URL = BETTERLINKS_ASSETS_URI; |
| 29 |
|
| 30 |
public function __construct() { |
| 31 |
$this->usage_tracker(); |
| 32 |
|
| 33 |
self::$cache_bank = CacheBank::get_instance(); |
| 34 |
try { |
| 35 |
$this->notices(); |
| 36 |
} catch ( Exception $e ) { |
| 37 |
unset( $e ); |
| 38 |
} |
| 39 |
|
| 40 |
add_action( 'in_admin_header', [ $this, 'remove_admin_notice' ] ); |
| 41 |
add_action( 'btl_compatibity_notices', [ $this, 'btlpro_compatibility_notices' ] ); |
| 42 |
// Use multiple hooks for better compatibility across different WordPress setups |
| 43 |
add_action( 'admin_footer', [ $this, 'black_friday_pointer_notice' ], 999 ); |
| 44 |
} |
| 45 |
|
| 46 |
public function btlpro_compatibility_notices() { |
| 47 |
global $wp_version; |
| 48 |
|
| 49 |
if ( ! defined( 'BETTERLINKS_PRO_VERSION' ) ) { |
| 50 |
return; |
| 51 |
} |
| 52 |
|
| 53 |
if ( version_compare( $wp_version, '6.6', '>=' ) && version_compare( BETTERLINKS_PRO_VERSION, '2.0.0', '<=' ) ) { |
| 54 |
$message = sprintf( ' |
| 55 |
<strong>%1$s</strong>: %2$s <strong>v2.0.1</strong> %3$s <strong>6.6 or later</strong>', |
| 56 |
__( 'Warning', 'betterlinks' ), |
| 57 |
__( 'Please update your BetterLinks Pro plugin to atleast', 'betterlinks' ), |
| 58 |
__( 'to ensure compatibility with WordPress', 'betterlinks' ) |
| 59 |
); |
| 60 |
|
| 61 |
$notice = sprintf( '<div style="padding: 10px;" class="notice notice-warning">%2$s</div>', 'betterlinks', $message ); |
| 62 |
|
| 63 |
echo wp_kses_post( $notice ); |
| 64 |
} |
| 65 |
} |
| 66 |
|
| 67 |
/** |
| 68 |
* Display Black Friday pointer notice |
| 69 |
* Shows only once per user with date range validation |
| 70 |
* Only displays for free users (without BetterLinks Pro) |
| 71 |
* Only shows on BetterLinks pages and WordPress dashboard |
| 72 |
* |
| 73 |
* @return void |
| 74 |
*/ |
| 75 |
public function black_friday_pointer_notice() { |
| 76 |
// Prevent duplicate display when hooked to multiple actions |
| 77 |
if ( self::$black_friday_notice_displayed ) { |
| 78 |
return; |
| 79 |
} |
| 80 |
|
| 81 |
// Check if notice is dismissed |
| 82 |
if ( get_transient( 'betterlinks_black_friday_pointer_dismissed' ) ) { |
| 83 |
return; |
| 84 |
} |
| 85 |
|
| 86 |
// Check date range: November 16, 2025 to December 4, 2025 |
| 87 |
$start_date = strtotime( '11:59:59pm 16th November, 2025' ); |
| 88 |
$end_date = strtotime( '11:59:59pm 4th December, 2025' ); |
| 89 |
$current_time = current_time( 'timestamp' ); |
| 90 |
|
| 91 |
// Only show if within date range |
| 92 |
if ( $current_time < $start_date || $current_time > $end_date ) { |
| 93 |
return; |
| 94 |
} |
| 95 |
|
| 96 |
// Don't show if Pro is already active or installed |
| 97 |
if ( defined( 'BETTERLINKS_PRO_VERSION' ) || is_plugin_active( 'betterlinks-pro/betterlinks-pro.php' ) ) { |
| 98 |
return; |
| 99 |
} |
| 100 |
|
| 101 |
// Check plugin pointer priority system |
| 102 |
// BetterLinks priority is 7 |
| 103 |
$betterlinks_priority = 7; |
| 104 |
$current_priority = get_option( '_wpdeveloper_plugin_pointer_priority' ); |
| 105 |
// If priority option doesn't exist, create it with BetterLinks priority |
| 106 |
if ( false === $current_priority || null === $current_priority || '' === $current_priority ) { |
| 107 |
update_option( '_wpdeveloper_plugin_pointer_priority', $betterlinks_priority ); |
| 108 |
} elseif ( $current_priority > $betterlinks_priority ) { |
| 109 |
// If current priority is higher than BetterLinks priority, update it |
| 110 |
update_option( '_wpdeveloper_plugin_pointer_priority', $betterlinks_priority ); |
| 111 |
$current_priority = $betterlinks_priority; |
| 112 |
} |
| 113 |
|
| 114 |
if ( $current_priority < $betterlinks_priority ) { |
| 115 |
return; |
| 116 |
} |
| 117 |
|
| 118 |
// Only show on BetterLinks pages, WordPress dashboard, and plugins directory |
| 119 |
$current_screen = get_current_screen(); |
| 120 |
$is_betterlinks_page = ( 0 === strpos( $current_screen->id, 'toplevel_page_betterlinks' ) || 0 === strpos( $current_screen->id, 'betterlinks_page_' ) ); |
| 121 |
$is_dashboard = ( 'dashboard' === $current_screen->id ); |
| 122 |
$is_plugins_page = ( 'plugins' === $current_screen->id ); |
| 123 |
|
| 124 |
if ( ! $is_betterlinks_page && ! $is_dashboard && ! $is_plugins_page ) { |
| 125 |
return; |
| 126 |
} |
| 127 |
|
| 128 |
// Enqueue pointer styles and scripts |
| 129 |
wp_enqueue_style( 'wp-pointer' ); |
| 130 |
wp_enqueue_script( 'wp-pointer' ); |
| 131 |
wp_enqueue_script( 'jquery' ); |
| 132 |
|
| 133 |
// Create nonce for AJAX |
| 134 |
$nonce = wp_create_nonce( 'betterlinks_dismiss_black_friday_notice' ); |
| 135 |
|
| 136 |
// Mark notice as displayed to prevent duplicates |
| 137 |
self::$black_friday_notice_displayed = true; |
| 138 |
|
| 139 |
// Output the notice markup |
| 140 |
?> |
| 141 |
|
| 142 |
<script type="text/javascript"> |
| 143 |
(function($) { |
| 144 |
$(document).ready(function() { |
| 145 |
|
| 146 |
const target = jQuery("#toplevel_page_betterlinks" || 'body'); |
| 147 |
|
| 148 |
if (target.length === 0) { |
| 149 |
return; |
| 150 |
} |
| 151 |
|
| 152 |
// Prepare content with optional button |
| 153 |
let content = '<h3><?php esc_html_e( 'BetterLinks Black Friday Sale', 'betterlinks' ); ?></h3><p><?php esc_html_e( 'Shorten and redirect links & analyze website performance efficiently.', 'betterlinks' ); ?> </p>' || ''; |
| 154 |
content += '<p style="margin-top: 15px;"><a href="https://betterlinks.io/bfcm-wp-admin-pointer" class="button button-primary" target="_blank" rel="noopener"><?php esc_html_e( 'Save 40%', 'betterlinks' ); ?></a></p>'; |
| 155 |
|
| 156 |
// Default pointer options |
| 157 |
const options = { |
| 158 |
content: content, |
| 159 |
position: { |
| 160 |
edge: "left", |
| 161 |
align: 'center' |
| 162 |
}, |
| 163 |
close: function() { |
| 164 |
// dismissPointer(pointerId); |
| 165 |
var nonce = '<?php echo esc_js($nonce); ?>'; |
| 166 |
// Send AJAX request to set transient |
| 167 |
$.ajax({ |
| 168 |
url: '<?php echo esc_url(admin_url( 'admin-ajax.php' )); ?>', |
| 169 |
type: 'POST', |
| 170 |
data: { |
| 171 |
action: 'betterlinks_dismiss_black_friday_notice', |
| 172 |
nonce: nonce |
| 173 |
}, |
| 174 |
}); |
| 175 |
} |
| 176 |
}; |
| 177 |
|
| 178 |
// Show the pointer |
| 179 |
target.pointer(options).pointer('open'); |
| 180 |
}); |
| 181 |
})(jQuery); |
| 182 |
</script> |
| 183 |
<?php |
| 184 |
} |
| 185 |
|
| 186 |
public function remove_admin_notice() { |
| 187 |
$current_screen = get_current_screen(); |
| 188 |
$dashboard_notice = get_option( 'betterlinks_dashboard_notice' ); |
| 189 |
|
| 190 |
if ( ! empty( strpos( $current_screen->id, 'betterlinks-quick-setup' ) ) ) { |
| 191 |
remove_all_actions( 'admin_notices' ); |
| 192 |
|
| 193 |
return; |
| 194 |
} |
| 195 |
|
| 196 |
if ( 0 === strpos( $current_screen->id, "toplevel_page_betterlinks" ) || 0 === strpos( $current_screen->id, "betterlinks_page_" ) ) { |
| 197 |
remove_all_actions( 'admin_notices' ); |
| 198 |
if ( BETTERLINKS_MENU_NOTICE !== $dashboard_notice ) { |
| 199 |
add_action( 'admin_notices', array( $this, 'new_feature_notice' ), - 1 ); |
| 200 |
} |
| 201 |
// To showing notice in BetterLinks page |
| 202 |
add_action( 'admin_notices', function () { |
| 203 |
do_action( 'btl_admin_notices' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 204 |
do_action( 'btl_compatibity_notices' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound |
| 205 |
Notice\PrettyLinks::init(); |
| 206 |
Notice\Simple301::init(); |
| 207 |
Notice\ThirstyAffiliates::init(); |
| 208 |
// Remove OLD notice from 1.0.0 (if other WPDeveloper plugin has notice) |
| 209 |
NoticeRemover::get_instance( '1.0.0' ); |
| 210 |
} ); |
| 211 |
} |
| 212 |
} |
| 213 |
|
| 214 |
/** |
| 215 |
* The "what's new in Pro" bar above the BetterLinks navbar. |
| 216 |
* |
| 217 |
* Keeps `notice is-dismissible` and the `btl-dashboard-notice` id: WordPress |
| 218 |
* injects the dismiss button into `.notice.is-dismissible`, and `App.js` binds |
| 219 |
* the persistence AJAX call to that injected `.notice-dismiss` element. The |
| 220 |
* `btl-notice` classes are what `_admin-notices.scss` styles; without that |
| 221 |
* stylesheet this still degrades to an ordinary WordPress success notice. |
| 222 |
*/ |
| 223 |
public function new_feature_notice() { |
| 224 |
printf( |
| 225 |
'<div class="notice notice-success is-dismissible btl-dashboard-notice btl-notice btl-notice--new" id="btl-dashboard-notice"> |
| 226 |
<p class="btl-notice__body"> |
| 227 |
<span class="btl-notice__pill">%1$s</span> |
| 228 |
<span class="btl-notice__text"><strong>%2$s</strong>%3$s<a class="btl-notice__link" target="_blank" rel="noopener noreferrer" href="%4$s">%5$s</a>%6$s<a class="btl-notice__link" target="_blank" rel="noopener noreferrer" href="%7$s">%8$s</a>%9$s<a class="btl-notice__link" target="_blank" rel="noopener noreferrer" href="%10$s">%11$s</a>%12$s</span> |
| 229 |
</p> |
| 230 |
</div>', |
| 231 |
esc_html__( 'NEW', 'betterlinks' ), |
| 232 |
esc_html__( 'BetterLinks Pro 3.0 New UI is here!', 'betterlinks' ), |
| 233 |
esc_html__( ' Explore ', 'betterlinks' ), |
| 234 |
esc_url( 'https://betterlinks.io/docs/create-promo-cards-with-betterlinks' ), |
| 235 |
esc_html__( 'Promo Cards', 'betterlinks' ), |
| 236 |
esc_html__( ', ', 'betterlinks' ), |
| 237 |
esc_url( 'https://betterlinks.io/docs/create-link-in-bio-betterlinks' ), |
| 238 |
esc_html__( 'Bio Links', 'betterlinks' ), |
| 239 |
esc_html__( ', and a refreshed experience. See the ', 'betterlinks' ), |
| 240 |
esc_url( 'https://betterlinks.io/changelog/' ), |
| 241 |
esc_html__( 'changelog', 'betterlinks' ), |
| 242 |
esc_html__( '.', 'betterlinks' ) |
| 243 |
); |
| 244 |
} |
| 245 |
|
| 246 |
public function usage_tracker() { |
| 247 |
$this->opt_in_tracker = PluginUsageTracker::get_instance( BETTERLINKS_PLUGIN_FILE, [ |
| 248 |
'opt_in' => true, |
| 249 |
'goodbye_form' => true, |
| 250 |
'item_id' => '720bbe6537bffcb73f37', |
| 251 |
] ); |
| 252 |
$this->opt_in_tracker->set_notice_options( array( |
| 253 |
'notice' => __( 'Want to help make <strong>BetterLinks</strong> even more awesome? Be the first to get access to <strong>BetterLinks PRO</strong> with a huge <strong>50% Early Bird Discount</strong> if you allow us to track the non-sensitive usage data.', 'betterlinks' ), |
| 254 |
'extra_notice' => __( 'We collect non-sensitive diagnostic data and plugin usage information. Your site URL, WordPress & PHP version, plugins & themes and email address to send you the discount coupon. This data lets us make sure this plugin always stays compatible with the most popular plugins and themes. No spam, I promise.', 'betterlinks' ), |
| 255 |
) ); |
| 256 |
$this->opt_in_tracker->init(); |
| 257 |
} |
| 258 |
|
| 259 |
/** |
| 260 |
* @throws Exception |
| 261 |
*/ |
| 262 |
public function notices() { |
| 263 |
$notices = new Notices( [ |
| 264 |
'id' => 'betterlinks', |
| 265 |
'storage_key' => 'notices', |
| 266 |
'lifetime' => 3, |
| 267 |
'stylesheet_url' => self::ASSET_URL . 'css/betterlinks-admin-notice.css', |
| 268 |
'styles' => self::ASSET_URL . 'css/betterlinks-admin-notice.css', |
| 269 |
'priority' => 7 |
| 270 |
] ); |
| 271 |
|
| 272 |
global $betterlinks; |
| 273 |
$current_user = wp_get_current_user(); |
| 274 |
$total_links = ( is_array( $betterlinks ) && isset( $betterlinks['links'] ) ? count( $betterlinks['links'] ) : 0 ); |
| 275 |
|
| 276 |
$review_notice = sprintf( |
| 277 |
'%s, %s! %s', |
| 278 |
__( 'Howdy', 'betterlinks' ), |
| 279 |
$current_user->user_login, |
| 280 |
sprintf( |
| 281 |
/* translators: %s = placeholder values supplied by WordPress */ |
| 282 |
__( '👋 You have created %d Shortened URLs so far 🎉 If you are enjoying using BetterLinks, feel free to leave a 5* Review on the WordPress Forum.', 'betterlinks' ), |
| 283 |
$total_links |
| 284 |
) |
| 285 |
); |
| 286 |
|
| 287 |
$_review_notice = [ |
| 288 |
'thumbnail' => self::ASSET_URL . 'images/logo-large.svg', |
| 289 |
'html' => '<p>' . $review_notice . '</p>', |
| 290 |
'links' => [ |
| 291 |
'later' => array( |
| 292 |
'link' => 'https://wordpress.org/plugins/betterlinks/#reviews', |
| 293 |
'target' => '_blank', |
| 294 |
'label' => __( 'Ok, you deserve it!', 'betterlinks' ), |
| 295 |
'icon_class' => 'dashicons dashicons-external', |
| 296 |
), |
| 297 |
'allready' => array( |
| 298 |
'label' => __( 'I already did', 'betterlinks' ), |
| 299 |
'icon_class' => 'dashicons dashicons-smiley', |
| 300 |
'attributes' => [ |
| 301 |
'data-dismiss' => true |
| 302 |
], |
| 303 |
), |
| 304 |
'maybe_later' => array( |
| 305 |
'label' => __( 'Maybe Later', 'betterlinks' ), |
| 306 |
'icon_class' => 'dashicons dashicons-calendar-alt', |
| 307 |
'attributes' => [ |
| 308 |
'data-later' => true |
| 309 |
], |
| 310 |
), |
| 311 |
'support' => array( |
| 312 |
'link' => 'https://wpdeveloper.com/support', |
| 313 |
'label' => __( 'I need help', 'betterlinks' ), |
| 314 |
'icon_class' => 'dashicons dashicons-sos', |
| 315 |
), |
| 316 |
'never_show_again' => array( |
| 317 |
'label' => __( 'Never show again', 'betterlinks' ), |
| 318 |
'icon_class' => 'dashicons dashicons-dismiss', |
| 319 |
'attributes' => [ |
| 320 |
'data-dismiss' => true |
| 321 |
], |
| 322 |
) |
| 323 |
] |
| 324 |
]; |
| 325 |
|
| 326 |
$notices->add( |
| 327 |
'review', |
| 328 |
$_review_notice, |
| 329 |
[ |
| 330 |
'start' => $notices->strtotime( '+20 day' ), |
| 331 |
'recurrence' => 30, |
| 332 |
'refresh' => BETTERLINKS_VERSION, |
| 333 |
'dismissible' => true, |
| 334 |
] |
| 335 |
); |
| 336 |
|
| 337 |
$notices->add( |
| 338 |
'opt_in', |
| 339 |
[ $this->opt_in_tracker, 'notice' ], |
| 340 |
[ |
| 341 |
'classes' => 'updated put-dismiss-notice', |
| 342 |
'start' => $notices->strtotime( '+25 day' ), |
| 343 |
// 'start' => $notices->time(), |
| 344 |
'refresh' => BETTERLINKS_VERSION, |
| 345 |
'dismissible' => true, |
| 346 |
'do_action' => 'wpdeveloper_notice_clicked_for_betterlinks', |
| 347 |
'display_if' => ! is_plugin_active( 'betterlinks-pro/betterlinks-pro.php' ) |
| 348 |
] |
| 349 |
); |
| 350 |
|
| 351 |
// Holiday Notice 2024 |
| 352 |
$crown_icon = self::ASSET_URL . 'images/crown.svg'; |
| 353 |
$b_message = "<p style='margin-top: 0; margin-bottom: 0;'>🎁 <strong>Holiday Gifts:</strong> Get Flat 25% OFF on every <strong>BetterLinks PRO</strong> plans & upgrade your WordPress links.</p><a style='display: inline-flex;align-items:center;column-gap:5px;' class='button button-primary' href='https://betterlinks.io/holiday24-admin-notice' target='_blank'><img style='width:15px;' src='{$crown_icon}'/>Upgrade To PRO</a>"; |
| 354 |
$_holiday_notices = [ |
| 355 |
'thumbnail' => self::ASSET_URL . 'images/full-logo.svg', |
| 356 |
'html' => $b_message, |
| 357 |
]; |
| 358 |
|
| 359 |
$notices->add( |
| 360 |
'betterlinks_holiday_24_25', |
| 361 |
$_holiday_notices, |
| 362 |
[ |
| 363 |
'start' => $notices->time(), |
| 364 |
'recurrence' => false, |
| 365 |
'dismissible' => true, |
| 366 |
'refresh' => BETTERLINKS_VERSION, |
| 367 |
"expire" => strtotime( '11:59:59pm 10th January, 2025' ), |
| 368 |
'display_if' => ! is_plugin_active( 'betterlinks-pro/betterlinks-pro.php' ) |
| 369 |
] |
| 370 |
); |
| 371 |
|
| 372 |
// Black Friday Mega Sale Notice |
| 373 |
$black_friday_icon = self::ASSET_URL . 'images/full-logo.svg'; |
| 374 |
$black_friday_message = "<style>#wpnotice-betterlinks-betterlinks_summer_camp_2026_deal { border-left: 3px solid #5252DC !important; } .notice-betterlinks-betterlinks_summer_camp_2026_deal { border-left: 4px solid #FF6B6B !important; }</style><div> <p style='margin-top: 0; margin-bottom: 10px; font-size: 14px;'><strong>🏖️ Summer Savings: </strong>Get AI-powered features to manage, shorten & track every click – now <strong> up to $150 OFF! </strong></p><a style='display: inline-flex;align-items:center;column-gap:5px; background: #5252DC; color: #FFFFFF; font-size: 14px; border-radius: 6px; border-color: unset; font-weight: 500;' class='button button-primary' href='https://betterlinks.io/summer2026-admin-notice' target='_blank'>Upgrade To Pro Now</a><a style='display: inline-flex;align-items:center;column-gap:5px;margin-left:10px; background: unset; box-shadow: unset; border-style: unset; color: #424242; font-size: 14px; text-decoration: underline;' class='button dismiss-btn' href='#' data-dismiss='true'>I Don’t Want Any Discount</a> </div>"; |
| 375 |
$_black_friday_notices = [ |
| 376 |
'thumbnail' => $black_friday_icon, |
| 377 |
'html' => $black_friday_message, |
| 378 |
]; |
| 379 |
// 'betterlinks_black_friday_2025', |
| 380 |
// 'betterlinks_feb_camp_2026', |
| 381 |
// 'betterlinks_spring_camp_2026_deal', |
| 382 |
$notices->add( |
| 383 |
'betterlinks_summer_camp_2026_deal', |
| 384 |
$_black_friday_notices, |
| 385 |
[ |
| 386 |
'start' => strtotime( '12:00:00am 20th May, 2026' ), |
| 387 |
'recurrence' => false, |
| 388 |
'dismissible' => true, |
| 389 |
'refresh' => BETTERLINKS_VERSION, |
| 390 |
"expire" => strtotime( '12:00:00am 25th June, 2026' ), |
| 391 |
'display_if' => ! is_plugin_active( 'betterlinks-pro/betterlinks-pro.php' ), |
| 392 |
'priority' => 7 |
| 393 |
] |
| 394 |
); |
| 395 |
self::$cache_bank->create_account( $notices ); |
| 396 |
self::$cache_bank->calculate_deposits( $notices ); |
| 397 |
|
| 398 |
if ( method_exists( self::$cache_bank, 'clear_notices_in_' ) ) { |
| 399 |
self::$cache_bank->clear_notices_in_( [ |
| 400 |
'toplevel_page_betterlinks', |
| 401 |
'betterlinks_page_betterlinks-keywords-linking', |
| 402 |
'betterlinks_page_betterlinks-manage-tags-and-categories', |
| 403 |
'betterlinks_page_betterlinks-custom-domain', |
| 404 |
'betterlinks_page_betterlinks-analytics', |
| 405 |
'betterlinks_page_betterlinks-settings', |
| 406 |
], $notices, true ); |
| 407 |
} |
| 408 |
} |
| 409 |
|
| 410 |
} |
| 411 |
|