Skip to content

Commit 184ef27

Browse files
authored
Merge pull request #173 from Codeinwp/feat/welcome_upgrade
feat: allow upsell notification for new users
2 parents f194615 + 54351ce commit 184ef27

File tree

5 files changed

+415
-1
lines changed

5 files changed

+415
-1
lines changed

src/Loader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ final class Loader {
5656
'recommendation',
5757
'notification',
5858
'promotions',
59+
'welcome',
5960
'compatibilities',
6061
];
6162

src/Modules/Notification.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ public static function get_notification_html( $notification_details ) {
256256
$default = [
257257
'id' => '',
258258
'heading' => '',
259+
'img_src' => '',
259260
'message' => '',
260261
'ctas' => [
261262
'confirm' => [
@@ -267,17 +268,26 @@ public static function get_notification_html( $notification_details ) {
267268
'text' => '',
268269
],
269270
],
271+
'type' => 'success',
270272
];
271273
$notification_details = wp_parse_args( $notification_details, $default );
272274
global $pagenow;
275+
$type = in_array( $notification_details['type'], [ 'success', 'info', 'warning', 'error' ], true ) ? $notification_details['type'] : 'success';
273276
$notification_details['ctas']['cancel']['link'] = wp_nonce_url( add_query_arg( [ 'nid' => $notification_details['id'] ], admin_url( $pagenow ) ), $notification_details['id'], 'tsdk_dismiss_nonce' );
274-
$notification_html = '<div class="notice notice-success is-dismissible themeisle-sdk-notice" data-notification-id="' . esc_attr( $notification_details['id'] ) . '" id="' . esc_attr( $notification_details['id'] ) . '-notification"> <div class="themeisle-sdk-notification-box">';
277+
$notification_html = '<div class="notice notice-' . $type . ' is-dismissible themeisle-sdk-notice" data-notification-id="' . esc_attr( $notification_details['id'] ) . '" id="' . esc_attr( $notification_details['id'] ) . '-notification"> <div class="themeisle-sdk-notification-box">';
275278

276279
if ( ! empty( $notification_details['heading'] ) ) {
277280
$notification_html .= sprintf( '<h4>%s</h4>', wp_kses_post( $notification_details['heading'] ) );
278281
}
282+
if ( ! empty( $notification_details['img_src'] ) ) {
283+
$notification_html .= '<div class="wrap-flex">';
284+
$notification_html .= sprintf( '<img src="%s" alt="%s" />', esc_attr( $notification_details['img_src'] ), esc_attr( $notification_details['heading'] ) );
285+
}
279286
if ( ! empty( $notification_details['message'] ) ) {
280287
$notification_html .= wp_kses_post( $notification_details['message'] );
288+
if ( ! empty( $notification_details['img_src'] ) ) {
289+
$notification_html .= '</div>';
290+
}
281291
}
282292
$notification_html .= '<div class="actions">';
283293

@@ -317,6 +327,18 @@ public static function render_snippets() {
317327
padding: 3px;
318328
}
319329

330+
.themeisle-sdk-notification-box .wrap-flex {
331+
display: flex;
332+
align-items: center;
333+
justify-content: start;
334+
gap: 12px;
335+
}
336+
337+
.themeisle-sdk-notification-box .wrap-flex img {
338+
width: 42px;
339+
object-fit: cover;
340+
}
341+
320342
.themeisle-sdk-notification-box .actions {
321343
margin-top: 6px;
322344
margin-bottom: 4px;
@@ -377,6 +399,7 @@ public static function dismiss() {
377399
if ( empty( $id ) ) {
378400
wp_send_json( [] );
379401
}
402+
self::setup_notifications();
380403
$ids = wp_list_pluck( self::$notifications, 'id' );
381404
if ( ! in_array( $id, $ids, true ) ) {
382405
wp_send_json( [] );

src/Modules/Welcome.php

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
/**
3+
* The welcome model class for ThemeIsle SDK
4+
*
5+
* Here's how to hook it in your plugin or theme:
6+
* ```php
7+
* add_filter( '<product_slug>_welcome_metadata', function() {
8+
* return [
9+
* 'is_enabled' => <condition_if_pro_available>,
10+
* 'pro_name' => 'Product PRO name',
11+
* 'logo' => '<path_to_logo>',
12+
* 'cta_link' => tsdk_utmify( 'https://link_to_upgrade.with/?discount=<discountCode>')
13+
* ];
14+
* } );
15+
* ```
16+
*
17+
* @package ThemeIsleSDK
18+
* @subpackage Modules
19+
* @copyright Copyright (c) 2023, Bogdan Preda
20+
* @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
21+
* @since 1.0.0
22+
*/
23+
24+
namespace ThemeisleSDK\Modules;
25+
26+
// Exit if accessed directly.
27+
use ThemeisleSDK\Common\Abstract_Module;
28+
29+
if ( ! defined( 'ABSPATH' ) ) {
30+
exit;
31+
}
32+
33+
/**
34+
* Promotions module for ThemeIsle SDK.
35+
*/
36+
class Welcome extends Abstract_Module {
37+
38+
/**
39+
* Debug mode.
40+
*
41+
* @var bool
42+
*/
43+
private $debug = false;
44+
45+
/**
46+
* Welcome metadata.
47+
*
48+
* @var array
49+
*/
50+
private $welcome_discounts = array();
51+
52+
/**
53+
* Check that we can load this module.
54+
*
55+
* @param \ThemeisleSDK\Product $product The product.
56+
*
57+
* @return bool
58+
*/
59+
public function can_load( $product ) {
60+
$this->debug = apply_filters( 'themeisle_sdk_welcome_debug', $this->debug );
61+
$welcome_metadata = apply_filters( $product->get_key() . '_welcome_metadata', array() );
62+
63+
$is_welcome_enabled = $this->is_welcome_meta_valid( $welcome_metadata );
64+
65+
if ( $is_welcome_enabled ) {
66+
$this->welcome_discounts[ $product->get_key() ] = $welcome_metadata;
67+
}
68+
69+
return $this->debug || $is_welcome_enabled;
70+
}
71+
72+
/**
73+
* Check that the metadata is valid and the welcome is enabled.
74+
*
75+
* @param array $welcome_metadata The metadata to validate.
76+
*
77+
* @return bool
78+
*/
79+
private function is_welcome_meta_valid( $welcome_metadata ) {
80+
return ! empty( $welcome_metadata ) && isset( $welcome_metadata['is_enabled'] ) && $welcome_metadata['is_enabled'];
81+
}
82+
83+
/**
84+
* Load the module.
85+
*
86+
* @param \ThemeisleSDK\Product $product The product.
87+
*
88+
* @return $this
89+
*/
90+
public function load( $product ) {
91+
if ( ! current_user_can( 'install_plugins' ) ) {
92+
return;
93+
}
94+
95+
$this->product = $product;
96+
if ( ! $this->is_time_to_show_welcome() && $this->debug === false ) {
97+
return;
98+
}
99+
100+
add_filter( 'themeisle_sdk_registered_notifications', [ $this, 'add_notification' ], 99, 1 );
101+
102+
return $this;
103+
}
104+
105+
/**
106+
* Check if it's time to show the welcome.
107+
*
108+
* @return bool
109+
*/
110+
private function is_time_to_show_welcome() {
111+
// if 7 days from install have not passed, don't show the welcome.
112+
if ( $this->product->get_install_time() + 7 * DAY_IN_SECONDS > time() ) {
113+
return false;
114+
}
115+
116+
// if 12 days from install have passed, don't show the welcome ( after 7 days for 5 days ).
117+
if ( $this->product->get_install_time() + 12 * DAY_IN_SECONDS < time() ) {
118+
return false;
119+
}
120+
121+
return true;
122+
}
123+
124+
/**
125+
* Add the welcome notification.
126+
* Will block all other notifications if a welcome notification is present.
127+
*
128+
* @return array
129+
*/
130+
public function add_notification( $all_notifications ) {
131+
if ( empty( $this->welcome_discounts ) ) {
132+
return $all_notifications;
133+
}
134+
135+
if ( ! isset( $this->welcome_discounts[ $this->product->get_key() ] ) ) {
136+
return $all_notifications;
137+
}
138+
139+
// filter out the notifications that are not welcome upsells
140+
// if we arrived here we will have at least one welcome upsell
141+
$all_notifications = array_filter(
142+
$all_notifications,
143+
function( $notification ) {
144+
return strpos( $notification['id'], '_welcome_upsell_flag' ) !== false;
145+
}
146+
);
147+
148+
$offer = $this->welcome_discounts[ $this->product->get_key() ];
149+
150+
$response = [];
151+
$logo = isset( $offer['logo'] ) ? $offer['logo'] : '';
152+
$pro_name = isset( $offer['pro_name'] ) ? $offer['pro_name'] : $this->product->get_friendly_name() . ' PRO';
153+
154+
$link = $offer['cta_link'];
155+
156+
$message = apply_filters( $this->product->get_key() . '_welcome_upsell_message', '<p>You\'ve been using <b>{product}</b> for 7 days now and we appreciate your loyalty! We also want to make sure you\'re getting the most out of our product. That\'s why we\'re offering you a special deal - upgrade to <b>{pro_product}</b> in the next 5 days and receive a discount of <b>up to 30%</b>. <a href="{cta_link}" target="_blank">Upgrade now</a> and unlock all the amazing features of <b>{pro_product}</b>!</p>' );
157+
158+
$button_submit = apply_filters( $this->product->get_key() . '_feedback_review_button_do', 'Upgrade Now!' );
159+
$button_cancel = apply_filters( $this->product->get_key() . '_feedback_review_button_cancel', 'No, thanks.' );
160+
$message = str_replace(
161+
[ '{product}', '{pro_product}', '{cta_link}' ],
162+
[
163+
$this->product->get_friendly_name(),
164+
$pro_name,
165+
$link,
166+
],
167+
$message
168+
);
169+
170+
$all_notifications[] = [
171+
'id' => $this->product->get_key() . '_welcome_upsell_flag',
172+
'message' => $message,
173+
'img_src' => $logo,
174+
'ctas' => [
175+
'confirm' => [
176+
'link' => $link,
177+
'text' => $button_submit,
178+
],
179+
'cancel' => [
180+
'link' => '#',
181+
'text' => $button_cancel,
182+
],
183+
],
184+
'type' => 'info',
185+
];
186+
187+
$key = array_rand( $all_notifications );
188+
$response[] = $all_notifications[ $key ];
189+
190+
return $response;
191+
}
192+
193+
}

start.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
$themeisle_library_path . '/src/Modules/Review.php',
3434
$themeisle_library_path . '/src/Modules/Recommendation.php',
3535
$themeisle_library_path . '/src/Modules/Promotions.php',
36+
$themeisle_library_path . '/src/Modules/Welcome.php',
3637
$themeisle_library_path . '/src/Modules/Compatibilities.php',
3738
];
3839

0 commit comments

Comments
 (0)