PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.5.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.5.0
4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Integrations / NpsSurvey / NpsSurveyNotice.php
surecart / app / src / Integrations / NpsSurvey Last commit date
NpsSurveyNotice.php 2 weeks ago NpsSurveyServiceProvider.php 3 months ago
NpsSurveyNotice.php
278 lines
1 <?php
2
3 namespace SureCart\Integrations\NpsSurvey;
4
5 use Nps_Survey;
6
7 /**
8 * Nps Survey Notice.
9 */
10 class NpsSurveyNotice {
11 /**
12 * Option name for last NPS submission date.
13 *
14 * @var string
15 */
16 public const LAST_SUBMITTED_OPTION = 'surecart_nps_last_submitted';
17
18 /**
19 * NPS Survey ID used in the library.
20 *
21 * @var string
22 */
23 public const NPS_SURVEY_ID = 'nps-survey-surecart';
24
25 /**
26 * Number of days after setup before showing NPS survey.
27 *
28 * @var int
29 */
30 public const DAYS_AFTER_SETUP = 15;
31
32 /**
33 * Minimum days between NPS survey displays.
34 *
35 * @var int
36 */
37 public const DAYS_BETWEEN_SURVEYS = 90;
38
39 /**
40 * Ottokit Webhook URL for NPS survey submissions.
41 *
42 * @var string
43 */
44 public const OTTOKIT_WEBHOOK_URL = 'https://webhook.ottokit.com/ottokit/01f97dac-ade0-4d21-b9a4-c788ca28da22';
45
46 /**
47 * Bootstrap.
48 *
49 * @return void
50 */
51 public function bootstrap(): void {
52 add_filter( 'nps_survey_post_data', [ $this, 'getNpsSurveyPostData' ], 10 );
53 add_filter( 'nps_survey_api_endpoint', [ $this, 'getNpsSurveyApiEndpoint' ], 11, 2 );
54 add_filter( 'nps_survey_should_skip_status_update', [ $this, 'handleStatusUpdate' ], 10, 2 );
55 add_filter( 'nps_survey_vars', [ $this, 'ensureNpsSurveyVars' ], 10 );
56
57 // Asset loading and the admin footer notice are admin-only; the nps_survey_* filters above stay registered unconditionally because the upstream library may invoke them from admin-AJAX or REST contexts where is_admin() is false.
58 if ( ! is_admin() ) {
59 return;
60 }
61
62 add_filter( 'script_loader_src', [ $this, 'forceNpsAssetSrc' ], 10, 2 );
63 add_filter( 'style_loader_src', [ $this, 'forceNpsAssetSrc' ], 10, 2 );
64 add_action( 'admin_footer', [ $this, 'showNpsNotice' ], 999 );
65 }
66
67 /**
68 * Check all conditions to determine if the survey should be shown.
69 *
70 * @return bool
71 */
72 protected function isReadyToShow(): bool {
73 // Must not have been submitted within the last 90 days.
74 $last_submitted = get_option( self::LAST_SUBMITTED_OPTION );
75 if ( $last_submitted && time() - (int) $last_submitted < self::DAYS_BETWEEN_SURVEYS * DAY_IN_SECONDS ) {
76 return false;
77 }
78
79 return true;
80 }
81
82 /**
83 * Handle NPS survey status updates.
84 *
85 * On submission, we skip the library's permanent dismiss and instead
86 * store our own timestamp so we can re-show the survey after 90 days.
87 *
88 * @param bool $skip Whether to skip the status update.
89 * @param array $data Data about the action (nps_id, action_type, plugin_slug, etc.).
90 * @return bool
91 */
92 public function handleStatusUpdate( bool $skip, array $data ): bool {
93 $plugin_slug = $data['plugin_slug'] ?? $data['nps_id'] ?? '';
94
95 // Only handle SureCart's NPS survey.
96 if ( 'surecart' !== $plugin_slug && self::NPS_SURVEY_ID !== $plugin_slug ) {
97 return $skip;
98 }
99
100 if ( 'submit' === ( $data['action_type'] ?? '' ) ) {
101 // Store our own submission timestamp for the 90-day re-show window.
102 update_option( self::LAST_SUBMITTED_OPTION, time(), false );
103
104 // Skip the library's permanent dismiss so we can re-show after 90 days.
105 return true;
106 }
107
108 return $skip;
109 }
110
111 /**
112 * Enrich NPS survey post data with SureCart account info.
113 *
114 * @param array $post_data Post data.
115 * @return array
116 */
117 public function getNpsSurveyPostData( array $post_data ): array {
118 if ( 'surecart' !== ( $post_data['plugin_slug'] ?? '' ) ) {
119 return $post_data;
120 }
121
122 $account = \SureCart::account();
123 $post_data['is_free_plan'] = $account->plan->free ?? true;
124 $post_data['plan_slug'] = $account->plan->name ?? '';
125
126 return $post_data;
127 }
128
129 /**
130 * Override the NPS survey API endpoint for SureCart.
131 *
132 * @param string $api_endpoint Default API endpoint.
133 * @param array $post_data Post data.
134 * @return string
135 */
136 public function getNpsSurveyApiEndpoint( string $api_endpoint, array $post_data ): string {
137 if ( 'surecart' !== ( $post_data['plugin_slug'] ?? '' ) ) {
138 return $api_endpoint;
139 }
140
141 return self::OTTOKIT_WEBHOOK_URL;
142 }
143
144 /**
145 * Ensure NPS survey vars include a valid REST API nonce for admins.
146 *
147 * @param array $vars Localized script variables.
148 * @return array
149 */
150 public function ensureNpsSurveyVars( array $vars ): array {
151 if ( empty( $vars['rest_api_nonce'] ) && current_user_can( 'manage_options' ) ) {
152 $vars['rest_api_nonce'] = wp_create_nonce( 'wp_rest' );
153 }
154
155 return $vars;
156 }
157
158 /**
159 * Map of NPS survey asset handles to their corresponding filenames.
160 *
161 * @var array<string, string>
162 */
163 private const NPS_ASSET_MAP = [
164 'nps-survey-script' => 'main.js',
165 'nps-survey-style' => 'style-main.css',
166 ];
167
168 /**
169 * Screen ids the NPS survey is allowed to render on.
170 *
171 * Scoped to the Dashboard and Settings screens only, so the survey does not
172 * appear across every SureCart admin page (e.g. the Product Editor).
173 *
174 * @return array
175 */
176 private function getNpsScreenIds(): array {
177 /**
178 * Filter the admin screen ids the NPS survey is allowed to render on.
179 *
180 * @since 4.4.0
181 *
182 * @param array $screen_ids Screen ids the survey may render on.
183 */
184 return apply_filters(
185 'surecart_nps_survey_screen_ids',
186 [
187 'toplevel_page_sc-dashboard',
188 'surecart_page_sc-settings',
189 ]
190 );
191 }
192
193 /**
194 * Force NPS survey assets to load from SureCart's vendor copy on SureCart admin pages.
195 *
196 * Bound to `script_loader_src` / `style_loader_src`, which may pass `null` for
197 * dependency-only handles registered with `src = false`. Query Monitor and similar
198 * tools enumerate every registered handle and can trigger this case.
199 *
200 * @param string|null $src Asset source URL.
201 * @param string|null $handle Asset handle name.
202 * @return string|null
203 */
204 public function forceNpsAssetSrc( ?string $src, ?string $handle ): ?string {
205 if ( ! is_string( $src ) || ! is_string( $handle ) ) {
206 return $src;
207 }
208
209 $filename = self::NPS_ASSET_MAP[ $handle ] ?? null;
210 if ( ! $filename ) {
211 return $src;
212 }
213
214 $screen = get_current_screen();
215 if ( ! $screen || ! in_array( $screen->id, $this->getNpsScreenIds(), true ) ) {
216 return $src;
217 }
218
219 $query = wp_parse_url( $src, PHP_URL_QUERY );
220
221 return plugins_url( 'vendor/brainstormforce/nps-survey/dist/' . $filename, SURECART_PLUGIN_FILE )
222 . ( $query ? '?' . $query : '' );
223 }
224
225 /**
226 * Show the NPS survey notice in the admin footer.
227 *
228 * @return void
229 */
230 public function showNpsNotice(): void {
231 // Only show on the SureCart Dashboard and Settings screens.
232 $screen = get_current_screen();
233 if ( ! $screen || ! in_array( $screen->id, $this->getNpsScreenIds(), true ) ) {
234 return;
235 }
236
237 if ( ! class_exists( 'Nps_Survey' ) ) {
238 return;
239 }
240
241 if ( ! current_user_can( 'manage_options' ) ) {
242 return;
243 }
244
245 if ( ! $this->isReadyToShow() ) {
246 return;
247 }
248
249 Nps_Survey::show_nps_notice(
250 self::NPS_SURVEY_ID,
251 [
252 'show_if' => true,
253 'dismiss_timespan' => self::DAYS_BETWEEN_SURVEYS * DAY_IN_SECONDS,
254 'display_after' => self::DAYS_AFTER_SETUP * DAY_IN_SECONDS,
255 'dismiss_count' => 0, // We handle dismissals ourselves in handleStatusUpdate().
256 'plugin_slug' => 'surecart',
257 'allow_review' => true, // Enables promoter (8-10) vs non-promoter (<8) split.
258 'show_on_screens' => $this->getNpsScreenIds(),
259 'message' => [
260 // Rating step.
261 'logo' => esc_url( trailingslashit( plugin_dir_url( SURECART_PLUGIN_FILE ) ) . 'images/icon.svg' ),
262 'plugin_name' => __( 'SureCart', 'surecart' ),
263 'nps_rating_message' => __( 'How likely are you to recommend #pluginname to your friends or colleagues?', 'surecart' ),
264
265 // Promoters (score 8-10): "plugin-rating" step.
266 'feedback_title' => __( 'Wow, we appreciate the feedback! 😀', 'surecart' ),
267 'feedback_content' => __( "Thanks. This means a lot!\n\nIf you've got a minute, a review on WordPress would mean the world to us. It helps others find us and keeps our momentum strong.", 'surecart' ),
268 'plugin_rating_link' => esc_url( 'https://wordpress.org/support/plugin/surecart/reviews/#new-post' ),
269
270 // General score (score 1-7): "comment" step — default for all scores < 8.
271 'plugin_rating_title' => __( 'Thanks for your honest feedback! 😊', 'surecart' ),
272 'plugin_rating_content' => __( "We'd love to understand what's not working or what's missing for you. What can SureCart improve to earn a higher score from you?", 'surecart' ),
273 ],
274 ]
275 );
276 }
277 }
278