| 1 |
<?php |
| 2 |
|
| 3 |
namespace Code_Snippets\Integration\Promotions\Notices; |
| 4 |
|
| 5 |
use function Code_Snippets\code_snippets; |
| 6 |
use function Code_Snippets\Settings\get_setting; |
| 7 |
use const Code_Snippets\PLUGIN_FILE; |
| 8 |
|
| 9 |
/** |
| 10 |
* Base class for plugin promotion notices. |
| 11 |
*/ |
| 12 |
abstract class Promotion_Base { |
| 13 |
|
| 14 |
/** |
| 15 |
* AJAX action name for dismissing the promotion. |
| 16 |
*/ |
| 17 |
private const AJAX_ACTION = 'code_snippets_dismiss_promotion'; |
| 18 |
|
| 19 |
/** |
| 20 |
* User meta key to store dismissed promotions. |
| 21 |
*/ |
| 22 |
private const USER_META_KEY = 'code_snippets_dismissed_promotions'; |
| 23 |
|
| 24 |
/** |
| 25 |
* Get the name of the plugin being promoted. |
| 26 |
* |
| 27 |
* @return string The name of the plugin. |
| 28 |
*/ |
| 29 |
abstract public function get_plugin_name(): string; |
| 30 |
|
| 31 |
/** |
| 32 |
* Get the slug of the plugin being promoted. |
| 33 |
* |
| 34 |
* @return string The slug of the plugin. |
| 35 |
*/ |
| 36 |
abstract public function get_plugin_slug(): string; |
| 37 |
|
| 38 |
/** |
| 39 |
* Get the admin screen IDs where the promotion should be displayed. |
| 40 |
* |
| 41 |
* @return array An array of admin screen IDs. |
| 42 |
*/ |
| 43 |
abstract public function get_plugin_admin_screens(): array; |
| 44 |
|
| 45 |
/** |
| 46 |
* Get the heading text for the promotion notice. |
| 47 |
* |
| 48 |
* @return string The promotion heading. |
| 49 |
*/ |
| 50 |
public function get_promotion_heading(): string { |
| 51 |
return __( 'Clean up your plugin list today', 'code-snippets' ); |
| 52 |
} |
| 53 |
|
| 54 |
/** |
| 55 |
* Get the message text for the promotion notice. |
| 56 |
* |
| 57 |
* @return string The promotion message. |
| 58 |
*/ |
| 59 |
public function get_promotion_message(): string { |
| 60 |
// translators: %s: plugin name. |
| 61 |
$message = __( 'Code Snippets provides a powerful and user-friendly alternative to "%s", with cloud sync, advanced features, and an intuitive interface.', 'code-snippets' ); |
| 62 |
return sprintf( $message, $this->get_plugin_name() ); |
| 63 |
} |
| 64 |
|
| 65 |
/** |
| 66 |
* Constructor to initialize the promotion notice. |
| 67 |
*/ |
| 68 |
public function __construct() { |
| 69 |
add_action( 'current_screen', [ $this, 'register_promotion_hooks' ] ); |
| 70 |
add_action( 'wp_ajax_' . self::AJAX_ACTION, [ $this, 'dismiss_promotion_ajax_handler' ] ); |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Register hooks to display the promotion notice if conditions are met. |
| 75 |
*/ |
| 76 |
public function register_promotion_hooks() { |
| 77 |
if ( $this->is_plugin_admin_screen() && ! $this->is_promotion_dismissed() ) { |
| 78 |
add_action( 'admin_notices', [ $this, 'display_promotion' ], 1 ); |
| 79 |
add_action( 'admin_print_styles', [ $this, 'print_promotion_styles' ] ); |
| 80 |
add_action( 'admin_print_footer_scripts', [ $this, 'print_dismiss_script' ], 99 ); |
| 81 |
} |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* Determine if the current admin screen is one of the plugin's admin screens. |
| 86 |
* |
| 87 |
* @return bool True if it's a plugin admin screen, false otherwise. |
| 88 |
*/ |
| 89 |
public function is_plugin_admin_screen(): bool { |
| 90 |
if ( ! is_admin() || ! function_exists( 'get_current_screen' ) ) { |
| 91 |
return false; |
| 92 |
} |
| 93 |
|
| 94 |
$current_screen = get_current_screen(); |
| 95 |
return $current_screen && in_array( $current_screen->id, $this->get_plugin_admin_screens(), true ); |
| 96 |
} |
| 97 |
|
| 98 |
/** |
| 99 |
* Check if the promotion has been dismissed by the current user. |
| 100 |
* |
| 101 |
* @return bool True if the promotion is dismissed, false otherwise. |
| 102 |
*/ |
| 103 |
public function is_promotion_dismissed(): bool { |
| 104 |
if ( get_setting( 'general', 'hide_upgrade_menu' ) ) { |
| 105 |
return true; |
| 106 |
} |
| 107 |
|
| 108 |
$user_id = get_current_user_id(); |
| 109 |
|
| 110 |
if ( ! $user_id ) { |
| 111 |
return false; |
| 112 |
} |
| 113 |
|
| 114 |
$dismissed_promotions = get_user_meta( $user_id, self::USER_META_KEY, true ); |
| 115 |
return is_array( $dismissed_promotions ) && in_array( $this->get_plugin_slug(), $dismissed_promotions, true ); |
| 116 |
} |
| 117 |
|
| 118 |
/** |
| 119 |
* Print the CSS styles for the promotion notice. |
| 120 |
*/ |
| 121 |
public function print_promotion_styles() { |
| 122 |
?> |
| 123 |
<style> |
| 124 |
.code-snippets-promotion.notice, |
| 125 |
.code-snippets-promotion.info, |
| 126 |
.code-snippets-promotion.warning, |
| 127 |
.code-snippets-promotion.error { |
| 128 |
padding: 0; |
| 129 |
} |
| 130 |
.code-snippets-promotion { |
| 131 |
display: flex; |
| 132 |
padding: 0; |
| 133 |
border-inline-start-width: 4px; |
| 134 |
} |
| 135 |
|
| 136 |
.code-snippets-promotion-icon { |
| 137 |
padding: 10px; |
| 138 |
background-color: #F0F9FF; |
| 139 |
} |
| 140 |
|
| 141 |
.code-snippets-promotion-content { |
| 142 |
padding: 10px; |
| 143 |
} |
| 144 |
|
| 145 |
.code-snippets-promotion-content p { |
| 146 |
margin-block: 0 .5em; |
| 147 |
} |
| 148 |
</style> |
| 149 |
<?php |
| 150 |
} |
| 151 |
|
| 152 |
/** |
| 153 |
* Print the JavaScript to handle dismissing the promotion notice. |
| 154 |
*/ |
| 155 |
public function print_dismiss_script() { |
| 156 |
?> |
| 157 |
<script> |
| 158 |
jQuery(document).ready(function ($) { |
| 159 |
$('.code-snippets-promotion').on('click', '.notice-dismiss', function () { |
| 160 |
$.post(ajaxurl, { |
| 161 |
action: '<?php echo esc_attr( self::AJAX_ACTION ); ?>', |
| 162 |
nonce: '<?php echo esc_attr( wp_create_nonce( self::AJAX_ACTION ) ); ?>', |
| 163 |
promotion_id: $(this).closest('.code-snippets-promotion').data('promotion-id') |
| 164 |
}) |
| 165 |
}) |
| 166 |
}) |
| 167 |
</script> |
| 168 |
<?php |
| 169 |
} |
| 170 |
|
| 171 |
/** |
| 172 |
* Display the promotion notice in the admin area. |
| 173 |
*/ |
| 174 |
public function display_promotion() { |
| 175 |
?> |
| 176 |
<div |
| 177 |
class="notice notice-info is-dismissible code-snippets-promotion" |
| 178 |
data-promotion-id="<?php echo esc_attr( $this->get_plugin_slug() ); ?>" |
| 179 |
aria-label="<?php esc_attr_e( 'Code Snippets Promotion', 'code-snippets' ); ?>" |
| 180 |
role="region" |
| 181 |
> |
| 182 |
<div class="code-snippets-promotion-icon"> |
| 183 |
<img |
| 184 |
src="<?php echo esc_url( plugins_url( 'assets/icon.svg', PLUGIN_FILE ) ); ?>" |
| 185 |
alt="<?php esc_attr_e( 'Code Snippets Logo', 'code-snippets' ); ?>" |
| 186 |
width="32" |
| 187 |
height="32" |
| 188 |
/> |
| 189 |
</div> |
| 190 |
<div class="code-snippets-promotion-content"> |
| 191 |
<p><strong><?php echo esc_html( $this->get_promotion_heading() ); ?></strong></p> |
| 192 |
<p><?php echo esc_html( $this->get_promotion_message() ); ?></p> |
| 193 |
<p><?php $this->print_promotion_buttons(); ?></p> |
| 194 |
</div> |
| 195 |
</div> |
| 196 |
<?php |
| 197 |
} |
| 198 |
|
| 199 |
/** |
| 200 |
* AJAX handler to dismiss the promotion notice. |
| 201 |
*/ |
| 202 |
public function dismiss_promotion_ajax_handler() { |
| 203 |
check_ajax_referer( self::AJAX_ACTION, 'nonce' ); |
| 204 |
|
| 205 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 206 |
wp_send_json_error( 'Unauthorized', 403 ); |
| 207 |
} |
| 208 |
|
| 209 |
$promotion_id = isset( $_POST['promotion_id'] ) ? sanitize_text_field( wp_unslash( $_POST['promotion_id'] ) ) : ''; |
| 210 |
|
| 211 |
if ( empty( $promotion_id ) ) { |
| 212 |
wp_send_json_error( 'Invalid promotion id.', 400 ); |
| 213 |
} |
| 214 |
|
| 215 |
$user_id = get_current_user_id(); |
| 216 |
$dismissed_promotions = get_user_meta( $user_id, self::USER_META_KEY, true ); |
| 217 |
|
| 218 |
if ( ! is_array( $dismissed_promotions ) ) { |
| 219 |
$dismissed_promotions = []; |
| 220 |
} |
| 221 |
|
| 222 |
if ( ! in_array( $promotion_id, $dismissed_promotions, true ) ) { |
| 223 |
$dismissed_promotions[] = $promotion_id; |
| 224 |
update_user_meta( $user_id, self::USER_META_KEY, $dismissed_promotions ); |
| 225 |
} |
| 226 |
|
| 227 |
wp_send_json_success(); |
| 228 |
} |
| 229 |
|
| 230 |
/** |
| 231 |
* Check if the user should see the migration button. |
| 232 |
* |
| 233 |
* @return bool Whether the user should see the migration button. |
| 234 |
*/ |
| 235 |
public function show_migration_button(): bool { |
| 236 |
return false; |
| 237 |
} |
| 238 |
|
| 239 |
/** |
| 240 |
* Render buttons for the promotion notice. |
| 241 |
* |
| 242 |
* @uses show_migration_button() to determine which buttons to show. |
| 243 |
*/ |
| 244 |
protected function print_promotion_buttons() { |
| 245 |
if ( $this->show_migration_button() ) { |
| 246 |
$primary_text = __( 'Migrate to Code Snippets', 'code-snippets' ); |
| 247 |
$primary_url = add_query_arg( |
| 248 |
[ |
| 249 |
'tab' => 'migrate', |
| 250 |
'from' => $this->get_plugin_slug(), |
| 251 |
], |
| 252 |
code_snippets()->get_menu_url( 'import' ) |
| 253 |
); |
| 254 |
} else { |
| 255 |
$primary_text = __( 'Manage your snippets', 'code-snippets' ); |
| 256 |
$primary_url = code_snippets()->get_menu_url(); |
| 257 |
} |
| 258 |
|
| 259 |
printf( |
| 260 |
'<a href="%s" class="button button-primary">%s</a>', |
| 261 |
esc_url( $primary_url ), |
| 262 |
esc_html( $primary_text ) |
| 263 |
); |
| 264 |
|
| 265 |
$secondary_url = add_query_arg( |
| 266 |
[ |
| 267 |
'utm_source' => $this->get_plugin_slug(), |
| 268 |
'utm_medium' => 'promotion', |
| 269 |
'utm_campaign' => 'custom-code', |
| 270 |
], |
| 271 |
'https://codesnippets.pro/pricing' |
| 272 |
); |
| 273 |
|
| 274 |
printf( |
| 275 |
' <a href="%s" class="button button-secondary" target="_blank" rel="noopener noreferrer">%s</a> ', |
| 276 |
esc_url( $secondary_url ), |
| 277 |
esc_html__( 'Learn more', 'code-snippets' ) |
| 278 |
); |
| 279 |
} |
| 280 |
} |
| 281 |
|