Changeset 3548033
- Timestamp:
- 05/25/2026 06:02:34 PM (5 months ago)
- Location:
- smsreminder-appointment-booking
- Files:
-
- 41 added
- 10 edited
-
tags/1.1.0 (added)
-
tags/1.1.0/assets (added)
-
tags/1.1.0/assets/admin-booking-settings.css (added)
-
tags/1.1.0/assets/admin-booking-settings.js (added)
-
tags/1.1.0/assets/appointment-booking-for-google-calendar-with-sms-reminders-hero.png (added)
-
tags/1.1.0/build (added)
-
tags/1.1.0/build/blocks (added)
-
tags/1.1.0/build/blocks/book-button (added)
-
tags/1.1.0/build/blocks/book-button/block.json (added)
-
tags/1.1.0/build/blocks/book-button/index.asset.php (added)
-
tags/1.1.0/build/blocks/book-button/index.js (added)
-
tags/1.1.0/includes (added)
-
tags/1.1.0/includes/Admin (added)
-
tags/1.1.0/includes/Admin/class-connection-actions.php (added)
-
tags/1.1.0/includes/Admin/class-settings-page.php (added)
-
tags/1.1.0/includes/Services (added)
-
tags/1.1.0/includes/Services/class-connected-app-client.php (added)
-
tags/1.1.0/includes/Services/class-connection-service.php (added)
-
tags/1.1.0/includes/Support (added)
-
tags/1.1.0/includes/Support/class-book-button-defaults.php (added)
-
tags/1.1.0/includes/Support/class-connection-store.php (added)
-
tags/1.1.0/includes/Support/class-pkce.php (added)
-
tags/1.1.0/includes/Support/class-reminders-promo-resolution.php (added)
-
tags/1.1.0/includes/Support/class-reminders-promo-visibility.php (added)
-
tags/1.1.0/includes/class-admin.php (added)
-
tags/1.1.0/includes/class-blocks.php (added)
-
tags/1.1.0/includes/class-database.php (added)
-
tags/1.1.0/includes/class-plugin-config.php (added)
-
tags/1.1.0/includes/class-plugin.php (added)
-
tags/1.1.0/readme.txt (added)
-
tags/1.1.0/smsreminder-appointment-booking.php (added)
-
tags/1.1.0/src (added)
-
tags/1.1.0/src/blocks (added)
-
tags/1.1.0/src/blocks/book-button (added)
-
tags/1.1.0/src/blocks/book-button/block.json (added)
-
tags/1.1.0/src/blocks/book-button/edit.js (added)
-
tags/1.1.0/src/blocks/book-button/index.js (added)
-
tags/1.1.0/src/blocks/book-button/save.js (added)
-
tags/1.1.0/uninstall.php (added)
-
trunk/assets/admin-booking-settings.css (modified) (1 diff)
-
trunk/assets/admin-booking-settings.js (modified) (2 diffs)
-
trunk/includes/Admin/class-connection-actions.php (modified) (3 diffs)
-
trunk/includes/Admin/class-settings-page.php (modified) (10 diffs)
-
trunk/includes/Services/class-connection-service.php (modified) (2 diffs)
-
trunk/includes/Support/class-reminders-promo-resolution.php (added)
-
trunk/includes/Support/class-reminders-promo-visibility.php (added)
-
trunk/includes/class-admin.php (modified) (1 diff)
-
trunk/includes/class-plugin-config.php (modified) (1 diff)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/smsreminder-appointment-booking.php (modified) (2 diffs)
-
trunk/uninstall.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
smsreminder-appointment-booking/trunk/assets/admin-booking-settings.css
r3519533 r3548033 245 245 } 246 246 } 247 248 .srb-reminders-column-header { 249 display: inline-flex; 250 align-items: center; 251 gap: 4px; 252 } 253 254 .srb-help-tip-wrap { 255 position: relative; 256 display: inline-flex; 257 vertical-align: middle; 258 } 259 260 .srb-help-tip { 261 border: none; 262 background: transparent; 263 padding: 0; 264 margin: 0; 265 width: 20px; 266 height: 20px; 267 line-height: 20px; 268 font-size: 20px; 269 color: #646970; 270 cursor: help; 271 vertical-align: middle; 272 } 273 274 .srb-help-tip:hover, 275 .srb-help-tip:focus { 276 color: #2271b1; 277 } 278 279 .srb-help-tip:focus { 280 outline: 2px solid #2271b1; 281 outline-offset: 1px; 282 box-shadow: none; 283 } 284 285 .srb-card table.widefat { 286 overflow: visible; 287 } 288 289 .srb-card table.widefat thead th { 290 overflow: visible; 291 position: relative; 292 } 293 294 .srb-help-tip-popover { 295 position: absolute; 296 z-index: 100; 297 top: calc(100% + 6px); 298 left: 50%; 299 transform: translateX(-50%); 300 min-width: 220px; 301 max-width: min(280px, calc(100vw - 32px)); 302 padding: 12px 14px; 303 background: #fff; 304 border: 1px solid #c3c4c7; 305 border-radius: 4px; 306 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12); 307 text-align: left; 308 font-weight: 400; 309 font-size: 13px; 310 line-height: 1.45; 311 color: #1d2327; 312 white-space: normal; 313 } 314 315 .srb-help-tip-popover[hidden] { 316 display: none; 317 } 318 319 .srb-help-tip-popover__title { 320 margin: 0 0 6px; 321 } 322 323 .srb-help-tip-popover__body { 324 margin: 0 0 8px; 325 } 326 327 .srb-help-tip-popover__action { 328 margin: 0; 329 } 330 331 @media (max-width: 782px) { 332 .srb-help-tip-popover { 333 left: auto; 334 right: 0; 335 transform: none; 336 } 337 } 338 339 .srb-reminders-promo { 340 background: #fff8e5; 341 border-color: #f0e6c8; 342 color: #1d2327; 343 } 344 345 .srb-reminders-promo__header { 346 display: flex; 347 justify-content: space-between; 348 align-items: flex-start; 349 gap: 16px; 350 } 351 352 .srb-reminders-promo__header h2 { 353 flex: 1 1 auto; 354 margin-bottom: 0; 355 } 356 357 .srb-reminders-promo__dismiss-form { 358 flex: 0 0 auto; 359 margin: 0; 360 } 361 362 .srb-reminders-promo__dismiss { 363 color: #50575e; 364 text-decoration: none; 365 } 366 367 .srb-reminders-promo__dismiss:hover, 368 .srb-reminders-promo__dismiss:focus { 369 color: #1d2327; 370 text-decoration: underline; 371 } 372 373 .srb-reminders-promo__dismiss:focus { 374 box-shadow: 0 0 0 1px #2271b1; 375 outline: 1px solid transparent; 376 } 377 378 .srb-reminders-promo__body { 379 margin-top: 10px; 380 } 381 382 .srb-reminders-promo__actions { 383 margin-top: 14px; 384 margin-bottom: 0; 385 } -
smsreminder-appointment-booking/trunk/assets/admin-booking-settings.js
r3519533 r3548033 233 233 } 234 234 235 236 237 function trackEvent( eventName ) { 238 if ( 239 typeof srbAdminConfig === 'undefined' || 240 ! srbAdminConfig.bearerToken || 241 ! srbAdminConfig.apiBase || 242 ! srbAdminConfig.analyticsTrackPath || 243 ! eventName 244 ) { 245 return; 246 } 247 248 var base = String( srbAdminConfig.apiBase ).replace( /\/$/, '' ); 249 var trackPath = String( srbAdminConfig.analyticsTrackPath ).replace( /^\//, '' ); 250 var url = base + '/' + trackPath; 251 252 try { 253 fetch( url, { 254 method: 'POST', 255 headers: { 256 'Content-Type': 'application/json', 257 Authorization: 'Bearer ' + srbAdminConfig.bearerToken, 258 }, 259 body: JSON.stringify( { event: eventName } ), 260 keepalive: true, 261 } ).catch( function () { 262 // Analytics failures are silent; admin UX must not be blocked. 263 } ); 264 } catch ( error ) { 265 // Analytics failures are silent; admin UX must not be blocked. 266 } 267 } 268 269 function initRemindersPromoAnalytics() { 270 var promoBlock = document.querySelector( '[data-srb-reminders-promo]' ); 271 if ( promoBlock ) { 272 trackEvent( 'wordpress_settings_reminders_promo_block_viewed' ); 273 274 var enableLink = promoBlock.querySelector( '[data-srb-reminders-promo-enable]' ); 275 if ( enableLink ) { 276 enableLink.addEventListener( 'click', function () { 277 trackEvent( 'wordpress_settings_reminders_promo_block_enable_click' ); 278 } ); 279 } 280 281 var learnMoreLink = promoBlock.querySelector( '[data-srb-reminders-promo-learn-more]' ); 282 if ( learnMoreLink ) { 283 learnMoreLink.addEventListener( 'click', function () { 284 trackEvent( 'wordpress_settings_reminders_promo_block_learn_more_click' ); 285 } ); 286 } 287 } 288 289 var tooltipLearnMore = document.querySelector( '[data-srb-reminders-column-tooltip-learn-more]' ); 290 if ( tooltipLearnMore ) { 291 tooltipLearnMore.addEventListener( 'click', function () { 292 trackEvent( 'wordpress_settings_reminders_column_tooltip_learn_more_click' ); 293 } ); 294 } 295 } 296 297 function closeHelpTip( wrap ) { 298 var button = wrap.querySelector( '.srb-help-tip' ); 299 var popover = wrap.querySelector( '.srb-help-tip-popover' ); 300 if ( ! button || ! popover ) { 301 return; 302 } 303 304 popover.hidden = true; 305 button.setAttribute( 'aria-expanded', 'false' ); 306 button.removeAttribute( 'aria-describedby' ); 307 } 308 309 function closeAllHelpTips( exceptWrap ) { 310 var wraps = document.querySelectorAll( '.srb-help-tip-wrap' ); 311 wraps.forEach( function ( wrap ) { 312 if ( exceptWrap && wrap === exceptWrap ) { 313 return; 314 } 315 closeHelpTip( wrap ); 316 } ); 317 } 318 319 function toggleHelpTip( wrap ) { 320 var button = wrap.querySelector( '.srb-help-tip' ); 321 var popover = wrap.querySelector( '.srb-help-tip-popover' ); 322 if ( ! button || ! popover ) { 323 return; 324 } 325 326 if ( popover.hidden ) { 327 closeAllHelpTips( wrap ); 328 popover.hidden = false; 329 button.setAttribute( 'aria-expanded', 'true' ); 330 if ( popover.id ) { 331 button.setAttribute( 'aria-describedby', popover.id ); 332 } 333 return; 334 } 335 336 closeHelpTip( wrap ); 337 } 338 339 function initHelpTips() { 340 var wraps = document.querySelectorAll( '.srb-help-tip-wrap' ); 341 if ( ! wraps.length ) { 342 return; 343 } 344 345 wraps.forEach( function ( wrap ) { 346 var button = wrap.querySelector( '.srb-help-tip' ); 347 if ( ! button ) { 348 return; 349 } 350 351 button.addEventListener( 'click', function ( event ) { 352 event.preventDefault(); 353 event.stopPropagation(); 354 toggleHelpTip( wrap ); 355 } ); 356 } ); 357 358 document.addEventListener( 'click', function ( event ) { 359 var target = event.target; 360 if ( target instanceof Element && target.closest( '.srb-help-tip-wrap' ) ) { 361 return; 362 } 363 closeAllHelpTips(); 364 } ); 365 366 document.addEventListener( 'keydown', function ( event ) { 367 if ( event.key === 'Escape' ) { 368 closeAllHelpTips(); 369 } 370 } ); 371 } 372 235 373 function initCopyBookingLinkButton() { 236 374 var button = document.querySelector( '[data-srb-copy-booking-link]' ); … … 263 401 initColorValueSync(); 264 402 initButtonPreview(); 403 initHelpTips(); 265 404 initCopyBookingLinkButton(); 405 initRemindersPromoAnalytics(); 266 406 }() ); -
smsreminder-appointment-booking/trunk/includes/Admin/class-connection-actions.php
r3519533 r3548033 9 9 10 10 use SmsReminderBooking\Services\Connection_Service; 11 use SmsReminderBooking\Support\Reminders_Promo_Visibility; 11 12 12 13 defined( 'ABSPATH' ) || exit; … … 27 28 add_action( 'admin_post_sms_reminder_booking_connect', array( $this, 'handle_connect' ) ); 28 29 add_action( 'admin_post_sms_reminder_booking_disconnect', array( $this, 'handle_disconnect' ) ); 30 add_action( 'admin_post_sms_reminder_booking_dismiss_promo', array( $this, 'handle_dismiss_promo' ) ); 29 31 add_action( 'admin_post_sms_reminder_booking_callback', array( $this, 'handle_callback' ) ); 30 32 } … … 75 77 } 76 78 79 public function handle_dismiss_promo(): void { 80 if ( ! current_user_can( Reminders_Promo_Visibility::DISMISS_REQUIRED_CAPABILITY ) ) { 81 wp_die( esc_html__( 'You do not have permission to dismiss this notice.', 'smsreminder-appointment-booking' ), '', array( 'response' => 403 ) ); 82 } 83 check_admin_referer( Reminders_Promo_Visibility::DISMISS_NONCE_ACTION ); 84 85 $payload = Reminders_Promo_Visibility::dismiss_persist_payload( get_current_user_id() ); 86 if ( $payload !== null ) { 87 update_user_meta( $payload['user_id'], $payload['meta_key'], $payload['meta_value'] ); 88 $this->service->track_analytics_event( $payload['analytics_event'] ); 89 } 90 91 wp_safe_redirect( self::settings_url_with_query( array() ) ); 92 exit; 93 } 94 77 95 public function handle_callback(): void { 78 96 if ( ! current_user_can( 'manage_options' ) ) { -
smsreminder-appointment-booking/trunk/includes/Admin/class-settings-page.php
r3519533 r3548033 10 10 use SmsReminderBooking\Services\Connection_Service; 11 11 use SmsReminderBooking\Support\Book_Button_Defaults; 12 use SmsReminderBooking\Support\Reminders_Promo_Resolution; 13 use SmsReminderBooking\Support\Reminders_Promo_Visibility; 12 14 13 15 defined( 'ABSPATH' ) || exit; 14 16 15 17 require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-book-button-defaults.php'; 18 require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-reminders-promo-visibility.php'; 19 require_once SMS_REMINDER_BOOKING_PLUGIN_DIR . 'includes/Support/class-reminders-promo-resolution.php'; 16 20 17 21 final class Settings_Page { … … 22 26 private Connection_Service $service; 23 27 28 private bool $has_paid_subscription = false; 29 30 private bool $promo_visible = false; 31 24 32 public function __construct( Connection_Service $service ) { 25 33 $this->service = $service; 34 } 35 36 public function get_has_paid_subscription(): bool { 37 return $this->has_paid_subscription; 38 } 39 40 public function get_should_show_reminders_promo(): bool { 41 return $this->promo_visible; 26 42 } 27 43 … … 47 63 <?php 48 64 else : 65 $profile = Reminders_Promo_Visibility::needs_profile_for_promo( 66 $booking_settings, 67 $this->is_promo_dismissed() 68 ) 69 ? $this->service->fetch_profile() 70 : null; 71 72 if ( is_wp_error( $profile ) ) { 73 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 74 error_log( 75 sprintf( 76 'SMS Reminder Booking: profile fetch failed (%s); hiding reminders promo.', 77 $profile->get_error_message() 78 ) 79 ); 80 } 81 82 $resolution = Reminders_Promo_Resolution::resolve( 83 $booking_settings, 84 $profile ?? array(), 85 $this->is_promo_dismissed() 86 ); 87 $this->promo_visible = $resolution['promo_visible']; 88 $this->has_paid_subscription = $resolution['has_paid_subscription']; 89 49 90 $this->render_booking_sections( $booking_settings ); 91 if ( $this->promo_visible ) { 92 $this->render_reminders_promo_block(); 93 } 50 94 endif; 51 95 ?> … … 60 104 61 105 <div class="srb-card"> 62 <p><?php esc_html_e( 'Connect your WordPress site to SMS Reminder to enable booking features.', 'smsreminder-appointment-booking' ); ?></p> 106 <h2><?php esc_html_e( 'Enable bookings and reduce no-shows', 'smsreminder-appointment-booking' ); ?></h2> 107 <p><?php esc_html_e( 'Connect your WordPress site to SMS Reminder to enable instant client self-booking.', 'smsreminder-appointment-booking' ); ?></p> 63 108 64 109 <?php $this->render_connect_external_services_disclosure(); ?> … … 75 120 </a> 76 121 </p> 122 <p class="description"><?php esc_html_e( 'Send automated SMS reminders to clients (optional).', 'smsreminder-appointment-booking' ); ?></p> 77 123 </div> 78 124 <?php endif; ?> … … 99 145 $dashboard_url = $this->service->get_plugin_config()->get_frontend_dashboard_settings_url(); 100 146 $setup_booking_page_url = rtrim( $this->service->get_plugin_config()->get_frontend_site_url(), '/' ) . '/dashboard/settings?tab=6'; 101 $onboarding_completed = $this->booking_onboarding_completed( $booking_settings );147 $onboarding_completed = Reminders_Promo_Visibility::booking_onboarding_completed( $booking_settings ); 102 148 if ( ! $onboarding_completed ) { 103 149 $this->render_finish_setup_section( $setup_booking_page_url ); … … 189 235 <th scope="col"><?php esc_html_e( 'Buffer after', 'smsreminder-appointment-booking' ); ?></th> 190 236 <th scope="col"><?php esc_html_e( 'Minimum notice', 'smsreminder-appointment-booking' ); ?></th> 191 <th scope="col"><?php esc_html_e( 'Reminders', 'smsreminder-appointment-booking'); ?></th>237 <th scope="col"><?php $this->render_reminders_column_header(); ?></th> 192 238 <th scope="col"><?php esc_html_e( 'Rescheduling', 'smsreminder-appointment-booking' ); ?></th> 193 239 </tr> … … 284 330 </tbody> 285 331 </table> 332 </div> 333 <?php 334 } 335 336 private function render_reminders_promo_block(): void { 337 $enable_url = $this->service->get_plugin_config()->get_enable_reminders_url(); 338 $learn_more_url = $this->service->get_plugin_config()->get_marketing_learn_more_url(); 339 ?> 340 <div class="srb-card srb-reminders-promo" data-srb-reminders-promo> 341 <div class="srb-reminders-promo__header"> 342 <h2><?php esc_html_e( 'Reduce no-shows with SMS reminders', 'smsreminder-appointment-booking' ); ?></h2> 343 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" class="srb-reminders-promo__dismiss-form"> 344 <input type="hidden" name="action" value="sms_reminder_booking_dismiss_promo" /> 345 <?php wp_nonce_field( Reminders_Promo_Visibility::DISMISS_NONCE_ACTION ); ?> 346 <button 347 type="submit" 348 class="button-link srb-reminders-promo__dismiss" 349 data-srb-reminders-promo-dismiss 350 > 351 <?php esc_html_e( 'Dismiss', 'smsreminder-appointment-booking' ); ?> 352 </button> 353 </form> 354 </div> 355 <p class="srb-reminders-promo__body"> 356 <?php esc_html_e( 'Send automatic reminders before appointments so clients actually show up.', 'smsreminder-appointment-booking' ); ?> 357 </p> 358 <p class="srb-reminders-promo__actions srb-inline-actions"> 359 <a 360 class="button button-primary" 361 href="<?php echo esc_url( $enable_url ); ?>" 362 target="_blank" 363 rel="noopener noreferrer" 364 data-srb-reminders-promo-enable 365 > 366 <?php esc_html_e( 'Enable SMS reminders', 'smsreminder-appointment-booking' ); ?> 367 </a> 368 <a 369 href="<?php echo esc_url( $learn_more_url ); ?>" 370 target="_blank" 371 rel="noopener noreferrer" 372 data-srb-reminders-promo-learn-more 373 > 374 <?php esc_html_e( 'Learn more', 'smsreminder-appointment-booking' ); ?> 375 </a> 376 </p> 286 377 </div> 287 378 <?php … … 663 754 664 755 /** 665 * @param array {booking_onboarding:array<string,mixed>|null} $booking_settings666 */ 667 private function booking_onboarding_completed( array $booking_settings): bool {668 $ onboarding = $this->get_value( $booking_settings, 'booking_onboarding');669 if ( ! is_array( $onboarding )) {756 * @param array|\WP_Error $profile 757 */ 758 private function is_promo_dismissed(): bool { 759 $user_id = get_current_user_id(); 760 if ( $user_id <= 0 ) { 670 761 return false; 671 762 } 672 763 673 return true === $this->get_value( $onboarding, 'onboarding_completed' );764 return get_user_meta( $user_id, Reminders_Promo_Visibility::DISMISS_USER_META_KEY, true ) === '1'; 674 765 } 675 766 … … 739 830 } 740 831 832 private function render_reminders_column_header(): void { 833 $learn_more_url = $this->service->get_plugin_config()->get_marketing_learn_more_url(); 834 ?> 835 <span class="srb-reminders-column-header"> 836 <?php esc_html_e( 'Reminders', 'smsreminder-appointment-booking' ); ?> 837 <span class="srb-help-tip-wrap"> 838 <button 839 type="button" 840 class="srb-help-tip dashicons dashicons-info" 841 aria-expanded="false" 842 aria-controls="srb-reminders-column-tooltip" 843 > 844 <span class="screen-reader-text"> 845 <?php esc_html_e( 'More information about optional SMS reminders', 'smsreminder-appointment-booking' ); ?> 846 </span> 847 </button> 848 <div 849 id="srb-reminders-column-tooltip" 850 class="srb-help-tip-popover" 851 role="tooltip" 852 hidden 853 > 854 <p class="srb-help-tip-popover__title"> 855 <strong><?php esc_html_e( 'Optional SMS reminders', 'smsreminder-appointment-booking' ); ?></strong> 856 </p> 857 <p class="srb-help-tip-popover__body"> 858 <?php esc_html_e( 'Automatically send appointment reminders to clients by SMS before their booking.', 'smsreminder-appointment-booking' ); ?> 859 </p> 860 <p class="srb-help-tip-popover__action"> 861 <a 862 href="<?php echo esc_url( $learn_more_url ); ?>" 863 target="_blank" 864 rel="noopener noreferrer" 865 data-srb-reminders-column-tooltip-learn-more 866 > 867 <?php esc_html_e( 'Learn more', 'smsreminder-appointment-booking' ); ?> 868 </a> 869 </p> 870 </div> 871 </span> 872 </span> 873 <?php 874 } 875 741 876 private static function format_connected_at( string $iso ): string { 742 877 if ( $iso === '' ) { -
smsreminder-appointment-booking/trunk/includes/Services/class-connection-service.php
r3522716 r3548033 349 349 350 350 $result = $this->client->get_json( 'connected-apps/booking/settings', (string) $conn['token'] ); 351 return $this->parse_booking_settings_result( $result ); 352 } 353 354 /** 355 * Fetches connected user profile (subscription signals for promo visibility). 356 * 357 * @return array{user:array{id:string,email:string,billing_subscription_id:int|null,has_paid_subscription:bool,has_reminder_setup_history:bool}}|\WP_Error 358 */ 359 public function fetch_profile() { 360 $conn = $this->store->get_connection(); 361 if ( $conn === null || empty( $conn['token'] ) ) { 362 return new \WP_Error( 'srb_no_token', __( 'There is no saved connection token.', 'smsreminder-appointment-booking' ) ); 363 } 364 365 $result = $this->client->get_json( 'connected-apps/profile', (string) $conn['token'] ); 366 return $this->parse_profile_result( $result ); 367 } 368 369 public function track_analytics_event( string $event ): void { 370 $conn = $this->store->get_connection(); 371 if ( $conn === null || empty( $conn['token'] ) || $event === '' ) { 372 return; 373 } 374 375 $result = $this->client->post_json( 376 'connected-apps/analytics/track', 377 array( 'event' => $event ), 378 (string) $conn['token'] 379 ); 380 381 if ( ! $result['ok'] ) { 382 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log 383 error_log( 384 sprintf( 385 'SMS Reminder Booking: analytics track failed for %s (%s).', 386 $event, 387 $result['error_message'] ?? 'unknown error' 388 ) 389 ); 390 } 391 } 392 393 /** 394 * @param array{ok:bool, status:int, data?:mixed, error_message?:string} $result 395 * @return array{booking_url:string, booking_onboarding:array<string,mixed>|null, availability:array<string,mixed>, appointment_types:array<int,mixed>, upcoming_bookings:array<int,mixed>}|\WP_Error 396 */ 397 private function parse_booking_settings_result( array $result ) { 351 398 if ( ! $result['ok'] ) { 352 399 return new \WP_Error( … … 371 418 372 419 return array( 373 'booking_url' => isset( $data['booking_url'] ) ? (string) $data['booking_url'] : '',420 'booking_url' => isset( $data['booking_url'] ) ? (string) $data['booking_url'] : '', 374 421 'booking_onboarding' => ( isset( $data['booking_onboarding'] ) && is_array( $data['booking_onboarding'] ) ) ? $data['booking_onboarding'] : null, 375 'availability' => $data['availability'], 376 'appointment_types' => $data['appointment_types'], 377 'upcoming_bookings' => $data['upcoming_bookings'], 422 'availability' => $data['availability'], 423 'appointment_types' => $data['appointment_types'], 424 'upcoming_bookings' => $data['upcoming_bookings'], 425 ); 426 } 427 428 /** 429 * @param array{ok:bool, status:int, data?:mixed, error_message?:string} $result 430 * @return array{user:array{id:string,email:string,billing_subscription_id:int|null,has_paid_subscription:bool,has_reminder_setup_history:bool}}|\WP_Error 431 */ 432 private function parse_profile_result( array $result ) { 433 if ( ! $result['ok'] ) { 434 return new \WP_Error( 435 'srb_profile', 436 $result['error_message'] ?? __( 'Could not load profile from SMS Reminder.', 'smsreminder-appointment-booking' ) 437 ); 438 } 439 440 $data = $result['data'] ?? null; 441 if ( ! is_array( $data ) || ! isset( $data['user'] ) || ! is_array( $data['user'] ) ) { 442 return new \WP_Error( 'srb_profile_shape', __( 'Unexpected profile response.', 'smsreminder-appointment-booking' ) ); 443 } 444 445 $user = $data['user']; 446 447 return array( 448 'user' => array( 449 'id' => isset( $user['id'] ) ? (string) $user['id'] : '', 450 'email' => isset( $user['email'] ) ? (string) $user['email'] : '', 451 'billing_subscription_id' => isset( $user['billing_subscription_id'] ) && is_numeric( $user['billing_subscription_id'] ) 452 ? (int) $user['billing_subscription_id'] 453 : null, 454 'has_paid_subscription' => true === ( $user['has_paid_subscription'] ?? false ), 455 'has_reminder_setup_history' => true === ( $user['has_reminder_setup_history'] ?? false ), 456 ), 378 457 ); 379 458 } -
smsreminder-appointment-booking/trunk/includes/class-admin.php
r3519533 r3548033 91 91 'smsreminder-appointment-booking-admin-settings', 92 92 SMS_REMINDER_BOOKING_PLUGIN_URL . 'assets/admin-booking-settings.css', 93 array( ),93 array( 'dashicons' ), 94 94 $style_ver 95 ); 96 97 if ( ! current_user_can( 'manage_options' ) ) { 98 return; 99 } 100 101 $connection = $this->connection_service->get_store()->get_connection(); 102 $bearer_token = is_array( $connection ) && ! empty( $connection['token'] ) 103 ? (string) $connection['token'] 104 : ''; 105 106 wp_localize_script( 107 'smsreminder-appointment-booking-admin-settings', 108 'srbAdminConfig', 109 array( 110 'apiBase' => $this->plugin_config->get_api_base(), 111 'analyticsTrackPath' => 'connected-apps/analytics/track', 112 'bearerToken' => $bearer_token, 113 ) 95 114 ); 96 115 } -
smsreminder-appointment-booking/trunk/includes/class-plugin-config.php
r3519533 r3548033 93 93 } 94 94 95 public function get_enable_reminders_url(): string { 96 return add_query_arg( 97 array( 98 'source' => 'wordpress', 99 'utm_source' => 'wordpress-plugin', 100 'utm_medium' => 'admin', 101 'utm_campaign' => 'enable_reminders', 102 ), 103 $this->get_frontend_dashboard_settings_url() 104 ); 105 } 106 107 public function get_marketing_learn_more_url(): string { 108 return add_query_arg( 109 array( 110 'utm_source' => 'wordpress-plugin', 111 'utm_medium' => 'admin', 112 'utm_campaign' => 'learn_more', 113 ), 114 'https://www.smsreminder.app/' 115 ); 116 } 117 95 118 public function get_http_timeout(): int { 96 119 if ( $this->http_timeout_cache !== null ) { -
smsreminder-appointment-booking/trunk/readme.txt
r3522716 r3548033 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.2 7 Stable tag: 1. 0.17 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 137 137 == Changelog == 138 138 139 = 1.1.0 = 140 * Connection screen: updated copy to highlight free booking and optional SMS reminders before sign-up. 141 * Connected dashboard: added a dismissible SMS reminders promo block with a direct link to enable reminders in the SMS Reminder dashboard. 142 * Appointment types table: added an informational tooltip on the Reminders column explaining optional SMS reminders. 143 * Admin analytics: track promo block and tooltip impressions and CTA clicks for product funnel measurement. 144 139 145 = 1.0.1 = 140 146 * Connection flow: the redirect to the SMS Reminder web app now includes a `source` query parameter (`wordpress`) so new signups started from this plugin can be attributed correctly in product analytics. No change to stored tokens, callbacks, or booking behavior. … … 148 154 == Upgrade Notice == 149 155 156 = 1.1.0 = 157 Marketing and education updates for optional SMS reminders. No settings changes or reconnection required. 158 150 159 = 1.0.1 = 151 160 Maintenance release. Improves how the SMS Reminder service attributes new signups that start from this plugin. No settings changes or reconnection required. -
smsreminder-appointment-booking/trunk/smsreminder-appointment-booking.php
r3522716 r3548033 4 4 * Plugin URI: https://www.smsreminder.app/ 5 5 * Description: Let clients book appointments through Google Calendar and reduce no-shows with simple scheduling and SMS reminders. 6 * Version: 1. 0.16 * Version: 1.1.0 7 7 * Requires at least: 6.9 8 8 * Requires PHP: 8.2 … … 17 17 defined( 'ABSPATH' ) || exit; 18 18 19 define( 'SMS_REMINDER_BOOKING_VERSION', '1. 0.1' );19 define( 'SMS_REMINDER_BOOKING_VERSION', '1.1.0' ); 20 20 /** Increment when the custom DB schema changes (tables/columns); not the same as plugin semver. */ 21 21 define( 'SMS_REMINDER_BOOKING_DB_VERSION', 1 ); -
smsreminder-appointment-booking/trunk/uninstall.php
r3519533 r3548033 8 8 defined( 'WP_UNINSTALL_PLUGIN' ) || exit; 9 9 10 require_once plugin_dir_path( __FILE__ ) . 'includes/Support/class-reminders-promo-visibility.php'; 11 12 use SmsReminderBooking\Support\Reminders_Promo_Visibility; 13 10 14 delete_option( 'sms_reminder_booking_button_defaults' ); 11 15 delete_option( 'sms_reminder_booking_connection' ); … … 13 17 delete_transient( 'sms_reminder_booking_pending_auth' ); 14 18 foreach ( get_users( array( 'fields' => 'ID' ) ) as $sms_reminder_booking_user_id ) { 15 delete_transient( 'sms_reminder_booking_pending_auth_' . (int) $sms_reminder_booking_user_id ); 19 $user_id = (int) $sms_reminder_booking_user_id; 20 delete_transient( 'sms_reminder_booking_pending_auth_' . $user_id ); 21 delete_user_meta( $user_id, Reminders_Promo_Visibility::DISMISS_USER_META_KEY ); 16 22 } 17 23 delete_option( 'sms_reminder_booking_db_version' );
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)