| 1 |
<?php |
| 2 |
/** |
| 3 |
* Property Hive first-run onboarding. |
| 4 |
* |
| 5 |
* @author PropertyHive |
| 6 |
* @category Admin |
| 7 |
* @package PropertyHive/Admin |
| 8 |
* @version 1.0.0 |
| 9 |
*/ |
| 10 |
|
| 11 |
if ( ! defined( 'ABSPATH' ) ) { |
| 12 |
exit; |
| 13 |
} |
| 14 |
|
| 15 |
if ( ! class_exists( 'PH_Admin_Onboarding' ) ) : |
| 16 |
|
| 17 |
/** |
| 18 |
* PH_Admin_Onboarding class. |
| 19 |
*/ |
| 20 |
class PH_Admin_Onboarding { |
| 21 |
|
| 22 |
const OPTION_NAME = 'propertyhive_onboarding'; |
| 23 |
|
| 24 |
const NONCE_ACTION = 'propertyhive-onboarding'; |
| 25 |
|
| 26 |
const RESTART_NONCE_ACTION = 'propertyhive-restart-onboarding'; |
| 27 |
|
| 28 |
/** |
| 29 |
* Wizard step ids. |
| 30 |
* |
| 31 |
* @var array |
| 32 |
*/ |
| 33 |
private $steps = array( 'intro', 'departments', 'country', 'office', 'usage', 'license', 'demo-data', 'complete' ); |
| 34 |
|
| 35 |
/** |
| 36 |
* Constructor. |
| 37 |
*/ |
| 38 |
public function __construct() { |
| 39 |
add_action( 'admin_menu', array( $this, 'admin_dashboard_pages' ) ); |
| 40 |
add_action( 'admin_head', array( $this, 'hide_dashboard_page_menu_item' ) ); |
| 41 |
//add_action( 'admin_init', array( $this, 'maybe_restart_onboarding' ) ); |
| 42 |
add_action( 'load-dashboard_page_ph-onboarding', array( $this, 'remove_admin_toolbar_html_class' ) ); |
| 43 |
add_filter( 'propertyhive_screen_ids', array( $this, 'add_screen_id' ) ); |
| 44 |
add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); |
| 45 |
add_action( 'propertyhive_settings_start', array( $this, 'output_restart_onboarding_notice' ) ); |
| 46 |
|
| 47 |
add_action( 'wp_ajax_propertyhive_onboarding_save_step', array( $this, 'ajax_save_step' ) ); |
| 48 |
add_action( 'wp_ajax_propertyhive_onboarding_skip', array( $this, 'ajax_skip' ) ); |
| 49 |
add_action( 'wp_ajax_propertyhive_onboarding_track', array( $this, 'ajax_track' ) ); |
| 50 |
add_action( 'wp_ajax_propertyhive_onboarding_activate_license', array( $this, 'ajax_activate_license' ) ); |
| 51 |
} |
| 52 |
|
| 53 |
/** |
| 54 |
* Register the hidden onboarding page. |
| 55 |
*/ |
| 56 |
public function admin_dashboard_pages() { |
| 57 |
add_dashboard_page( |
| 58 |
__( 'Property Hive Setup', 'propertyhive' ), |
| 59 |
__( 'Property Hive Setup', 'propertyhive' ), |
| 60 |
'manage_options', |
| 61 |
'ph-onboarding', |
| 62 |
array( $this, 'output' ) |
| 63 |
); |
| 64 |
} |
| 65 |
|
| 66 |
/** |
| 67 |
* Hide the dashboard submenu item. |
| 68 |
*/ |
| 69 |
public function hide_dashboard_page_menu_item() { |
| 70 |
remove_submenu_page( 'index.php', 'ph-onboarding' ); |
| 71 |
} |
| 72 |
|
| 73 |
/** |
| 74 |
* Prepare the HTML element classes for the full-screen onboarding page. |
| 75 |
*/ |
| 76 |
public function remove_admin_toolbar_html_class() { |
| 77 |
ob_start( array( $this, 'filter_admin_html_class' ) ); |
| 78 |
} |
| 79 |
|
| 80 |
/** |
| 81 |
* Remove wp-toolbar from the admin HTML tag and add an onboarding class. |
| 82 |
* |
| 83 |
* @param string $buffer Page output. |
| 84 |
* @return string |
| 85 |
*/ |
| 86 |
public function filter_admin_html_class( $buffer ) { |
| 87 |
return preg_replace_callback( |
| 88 |
'/<html\s+class=(["\'])([^"\']*)\1([^>]*)>/i', |
| 89 |
array( $this, 'remove_wp_toolbar_from_html_tag' ), |
| 90 |
$buffer, |
| 91 |
1 |
| 92 |
); |
| 93 |
} |
| 94 |
|
| 95 |
/** |
| 96 |
* Strip wp-toolbar and add the onboarding class while preserving attributes. |
| 97 |
* |
| 98 |
* @param array $matches Regex matches. |
| 99 |
* @return string |
| 100 |
*/ |
| 101 |
private function remove_wp_toolbar_from_html_tag( $matches ) { |
| 102 |
$quote = $matches[1]; |
| 103 |
$classes = preg_split( '/\s+/', trim( $matches[2] ) ); |
| 104 |
$classes = array_filter( |
| 105 |
$classes, |
| 106 |
function( $class ) { |
| 107 |
return 'wp-toolbar' !== $class; |
| 108 |
} |
| 109 |
); |
| 110 |
|
| 111 |
$classes[] = 'propertyhive-onboarding-html'; |
| 112 |
$classes = array_unique( $classes ); |
| 113 |
|
| 114 |
return '<html class=' . $quote . esc_attr( implode( ' ', $classes ) ) . $quote . $matches[3] . '>'; |
| 115 |
} |
| 116 |
|
| 117 |
/** |
| 118 |
* Add onboarding screen to Property Hive admin screens. |
| 119 |
* |
| 120 |
* @param array $screen_ids Screen ids. |
| 121 |
* @return array |
| 122 |
*/ |
| 123 |
public function add_screen_id( $screen_ids ) { |
| 124 |
$screen_ids[] = 'dashboard_page_ph-onboarding'; |
| 125 |
return $screen_ids; |
| 126 |
} |
| 127 |
|
| 128 |
/** |
| 129 |
* Add body class on the full-screen onboarding page. |
| 130 |
* |
| 131 |
* @param string $classes Body classes. |
| 132 |
* @return string |
| 133 |
*/ |
| 134 |
public function admin_body_class( $classes ) { |
| 135 |
$screen = get_current_screen(); |
| 136 |
if ( $screen && 'dashboard_page_ph-onboarding' === $screen->id ) { |
| 137 |
$classes .= ' propertyhive-onboarding-screen'; |
| 138 |
} |
| 139 |
return $classes; |
| 140 |
} |
| 141 |
|
| 142 |
/** |
| 143 |
* Get onboarding state. |
| 144 |
* |
| 145 |
* @return array |
| 146 |
*/ |
| 147 |
public static function get_state() { |
| 148 |
$state = get_option( self::OPTION_NAME, array() ); |
| 149 |
if ( ! is_array( $state ) ) { |
| 150 |
$state = array(); |
| 151 |
} |
| 152 |
|
| 153 |
return wp_parse_args( |
| 154 |
$state, |
| 155 |
array( |
| 156 |
'status' => 'not_started', |
| 157 |
'last_step' => '', |
| 158 |
'departments' => array(), |
| 159 |
'country' => '', |
| 160 |
'office' => array(), |
| 161 |
'office_id' => 0, |
| 162 |
'usage' => array(), |
| 163 |
'usage_tracking' => true, |
| 164 |
'has_license_key' => 'no', |
| 165 |
'license_key_type' => 'pro', |
| 166 |
'license_activated' => false, |
| 167 |
'demo_data_imported' => false, |
| 168 |
'import_feature_enabled' => false, |
| 169 |
'completed_via' => '', |
| 170 |
'events' => array(), |
| 171 |
) |
| 172 |
); |
| 173 |
} |
| 174 |
|
| 175 |
/** |
| 176 |
* Save onboarding state. |
| 177 |
* |
| 178 |
* @param array $updates State updates. |
| 179 |
* @return array |
| 180 |
*/ |
| 181 |
private static function update_state( $updates ) { |
| 182 |
$state = self::get_state(); |
| 183 |
$state = array_merge( $state, $updates ); |
| 184 |
|
| 185 |
update_option( self::OPTION_NAME, $state, false ); |
| 186 |
|
| 187 |
return $state; |
| 188 |
} |
| 189 |
|
| 190 |
/** |
| 191 |
* Add a local event, and optionally send it remotely. |
| 192 |
* |
| 193 |
* @param string $event Event name. |
| 194 |
* @param array $data Event data. |
| 195 |
*/ |
| 196 |
public static function track_event( $event, $data = array() ) { |
| 197 |
$event = sanitize_key( $event ); |
| 198 |
if ( '' === $event ) { |
| 199 |
return; |
| 200 |
} |
| 201 |
|
| 202 |
$state = self::get_state(); |
| 203 |
$events = isset( $state['events'] ) && is_array( $state['events'] ) ? $state['events'] : array(); |
| 204 |
|
| 205 |
$events[] = array( |
| 206 |
'event' => $event, |
| 207 |
'data' => self::sanitize_event_data( $data ), |
| 208 |
'created_at' => time(), |
| 209 |
); |
| 210 |
|
| 211 |
if ( count( $events ) > 50 ) { |
| 212 |
$events = array_slice( $events, -50 ); |
| 213 |
} |
| 214 |
|
| 215 |
self::update_state( array( 'events' => $events ) ); |
| 216 |
|
| 217 |
$endpoint = apply_filters( 'propertyhive_onboarding_tracking_endpoint', '' ); |
| 218 |
if ( 'yes' !== get_option( 'propertyhive_data_sharing', 'no' ) || empty( $endpoint ) ) { |
| 219 |
return; |
| 220 |
} |
| 221 |
|
| 222 |
wp_remote_post( |
| 223 |
esc_url_raw( $endpoint ), |
| 224 |
array( |
| 225 |
'timeout' => 5, |
| 226 |
'body' => array( |
| 227 |
'event' => $event, |
| 228 |
'data' => wp_json_encode( self::sanitize_event_data( $data ) ), |
| 229 |
'site_url' => home_url(), |
| 230 |
'ph_version' => defined( 'PH_VERSION' ) ? PH_VERSION : '', |
| 231 |
), |
| 232 |
) |
| 233 |
); |
| 234 |
} |
| 235 |
|
| 236 |
/** |
| 237 |
* Sanitize arbitrary event data. |
| 238 |
* |
| 239 |
* @param array $data Event data. |
| 240 |
* @return array |
| 241 |
*/ |
| 242 |
private static function sanitize_event_data( $data ) { |
| 243 |
if ( ! is_array( $data ) ) { |
| 244 |
return array(); |
| 245 |
} |
| 246 |
|
| 247 |
$sanitized = array(); |
| 248 |
foreach ( $data as $key => $value ) { |
| 249 |
$key = sanitize_key( $key ); |
| 250 |
if ( is_array( $value ) ) { |
| 251 |
$sanitized[ $key ] = array_map( 'sanitize_text_field', wp_unslash( $value ) ); |
| 252 |
} else { |
| 253 |
$sanitized[ $key ] = sanitize_text_field( wp_unslash( $value ) ); |
| 254 |
} |
| 255 |
} |
| 256 |
|
| 257 |
return $sanitized; |
| 258 |
} |
| 259 |
|
| 260 |
/** |
| 261 |
* Save a wizard step via AJAX. |
| 262 |
*/ |
| 263 |
public function ajax_save_step() { |
| 264 |
$this->check_ajax_permissions(); |
| 265 |
|
| 266 |
$step = isset( $_POST['step'] ) ? sanitize_key( wp_unslash( $_POST['step'] ) ) : ''; |
| 267 |
if ( ! in_array( $step, $this->steps, true ) ) { |
| 268 |
wp_send_json_error( |
| 269 |
array( 'message' => __( 'Invalid setup step.', 'propertyhive' ) ), |
| 270 |
400 |
| 271 |
); |
| 272 |
} |
| 273 |
|
| 274 |
switch ( $step ) { |
| 275 |
case 'intro': |
| 276 |
$state = $this->save_intro_step(); |
| 277 |
break; |
| 278 |
case 'departments': |
| 279 |
$state = $this->save_departments_step(); |
| 280 |
break; |
| 281 |
case 'country': |
| 282 |
$state = $this->save_country_step(); |
| 283 |
break; |
| 284 |
case 'office': |
| 285 |
$state = $this->save_office_step(); |
| 286 |
break; |
| 287 |
case 'usage': |
| 288 |
$state = $this->save_usage_step(); |
| 289 |
break; |
| 290 |
case 'license': |
| 291 |
$state = $this->save_license_step(); |
| 292 |
break; |
| 293 |
case 'demo-data': |
| 294 |
$state = $this->save_demo_data_step(); |
| 295 |
break; |
| 296 |
case 'complete': |
| 297 |
$state = $this->save_complete_step(); |
| 298 |
break; |
| 299 |
} |
| 300 |
|
| 301 |
self::track_event( 'step_saved', array( 'step' => $step ) ); |
| 302 |
|
| 303 |
wp_send_json_success( $state ); |
| 304 |
} |
| 305 |
|
| 306 |
/** |
| 307 |
* Skip the wizard. |
| 308 |
*/ |
| 309 |
public function ajax_skip() { |
| 310 |
$this->check_ajax_permissions(); |
| 311 |
|
| 312 |
$step = isset( $_POST['step'] ) ? sanitize_key( wp_unslash( $_POST['step'] ) ) : ''; |
| 313 |
|
| 314 |
$updates = array( |
| 315 |
'status' => 'skipped', |
| 316 |
'last_step' => $step, |
| 317 |
'skipped_at' => time(), |
| 318 |
); |
| 319 |
|
| 320 |
if ( isset( $_POST['usage_tracking'] ) ) { |
| 321 |
$usage_tracking = 'yes' === sanitize_text_field( wp_unslash( $_POST['usage_tracking'] ) ); |
| 322 |
|
| 323 |
update_option( 'propertyhive_data_sharing', $usage_tracking ? 'yes' : 'no' ); |
| 324 |
|
| 325 |
$updates['usage_tracking'] = $usage_tracking; |
| 326 |
} |
| 327 |
|
| 328 |
$state = self::update_state( $updates ); |
| 329 |
|
| 330 |
self::track_event( 'skipped', array( 'step' => $step ) ); |
| 331 |
|
| 332 |
wp_send_json_success( |
| 333 |
array( |
| 334 |
'state' => $state, |
| 335 |
'redirect_url' => admin_url( 'admin.php?page=ph-settings' ), |
| 336 |
) |
| 337 |
); |
| 338 |
} |
| 339 |
|
| 340 |
/** |
| 341 |
* Track a view-only event. |
| 342 |
*/ |
| 343 |
public function ajax_track() { |
| 344 |
$this->check_ajax_permissions(); |
| 345 |
|
| 346 |
$event = isset( $_POST['event'] ) ? sanitize_key( wp_unslash( $_POST['event'] ) ) : ''; |
| 347 |
$step = isset( $_POST['step'] ) ? sanitize_key( wp_unslash( $_POST['step'] ) ) : ''; |
| 348 |
|
| 349 |
if ( '' !== $event ) { |
| 350 |
self::track_event( $event, array( 'step' => $step ) ); |
| 351 |
} |
| 352 |
|
| 353 |
wp_send_json_success(); |
| 354 |
} |
| 355 |
|
| 356 |
/** |
| 357 |
* Activate a license key from the onboarding wizard. |
| 358 |
*/ |
| 359 |
public function ajax_activate_license() { |
| 360 |
$this->check_ajax_permissions(); |
| 361 |
|
| 362 |
$license_key_type = isset( $_POST['license_key_type'] ) ? sanitize_key( wp_unslash( $_POST['license_key_type'] ) ) : ''; |
| 363 |
if ( ! in_array( $license_key_type, array( 'pro', 'old' ), true ) ) { |
| 364 |
wp_send_json_error( |
| 365 |
array( 'message' => __( 'Please choose a valid license type.', 'propertyhive' ) ), |
| 366 |
400 |
| 367 |
); |
| 368 |
} |
| 369 |
|
| 370 |
$license_key = isset( $_POST['license_key'] ) ? sanitize_text_field( ph_clean( wp_unslash( $_POST['license_key'] ) ) ) : ''; |
| 371 |
if ( '' === $license_key ) { |
| 372 |
wp_send_json_error( |
| 373 |
array( 'message' => __( 'Please enter your license key.', 'propertyhive' ) ), |
| 374 |
400 |
| 375 |
); |
| 376 |
} |
| 377 |
|
| 378 |
$snapshot = $this->snapshot_license_state(); |
| 379 |
|
| 380 |
update_option( 'missing_invalid_expired_license_key_notice_dismissed', '' ); |
| 381 |
|
| 382 |
if ( 'pro' === $license_key_type ) { |
| 383 |
$result = $this->activate_pro_license_for_onboarding( $license_key ); |
| 384 |
} else { |
| 385 |
$result = $this->activate_old_license_for_onboarding( $license_key ); |
| 386 |
} |
| 387 |
|
| 388 |
if ( empty( $result['activated'] ) ) { |
| 389 |
$this->restore_license_state( $snapshot ); |
| 390 |
} |
| 391 |
|
| 392 |
self::update_state( |
| 393 |
array( |
| 394 |
'has_license_key' => 'yes', |
| 395 |
'license_key_type' => $license_key_type, |
| 396 |
'license_activated' => ! empty( $result['activated'] ), |
| 397 |
'license_activated_at' => ! empty( $result['activated'] ) ? time() : 0, |
| 398 |
) |
| 399 |
); |
| 400 |
|
| 401 |
self::track_event( |
| 402 |
! empty( $result['activated'] ) ? 'license_activated' : 'license_activation_failed', |
| 403 |
array( 'license_type' => $license_key_type ) |
| 404 |
); |
| 405 |
|
| 406 |
wp_send_json_success( |
| 407 |
array( |
| 408 |
'activated' => ! empty( $result['activated'] ), |
| 409 |
'license_type' => $license_key_type, |
| 410 |
'summary' => isset( $result['summary'] ) ? $result['summary'] : '', |
| 411 |
'message' => isset( $result['message'] ) ? $result['message'] : '', |
| 412 |
'renew_url' => isset( $result['renew_url'] ) ? $result['renew_url'] : '', |
| 413 |
) |
| 414 |
); |
| 415 |
} |
| 416 |
|
| 417 |
/** |
| 418 |
* Activate a Pro license key and confirm it with a fresh status check. |
| 419 |
* |
| 420 |
* @param string $license_key License key. |
| 421 |
* @return array |
| 422 |
*/ |
| 423 |
private function activate_pro_license_for_onboarding( $license_key ) { |
| 424 |
$this->clear_pro_license_cache_surfaces(); |
| 425 |
|
| 426 |
update_option( 'propertyhive_license_type', 'pro' ); |
| 427 |
update_option( 'propertyhive_pro_license_key', $license_key ); |
| 428 |
|
| 429 |
$activation = PH()->license->activate_pro_license_key(); |
| 430 |
if ( ! isset( $activation['success'] ) || true !== $activation['success'] ) { |
| 431 |
return array( |
| 432 |
'activated' => false, |
| 433 |
'message' => ! empty( $activation['error'] ) ? $activation['error'] : __( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost.", 'propertyhive' ), |
| 434 |
); |
| 435 |
} |
| 436 |
|
| 437 |
if ( ! PH()->license->is_valid_pro_license_key( true ) ) { |
| 438 |
$license = PH()->license->get_current_pro_license(); |
| 439 |
|
| 440 |
return array( |
| 441 |
'activated' => false, |
| 442 |
'message' => ! empty( $license['error'] ) ? $license['error'] : __( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost.", 'propertyhive' ), |
| 443 |
); |
| 444 |
} |
| 445 |
|
| 446 |
return array( |
| 447 |
'activated' => true, |
| 448 |
'summary' => '', |
| 449 |
); |
| 450 |
} |
| 451 |
|
| 452 |
/** |
| 453 |
* Activate an old-style license key. |
| 454 |
* |
| 455 |
* @param string $license_key License key. |
| 456 |
* @return array |
| 457 |
*/ |
| 458 |
private function activate_old_license_for_onboarding( $license_key ) { |
| 459 |
update_option( 'propertyhive_license_type', 'old' ); |
| 460 |
update_option( 'propertyhive_license_key', $license_key ); |
| 461 |
|
| 462 |
PH()->license->ph_check_licenses( true ); |
| 463 |
|
| 464 |
$license = PH()->license->get_current_license(); |
| 465 |
$error = get_option( 'propertyhive_license_key_error', '' ); |
| 466 |
|
| 467 |
if ( '' !== $error ) { |
| 468 |
return array( |
| 469 |
'activated' => false, |
| 470 |
'message' => sprintf( |
| 471 |
/* translators: %s: license service error detail. */ |
| 472 |
__( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost. %s", 'propertyhive' ), |
| 473 |
$error |
| 474 |
), |
| 475 |
); |
| 476 |
} |
| 477 |
|
| 478 |
if ( empty( $license ) || ! is_array( $license ) || ! isset( $license['active'] ) || '1' !== (string) $license['active'] ) { |
| 479 |
return array( |
| 480 |
'activated' => false, |
| 481 |
'message' => isset( $license['active'] ) && '1' !== (string) $license['active'] ? __( 'Your Property Hive license key is inactive. You can keep going and add it later - nothing is lost.', 'propertyhive' ) : __( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost.", 'propertyhive' ), |
| 482 |
); |
| 483 |
} |
| 484 |
|
| 485 |
if ( empty( $license['expires_at'] ) || ( strtotime( $license['expires_at'] ) + DAY_IN_SECONDS ) <= time() ) { |
| 486 |
$expires_at = ! empty( $license['expires_at'] ) ? date_i18n( 'jS F Y', strtotime( $license['expires_at'] ) ) : ''; |
| 487 |
|
| 488 |
return array( |
| 489 |
'activated' => false, |
| 490 |
'message' => '' !== $expires_at ? sprintf( |
| 491 |
/* translators: %s: license expiry date. */ |
| 492 |
__( 'License expired on %s.', 'propertyhive' ), |
| 493 |
$expires_at |
| 494 |
) : __( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost.", 'propertyhive' ), |
| 495 |
'renew_url' => 'https://wp-property-hive.com/license-key/', |
| 496 |
); |
| 497 |
} |
| 498 |
|
| 499 |
return array( |
| 500 |
'activated' => true, |
| 501 |
'summary' => date_i18n( 'jS F Y', strtotime( $license['expires_at'] ) ), |
| 502 |
); |
| 503 |
} |
| 504 |
|
| 505 |
/** |
| 506 |
* Check whether the onboarding restart tool should be available. |
| 507 |
* |
| 508 |
* @return bool |
| 509 |
*/ |
| 510 |
public static function can_restart_onboarding() { |
| 511 |
return in_array( self::get_environment_type(), array( 'local', 'development', 'staging' ), true ); |
| 512 |
} |
| 513 |
|
| 514 |
/** |
| 515 |
* Get the current WordPress environment type. |
| 516 |
* |
| 517 |
* @return string |
| 518 |
*/ |
| 519 |
private static function get_environment_type() { |
| 520 |
if ( function_exists( 'wp_get_environment_type' ) ) { |
| 521 |
return wp_get_environment_type(); |
| 522 |
} |
| 523 |
|
| 524 |
return 'production'; |
| 525 |
} |
| 526 |
|
| 527 |
/** |
| 528 |
* Restart the onboarding wizard from a gated admin URL. |
| 529 |
*/ |
| 530 |
public function maybe_restart_onboarding() { |
| 531 |
if ( empty( $_GET['propertyhive_restart_onboarding'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 532 |
return; |
| 533 |
} |
| 534 |
|
| 535 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 536 |
wp_die( esc_html__( 'You do not have permission to restart setup.', 'propertyhive' ) ); |
| 537 |
} |
| 538 |
|
| 539 |
check_admin_referer( self::RESTART_NONCE_ACTION ); |
| 540 |
|
| 541 |
if ( ! self::can_restart_onboarding() ) { |
| 542 |
wp_die( esc_html__( 'The setup wizard can only be restarted on local, development or staging sites.', 'propertyhive' ) ); |
| 543 |
} |
| 544 |
|
| 545 |
delete_option( self::OPTION_NAME ); |
| 546 |
self::track_event( 'restarted', array( 'environment_type' => self::get_environment_type() ) ); |
| 547 |
|
| 548 |
wp_safe_redirect( admin_url( 'index.php?page=ph-onboarding' ) ); |
| 549 |
exit; |
| 550 |
} |
| 551 |
|
| 552 |
/** |
| 553 |
* Output a development-only setup restart prompt on the settings page. |
| 554 |
*/ |
| 555 |
public function output_restart_onboarding_notice() { |
| 556 |
if ( ! current_user_can( 'manage_options' ) || ! self::can_restart_onboarding() ) { |
| 557 |
return; |
| 558 |
} |
| 559 |
|
| 560 |
?> |
| 561 |
<div class="notice notice-info inline"> |
| 562 |
<p> |
| 563 |
<strong><?php esc_html_e( 'Development setup tools', 'propertyhive' ); ?></strong> |
| 564 |
</p> |
| 565 |
<p> |
| 566 |
<?php |
| 567 |
printf( |
| 568 |
/* translators: %s: WordPress environment type. */ |
| 569 |
esc_html__( 'This site is marked as %s, so administrators can restart the Property Hive setup wizard for testing.', 'propertyhive' ), |
| 570 |
'<code>' . esc_html( self::get_environment_type() ) . '</code>' |
| 571 |
); |
| 572 |
?> |
| 573 |
</p> |
| 574 |
<p> |
| 575 |
<a class="button" href="<?php echo esc_url( $this->get_restart_onboarding_url() ); ?>"><?php esc_html_e( 'Restart setup wizard', 'propertyhive' ); ?></a> |
| 576 |
</p> |
| 577 |
</div> |
| 578 |
<?php |
| 579 |
} |
| 580 |
|
| 581 |
/** |
| 582 |
* Get the onboarding restart URL. |
| 583 |
* |
| 584 |
* @return string |
| 585 |
*/ |
| 586 |
private function get_restart_onboarding_url() { |
| 587 |
return wp_nonce_url( |
| 588 |
add_query_arg( 'propertyhive_restart_onboarding', '1', admin_url( 'admin.php?page=ph-settings' ) ), |
| 589 |
self::RESTART_NONCE_ACTION |
| 590 |
); |
| 591 |
} |
| 592 |
|
| 593 |
/** |
| 594 |
* Validate AJAX permissions. |
| 595 |
*/ |
| 596 |
private function check_ajax_permissions() { |
| 597 |
check_ajax_referer( self::NONCE_ACTION, 'security' ); |
| 598 |
|
| 599 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 600 |
wp_send_json_error( |
| 601 |
array( 'message' => __( 'You do not have permission to run setup.', 'propertyhive' ) ), |
| 602 |
403 |
| 603 |
); |
| 604 |
} |
| 605 |
} |
| 606 |
|
| 607 |
/** |
| 608 |
* Return a validation error response. |
| 609 |
* |
| 610 |
* @param string $message Error summary. |
| 611 |
* @param array $errors Field errors. |
| 612 |
*/ |
| 613 |
private function send_validation_error( $message, $errors = array() ) { |
| 614 |
wp_send_json_error( |
| 615 |
array( |
| 616 |
'message' => $message, |
| 617 |
'errors' => $errors, |
| 618 |
), |
| 619 |
400 |
| 620 |
); |
| 621 |
} |
| 622 |
|
| 623 |
/** |
| 624 |
* Get the first validation error message. |
| 625 |
* |
| 626 |
* @param array $errors Field errors. |
| 627 |
* @param string $fallback Fallback message. |
| 628 |
* @return string |
| 629 |
*/ |
| 630 |
private function get_first_validation_error_message( $errors, $fallback ) { |
| 631 |
foreach ( $errors as $message ) { |
| 632 |
if ( is_string( $message ) && '' !== $message ) { |
| 633 |
return $message; |
| 634 |
} |
| 635 |
} |
| 636 |
|
| 637 |
return $fallback; |
| 638 |
} |
| 639 |
|
| 640 |
/** |
| 641 |
* Snapshot license options and transients so failed attempts can roll back. |
| 642 |
* |
| 643 |
* @return array |
| 644 |
*/ |
| 645 |
private function snapshot_license_state() { |
| 646 |
$option_names = array( |
| 647 |
'propertyhive_license_type', |
| 648 |
'propertyhive_pro_license_key', |
| 649 |
'propertyhive_license_key', |
| 650 |
'propertyhive_license_key_details', |
| 651 |
'propertyhive_license_key_error', |
| 652 |
'propertyhive_pro_license_key_status', |
| 653 |
'propertyhive_pro_license_key_last_checked', |
| 654 |
'ph_pro_last_known_license_product_id_and_package', |
| 655 |
'propertyhive_pro_instance_id', |
| 656 |
); |
| 657 |
|
| 658 |
$transient_names = array( |
| 659 |
'ph_pro_license_status', |
| 660 |
'ph_pro_license_product_id_and_package', |
| 661 |
); |
| 662 |
|
| 663 |
$snapshot = array( |
| 664 |
'options' => array(), |
| 665 |
'transients' => array(), |
| 666 |
); |
| 667 |
|
| 668 |
foreach ( $option_names as $option_name ) { |
| 669 |
$snapshot['options'][ $option_name ] = $this->snapshot_option( $option_name ); |
| 670 |
} |
| 671 |
|
| 672 |
foreach ( $transient_names as $transient_name ) { |
| 673 |
$snapshot['transients'][ $transient_name ] = array( |
| 674 |
'value' => $this->snapshot_option( '_transient_' . $transient_name ), |
| 675 |
'timeout' => $this->snapshot_option( '_transient_timeout_' . $transient_name ), |
| 676 |
); |
| 677 |
} |
| 678 |
|
| 679 |
return $snapshot; |
| 680 |
} |
| 681 |
|
| 682 |
/** |
| 683 |
* Snapshot one option while retaining whether it existed. |
| 684 |
* |
| 685 |
* @param string $option_name Option name. |
| 686 |
* @return array |
| 687 |
*/ |
| 688 |
private function snapshot_option( $option_name ) { |
| 689 |
$missing = '__propertyhive_onboarding_missing_option__'; |
| 690 |
$value = get_option( $option_name, $missing ); |
| 691 |
|
| 692 |
return array( |
| 693 |
'exists' => $missing !== $value, |
| 694 |
'value' => $value, |
| 695 |
); |
| 696 |
} |
| 697 |
|
| 698 |
/** |
| 699 |
* Restore a previously snapshotted option. |
| 700 |
* |
| 701 |
* @param string $option_name Option name. |
| 702 |
* @param array $snapshot Snapshot. |
| 703 |
*/ |
| 704 |
private function restore_option_snapshot( $option_name, $snapshot ) { |
| 705 |
if ( empty( $snapshot['exists'] ) ) { |
| 706 |
delete_option( $option_name ); |
| 707 |
return; |
| 708 |
} |
| 709 |
|
| 710 |
update_option( $option_name, $snapshot['value'] ); |
| 711 |
} |
| 712 |
|
| 713 |
/** |
| 714 |
* Restore license options and transients after a failed activation. |
| 715 |
* |
| 716 |
* @param array $snapshot Snapshot. |
| 717 |
*/ |
| 718 |
private function restore_license_state( $snapshot ) { |
| 719 |
if ( ! empty( $snapshot['options'] ) && is_array( $snapshot['options'] ) ) { |
| 720 |
foreach ( $snapshot['options'] as $option_name => $option_snapshot ) { |
| 721 |
$this->restore_option_snapshot( $option_name, $option_snapshot ); |
| 722 |
} |
| 723 |
} |
| 724 |
|
| 725 |
if ( ! empty( $snapshot['transients'] ) && is_array( $snapshot['transients'] ) ) { |
| 726 |
foreach ( $snapshot['transients'] as $transient_name => $transient_snapshot ) { |
| 727 |
$this->restore_option_snapshot( '_transient_' . $transient_name, $transient_snapshot['value'] ); |
| 728 |
$this->restore_option_snapshot( '_transient_timeout_' . $transient_name, $transient_snapshot['timeout'] ); |
| 729 |
} |
| 730 |
} |
| 731 |
} |
| 732 |
|
| 733 |
/** |
| 734 |
* Clear all Pro license cache surfaces before validating a submitted key. |
| 735 |
*/ |
| 736 |
private function clear_pro_license_cache_surfaces() { |
| 737 |
delete_transient( 'ph_pro_license_status' ); |
| 738 |
delete_transient( 'ph_pro_license_product_id_and_package' ); |
| 739 |
delete_option( 'propertyhive_pro_license_key_status' ); |
| 740 |
delete_option( 'propertyhive_pro_license_key_last_checked' ); |
| 741 |
delete_option( 'ph_pro_last_known_license_product_id_and_package' ); |
| 742 |
} |
| 743 |
|
| 744 |
/** |
| 745 |
* Save intro step. |
| 746 |
* |
| 747 |
* @return array |
| 748 |
*/ |
| 749 |
private function save_intro_step() { |
| 750 |
$usage_tracking = isset( $_POST['usage_tracking'] ) && 'yes' === sanitize_text_field( wp_unslash( $_POST['usage_tracking'] ) ); |
| 751 |
|
| 752 |
update_option( 'propertyhive_data_sharing', $usage_tracking ? 'yes' : 'no' ); |
| 753 |
|
| 754 |
return self::update_state( |
| 755 |
array( |
| 756 |
'status' => 'in_progress', |
| 757 |
'last_step' => 'intro', |
| 758 |
'started_at' => $this->get_started_at(), |
| 759 |
'usage_tracking' => $usage_tracking, |
| 760 |
) |
| 761 |
); |
| 762 |
} |
| 763 |
|
| 764 |
/** |
| 765 |
* Save departments. |
| 766 |
* |
| 767 |
* @return array |
| 768 |
*/ |
| 769 |
private function save_departments_step() { |
| 770 |
$departments = isset( $_POST['departments'] ) && is_array( $_POST['departments'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['departments'] ) ) : array(); |
| 771 |
$allowed = array( 'residential-sales', 'residential-lettings', 'commercial' ); |
| 772 |
$departments = array_values( array_intersect( $allowed, $departments ) ); |
| 773 |
|
| 774 |
if ( empty( $departments ) ) { |
| 775 |
$this->send_validation_error( |
| 776 |
__( 'Please choose at least one property sector.', 'propertyhive' ), |
| 777 |
array( |
| 778 |
'departments' => __( 'Please choose at least one property sector.', 'propertyhive' ), |
| 779 |
) |
| 780 |
); |
| 781 |
} |
| 782 |
|
| 783 |
update_option( 'propertyhive_active_departments_sales', in_array( 'residential-sales', $departments, true ) ? 'yes' : 'no' ); |
| 784 |
update_option( 'propertyhive_active_departments_lettings', in_array( 'residential-lettings', $departments, true ) ? 'yes' : 'no' ); |
| 785 |
update_option( 'propertyhive_active_departments_commercial', in_array( 'commercial', $departments, true ) ? 'yes' : 'no' ); |
| 786 |
update_option( 'propertyhive_primary_department', reset( $departments ) ); |
| 787 |
|
| 788 |
return self::update_state( |
| 789 |
array( |
| 790 |
'status' => 'in_progress', |
| 791 |
'last_step' => 'departments', |
| 792 |
'departments' => $departments, |
| 793 |
'started_at' => $this->get_started_at(), |
| 794 |
) |
| 795 |
); |
| 796 |
} |
| 797 |
|
| 798 |
/** |
| 799 |
* Save country. |
| 800 |
* |
| 801 |
* @return array |
| 802 |
*/ |
| 803 |
private function save_country_step() { |
| 804 |
$country = isset( $_POST['country'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_POST['country'] ) ) ) : ''; |
| 805 |
|
| 806 |
$countries = $this->get_countries(); |
| 807 |
if ( ! isset( $countries[ $country ] ) ) { |
| 808 |
$this->send_validation_error( |
| 809 |
__( 'Please choose a valid country.', 'propertyhive' ), |
| 810 |
array( |
| 811 |
'country' => __( 'Please choose a valid country.', 'propertyhive' ), |
| 812 |
) |
| 813 |
); |
| 814 |
} |
| 815 |
|
| 816 |
update_option( 'propertyhive_default_country', $country ); |
| 817 |
update_option( 'propertyhive_countries', array( $country ) ); |
| 818 |
|
| 819 |
if ( ! empty( $countries[ $country ]['currency_code'] ) ) { |
| 820 |
update_option( 'propertyhive_search_form_currency', sanitize_text_field( $countries[ $country ]['currency_code'] ) ); |
| 821 |
} |
| 822 |
|
| 823 |
return self::update_state( |
| 824 |
array( |
| 825 |
'status' => 'in_progress', |
| 826 |
'last_step' => 'country', |
| 827 |
'country' => $country, |
| 828 |
) |
| 829 |
); |
| 830 |
} |
| 831 |
|
| 832 |
/** |
| 833 |
* Save primary office details. |
| 834 |
* |
| 835 |
* @return array |
| 836 |
*/ |
| 837 |
private function save_office_step() { |
| 838 |
$office = $this->sanitize_office_payload(); |
| 839 |
$errors = $this->validate_office_payload( $office ); |
| 840 |
|
| 841 |
if ( ! empty( $errors ) ) { |
| 842 |
$this->send_validation_error( |
| 843 |
$this->get_first_validation_error_message( $errors, __( 'Please check your office details.', 'propertyhive' ) ), |
| 844 |
$errors |
| 845 |
); |
| 846 |
} |
| 847 |
|
| 848 |
$office['email_address'] = sanitize_email( $office['email_address'] ); |
| 849 |
|
| 850 |
$office_id = $this->get_primary_office_id(); |
| 851 |
if ( ! $office_id ) { |
| 852 |
$office_id = wp_insert_post( |
| 853 |
array( |
| 854 |
'post_title' => '' !== $office['name'] ? $office['name'] : __( 'My Office', 'propertyhive' ), |
| 855 |
'post_content' => '', |
| 856 |
'post_status' => 'publish', |
| 857 |
'post_type' => 'office', |
| 858 |
), |
| 859 |
true |
| 860 |
); |
| 861 |
|
| 862 |
if ( is_wp_error( $office_id ) || ! $office_id ) { |
| 863 |
wp_send_json_error( |
| 864 |
array( 'message' => __( 'Office details could not be saved.', 'propertyhive' ) ), |
| 865 |
500 |
| 866 |
); |
| 867 |
} |
| 868 |
} |
| 869 |
|
| 870 |
update_post_meta( $office_id, 'primary', '1' ); |
| 871 |
|
| 872 |
if ( '' !== $office['name'] && get_the_title( $office_id ) !== $office['name'] ) { |
| 873 |
wp_update_post( |
| 874 |
array( |
| 875 |
'ID' => $office_id, |
| 876 |
'post_title' => $office['name'], |
| 877 |
) |
| 878 |
); |
| 879 |
} |
| 880 |
|
| 881 |
update_post_meta( $office_id, '_office_address_1', $office['address_1'] ); |
| 882 |
update_post_meta( $office_id, '_office_address_2', $office['address_2'] ); |
| 883 |
update_post_meta( $office_id, '_office_address_3', $office['address_3'] ); |
| 884 |
update_post_meta( $office_id, '_office_address_4', $office['address_4'] ); |
| 885 |
update_post_meta( $office_id, '_office_address_postcode', $office['postcode'] ); |
| 886 |
|
| 887 |
foreach ( $this->get_office_contact_departments() as $department ) { |
| 888 |
$department_key = str_replace( 'residential-', '', $department ); |
| 889 |
update_post_meta( $office_id, '_office_telephone_number_' . $department_key, $office['telephone_number'] ); |
| 890 |
update_post_meta( $office_id, '_office_email_address_' . $department_key, $office['email_address'] ); |
| 891 |
} |
| 892 |
|
| 893 |
return self::update_state( |
| 894 |
array( |
| 895 |
'status' => 'in_progress', |
| 896 |
'last_step' => 'office', |
| 897 |
'office' => $office, |
| 898 |
'office_id' => absint( $office_id ), |
| 899 |
) |
| 900 |
); |
| 901 |
} |
| 902 |
|
| 903 |
/** |
| 904 |
* Save intended usage. |
| 905 |
* |
| 906 |
* @return array |
| 907 |
*/ |
| 908 |
private function save_usage_step() { |
| 909 |
$usage = isset( $_POST['usage'] ) && is_array( $_POST['usage'] ) ? array_map( 'sanitize_key', wp_unslash( $_POST['usage'] ) ) : array(); |
| 910 |
$usage = array_values( array_intersect( array( 'import_properties', 'crm', 'portal_uploads', 'not_sure' ), $usage ) ); |
| 911 |
|
| 912 |
if ( empty( $usage ) ) { |
| 913 |
$this->send_validation_error( |
| 914 |
__( 'Please choose how you will use Property Hive.', 'propertyhive' ), |
| 915 |
array( |
| 916 |
'usage' => __( 'Please choose how you will use Property Hive.', 'propertyhive' ), |
| 917 |
) |
| 918 |
); |
| 919 |
} |
| 920 |
|
| 921 |
$crm_enabled = in_array( 'crm', $usage, true ); |
| 922 |
$disabled = $crm_enabled ? 'no' : 'yes'; |
| 923 |
|
| 924 |
update_option( 'propertyhive_module_disabled_contacts', $disabled ); |
| 925 |
update_option( 'propertyhive_module_disabled_appraisals', $disabled ); |
| 926 |
update_option( 'propertyhive_module_disabled_viewings', $disabled ); |
| 927 |
update_option( 'propertyhive_module_disabled_offers_sales', $disabled ); |
| 928 |
update_option( 'propertyhive_module_disabled_tenancies', $disabled ); |
| 929 |
|
| 930 |
return self::update_state( |
| 931 |
array( |
| 932 |
'status' => 'in_progress', |
| 933 |
'last_step' => 'usage', |
| 934 |
'usage' => $usage, |
| 935 |
) |
| 936 |
); |
| 937 |
} |
| 938 |
|
| 939 |
/** |
| 940 |
* Save license step choices. |
| 941 |
* |
| 942 |
* @return array |
| 943 |
*/ |
| 944 |
private function save_license_step() { |
| 945 |
$has_license_key = isset( $_POST['has_license_key'] ) ? sanitize_key( wp_unslash( $_POST['has_license_key'] ) ) : 'no'; |
| 946 |
$license_key_type = isset( $_POST['license_key_type'] ) ? sanitize_key( wp_unslash( $_POST['license_key_type'] ) ) : 'pro'; |
| 947 |
|
| 948 |
if ( ! in_array( $has_license_key, array( 'yes', 'no' ), true ) ) { |
| 949 |
$has_license_key = 'no'; |
| 950 |
} |
| 951 |
|
| 952 |
if ( ! in_array( $license_key_type, array( 'pro', 'old' ), true ) ) { |
| 953 |
$license_key_type = 'pro'; |
| 954 |
} |
| 955 |
|
| 956 |
return self::update_state( |
| 957 |
array( |
| 958 |
'status' => 'in_progress', |
| 959 |
'last_step' => 'license', |
| 960 |
'has_license_key' => $has_license_key, |
| 961 |
'license_key_type' => $license_key_type, |
| 962 |
) |
| 963 |
); |
| 964 |
} |
| 965 |
|
| 966 |
/** |
| 967 |
* Save demo-data result. |
| 968 |
* |
| 969 |
* @return array |
| 970 |
*/ |
| 971 |
private function save_demo_data_step() { |
| 972 |
$demo_choice = isset( $_POST['demo_data_choice'] ) ? sanitize_key( wp_unslash( $_POST['demo_data_choice'] ) ) : ''; |
| 973 |
$imported = isset( $_POST['demo_data_imported'] ) && 'yes' === sanitize_text_field( wp_unslash( $_POST['demo_data_imported'] ) ); |
| 974 |
|
| 975 |
if ( ! in_array( $demo_choice, array( 'yes', 'no' ), true ) ) { |
| 976 |
$this->send_validation_error( |
| 977 |
__( 'Please choose whether to import demo data.', 'propertyhive' ), |
| 978 |
array( |
| 979 |
'demo_data_choice' => __( 'Please choose whether to import demo data.', 'propertyhive' ), |
| 980 |
) |
| 981 |
); |
| 982 |
} |
| 983 |
|
| 984 |
if ( 'yes' === $demo_choice && ! $imported ) { |
| 985 |
$this->send_validation_error( |
| 986 |
__( 'Demo data could not be imported. Please try again or choose No.', 'propertyhive' ), |
| 987 |
array( |
| 988 |
'demo_data_choice' => __( 'Demo data could not be imported. Please try again or choose No.', 'propertyhive' ), |
| 989 |
) |
| 990 |
); |
| 991 |
} |
| 992 |
|
| 993 |
self::track_event( $imported ? 'demo_data_completed' : 'demo_data_skipped' ); |
| 994 |
|
| 995 |
return self::update_state( |
| 996 |
array( |
| 997 |
'status' => 'in_progress', |
| 998 |
'last_step' => 'demo-data', |
| 999 |
'demo_data_imported' => $imported, |
| 1000 |
) |
| 1001 |
); |
| 1002 |
} |
| 1003 |
|
| 1004 |
/** |
| 1005 |
* Complete the wizard. |
| 1006 |
* |
| 1007 |
* @return array |
| 1008 |
*/ |
| 1009 |
private function save_complete_step() { |
| 1010 |
$import_feature = $this->get_import_feature_for_onboarding(); |
| 1011 |
$import_feature_active = $this->is_import_feature_active( ! empty( $import_feature['plugin_file'] ) ? $import_feature['plugin_file'] : '' ); |
| 1012 |
$completed_via = isset( $_POST['completed_via'] ) ? sanitize_key( wp_unslash( $_POST['completed_via'] ) ) : ''; |
| 1013 |
|
| 1014 |
if ( ! in_array( $completed_via, array( 'import', 'properties', 'site', 'settings' ), true ) ) { |
| 1015 |
$completed_via = ''; |
| 1016 |
} |
| 1017 |
|
| 1018 |
$state = self::update_state( |
| 1019 |
array( |
| 1020 |
'status' => 'completed', |
| 1021 |
'last_step' => 'complete', |
| 1022 |
'completed_at' => time(), |
| 1023 |
'import_feature_enabled' => $import_feature_active, |
| 1024 |
'completed_via' => $completed_via, |
| 1025 |
) |
| 1026 |
); |
| 1027 |
|
| 1028 |
self::track_event( 'completed', array( 'completed_via' => $completed_via ) ); |
| 1029 |
|
| 1030 |
return $state; |
| 1031 |
} |
| 1032 |
|
| 1033 |
/** |
| 1034 |
* Get existing start time or current time. |
| 1035 |
* |
| 1036 |
* @return int |
| 1037 |
*/ |
| 1038 |
private function get_started_at() { |
| 1039 |
$state = self::get_state(); |
| 1040 |
if ( ! empty( $state['started_at'] ) ) { |
| 1041 |
return absint( $state['started_at'] ); |
| 1042 |
} |
| 1043 |
|
| 1044 |
self::track_event( 'started' ); |
| 1045 |
|
| 1046 |
return time(); |
| 1047 |
} |
| 1048 |
|
| 1049 |
/** |
| 1050 |
* Get license state for rendering without remote checks. |
| 1051 |
* |
| 1052 |
* @return array |
| 1053 |
*/ |
| 1054 |
private function get_license_render_state() { |
| 1055 |
$pro_status = get_transient( 'ph_pro_license_status' ); |
| 1056 |
if ( false === $pro_status ) { |
| 1057 |
$pro_status = get_option( 'propertyhive_pro_license_key_status', '' ); |
| 1058 |
} |
| 1059 |
|
| 1060 |
if ( is_array( $pro_status ) && isset( $pro_status['success'] ) && true === $pro_status['success'] ) { |
| 1061 |
return array( |
| 1062 |
'type' => 'pro', |
| 1063 |
'active' => true, |
| 1064 |
'message' => __( 'You already have an active Property Hive subscription on this site.', 'propertyhive' ), |
| 1065 |
); |
| 1066 |
} |
| 1067 |
|
| 1068 |
// TO DO: TAKE INTO ACCOUNT LICENSE TYPE SELECTED |
| 1069 |
// SCENARIO - HAVING PRO TICKED AND NO LICENSE KEY BUT AN ACTIVE OLD KEY |
| 1070 |
$license = PH()->license->get_current_license(); |
| 1071 |
if ( |
| 1072 |
is_array( $license ) && |
| 1073 |
isset( $license['active'] ) && |
| 1074 |
'1' === (string) $license['active'] && |
| 1075 |
! empty( $license['expires_at'] ) && |
| 1076 |
( strtotime( $license['expires_at'] ) + DAY_IN_SECONDS ) > time() |
| 1077 |
) { |
| 1078 |
return array( |
| 1079 |
'type' => 'old', |
| 1080 |
'active' => true, |
| 1081 |
'message' => __( 'Your license is active - add-on updates are enabled.', 'propertyhive' ), |
| 1082 |
); |
| 1083 |
} |
| 1084 |
|
| 1085 |
return array( |
| 1086 |
'type' => '', |
| 1087 |
'active' => false, |
| 1088 |
'message' => '', |
| 1089 |
); |
| 1090 |
} |
| 1091 |
|
| 1092 |
/** |
| 1093 |
* Resolve the Property Import feature from the cached add-ons feed. |
| 1094 |
* |
| 1095 |
* @return array |
| 1096 |
*/ |
| 1097 |
private function get_import_feature_for_onboarding() { |
| 1098 |
$features = get_transient( 'propertyhive_features' ); |
| 1099 |
if ( false === $features || ! is_array( $features ) ) { |
| 1100 |
return array(); |
| 1101 |
} |
| 1102 |
|
| 1103 |
foreach ( $features as $feature ) { |
| 1104 |
if ( empty( $feature['wordpress_plugin_file'] ) || ! is_string( $feature['wordpress_plugin_file'] ) ) { |
| 1105 |
continue; |
| 1106 |
} |
| 1107 |
|
| 1108 |
$plugin_file = $feature['wordpress_plugin_file']; |
| 1109 |
$plugin_parts = explode( '/', $plugin_file ); |
| 1110 |
$slug = $plugin_parts[0]; |
| 1111 |
$haystack = strtolower( $plugin_file . ' ' . $slug ); |
| 1112 |
|
| 1113 |
if ( |
| 1114 |
false === strpos( $haystack, 'property-import' ) && |
| 1115 |
false === strpos( $haystack, 'property_import' ) && |
| 1116 |
false === strpos( $haystack, 'wp-property-hive-property-import' ) |
| 1117 |
) { |
| 1118 |
continue; |
| 1119 |
} |
| 1120 |
|
| 1121 |
return array( |
| 1122 |
'slug' => $slug, |
| 1123 |
'plugin_file' => $plugin_file, |
| 1124 |
'active' => $this->is_import_feature_active( $plugin_file ), |
| 1125 |
); |
| 1126 |
} |
| 1127 |
|
| 1128 |
return array(); |
| 1129 |
} |
| 1130 |
|
| 1131 |
/** |
| 1132 |
* Determine whether the Property Import feature is active. |
| 1133 |
* |
| 1134 |
* @param string $plugin_file Optional plugin file. |
| 1135 |
* @return bool |
| 1136 |
*/ |
| 1137 |
private function is_import_feature_active( $plugin_file = '' ) { |
| 1138 |
if ( ! function_exists( 'is_plugin_active' ) ) { |
| 1139 |
require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 1140 |
} |
| 1141 |
|
| 1142 |
if ( '' !== $plugin_file && is_plugin_active( $plugin_file ) ) { |
| 1143 |
return true; |
| 1144 |
} |
| 1145 |
|
| 1146 |
return class_exists( 'PH_Property_Import' ); |
| 1147 |
} |
| 1148 |
|
| 1149 |
/** |
| 1150 |
* Output onboarding page. |
| 1151 |
*/ |
| 1152 |
public function output() { |
| 1153 |
if ( ! current_user_can( 'manage_options' ) ) { |
| 1154 |
wp_die( esc_html__( 'You do not have permission to run setup.', 'propertyhive' ) ); |
| 1155 |
} |
| 1156 |
|
| 1157 |
$state = self::get_state(); |
| 1158 |
$countries = $this->get_countries(); |
| 1159 |
$default_country = $this->get_default_country(); |
| 1160 |
$office = $this->get_office_defaults( $state ); |
| 1161 |
$demo_active = class_exists( 'PH_Demo_Data' ); |
| 1162 |
$video_url = apply_filters( 'propertyhive_onboarding_video_url', '' ); |
| 1163 |
$departments = ! empty( $state['departments'] ) && is_array( $state['departments'] ) ? $state['departments'] : array( 'residential-sales', 'residential-lettings' ); |
| 1164 |
$usage = ! empty( $state['usage'] ) && is_array( $state['usage'] ) ? $state['usage'] : array(); |
| 1165 |
$usage_tracking = 'yes' === get_option( 'propertyhive_data_sharing', 'yes' ); |
| 1166 |
$has_license_key = ! empty( $state['has_license_key'] ) && 'yes' === $state['has_license_key'] ? 'yes' : 'no'; |
| 1167 |
$license_key_type = ! empty( $state['license_key_type'] ) && in_array( $state['license_key_type'], array( 'pro', 'old' ), true ) ? $state['license_key_type'] : 'pro'; |
| 1168 |
$license_render_state = $this->get_license_render_state(); |
| 1169 |
$license_activated = ! empty( $state['license_activated'] ) || ! empty( $license_render_state['active'] ); |
| 1170 |
$import_feature = $this->get_import_feature_for_onboarding(); |
| 1171 |
$import_feature_active = ! empty( $import_feature['active'] ) || $this->is_import_feature_active(); |
| 1172 |
$demo_choice = ! empty( $state['demo_data_imported'] ) ? 'yes' : 'no'; |
| 1173 |
$address_lookup_enabled = function_exists( 'ph_is_development_environment' ) && ph_is_development_environment() && '' !== trim( (string) get_option( 'propertyhive_getaddress_api_key', '' ) ); |
| 1174 |
$search_results_page_id = function_exists( 'ph_get_page_id' ) ? absint( ph_get_page_id( 'search_results' ) ) : 0; |
| 1175 |
$site_preview_url = home_url( '/' ); |
| 1176 |
if ( $search_results_page_id && get_post( $search_results_page_id ) ) { |
| 1177 |
$search_results_permalink = get_permalink( $search_results_page_id ); |
| 1178 |
if ( $search_results_permalink ) { |
| 1179 |
$site_preview_url = $search_results_permalink; |
| 1180 |
} |
| 1181 |
} |
| 1182 |
|
| 1183 |
$current_step = $this->get_current_step( $state ); |
| 1184 |
|
| 1185 |
self::track_event( 'step_viewed', array( 'step' => $current_step ) ); |
| 1186 |
$this->localize_script( $demo_active, $import_feature, $import_feature_active, $license_activated ); |
| 1187 |
?> |
| 1188 |
<div class="ph-onboarding" data-current-step="<?php echo esc_attr( $current_step ); ?>" data-demo-imported="<?php echo ! empty( $state['demo_data_imported'] ) ? 'yes' : 'no'; ?>"> |
| 1189 |
<div class="ph-onboarding__chrome"> |
| 1190 |
<div class="ph-onboarding__brand" aria-label="<?php esc_attr_e( 'Property Hive', 'propertyhive' ); ?>"> |
| 1191 |
<img src="<?php echo esc_url( PH()->plugin_url() . '/assets/images/admin/propertyhive-logo-onboarding.png' ); ?>" alt="<?php esc_attr_e( 'Property Hive', 'propertyhive' ); ?>"> |
| 1192 |
</div> |
| 1193 |
<button type="button" class="ph-onboarding__skip" data-ph-onboarding-skip><?php esc_html_e( 'Skip setup', 'propertyhive' ); ?></button> |
| 1194 |
</div> |
| 1195 |
|
| 1196 |
<div class="ph-onboarding__shell"> |
| 1197 |
<main class="ph-onboarding__content"> |
| 1198 |
<div class="ph-onboarding__progress" aria-hidden="true"> |
| 1199 |
<span data-progress-bar></span> |
| 1200 |
</div> |
| 1201 |
|
| 1202 |
<section class="ph-onboarding__panel ph-onboarding__panel--intro is-active" data-step="intro"> |
| 1203 |
<header class="ph-onboarding__intro"> |
| 1204 |
<h1><?php esc_html_e( 'Let\'s get Property Hive set up for you. Answer a few quick questions and we\'ll take care of the basics.', 'propertyhive' ); ?></h1> |
| 1205 |
</header> |
| 1206 |
<div class="ph-onboarding__intro-visual"> |
| 1207 |
<img src="<?php echo esc_url( PH()->plugin_url() . '/assets/images/admin/onboarding-intro-search.png' ); ?>" alt=""> |
| 1208 |
</div> |
| 1209 |
</section> |
| 1210 |
|
| 1211 |
<section class="ph-onboarding__panel" data-step="departments"> |
| 1212 |
<h2><?php esc_html_e( 'Which property sectors do you work in?', 'propertyhive' ); ?></h2> |
| 1213 |
<p><?php esc_html_e( 'Choose the ones you want to use in Property Hive.', 'propertyhive' ); ?></p> |
| 1214 |
<div class="ph-onboarding__cards" data-input-group="departments" data-validation-field="departments"> |
| 1215 |
<?php $this->output_choice_card( 'departments', 'residential-sales', __( 'Residential sales', 'propertyhive' ), __( 'Market and manage properties for sale.', 'propertyhive' ), in_array( 'residential-sales', $departments, true ) ); ?> |
| 1216 |
<?php $this->output_choice_card( 'departments', 'residential-lettings', __( 'Residential lettings', 'propertyhive' ), __( 'Manage lettings, tenancies and landlords.', 'propertyhive' ), in_array( 'residential-lettings', $departments, true ) ); ?> |
| 1217 |
<?php $this->output_choice_card( 'departments', 'commercial', __( 'Commercial', 'propertyhive' ), __( 'Manage commercial properties.', 'propertyhive' ), in_array( 'commercial', $departments, true ) ); ?> |
| 1218 |
</div> |
| 1219 |
<?php $this->output_field_error( 'departments' ); ?> |
| 1220 |
<?php $this->output_settings_note( __( 'General', 'propertyhive' ) ); ?> |
| 1221 |
</section> |
| 1222 |
|
| 1223 |
<section class="ph-onboarding__panel" data-step="country"> |
| 1224 |
<h2><?php esc_html_e( 'Where does your agency operate?', 'propertyhive' ); ?></h2> |
| 1225 |
<p><?php esc_html_e( 'Choose your country and we\'ll set the right currency and regional settings for Property Hive.', 'propertyhive' ); ?></p> |
| 1226 |
<label class="ph-onboarding__field" data-validation-field="country"> |
| 1227 |
<span><?php esc_html_e( 'Country', 'propertyhive' ); ?></span> |
| 1228 |
<select name="country" data-country-select required aria-describedby="ph-onboarding-error-country"> |
| 1229 |
<?php foreach ( $countries as $code => $country ) : ?> |
| 1230 |
<option value="<?php echo esc_attr( $code ); ?>" <?php selected( $default_country, $code ); ?>><?php echo esc_html( $country['name'] ); ?></option> |
| 1231 |
<?php endforeach; ?> |
| 1232 |
</select> |
| 1233 |
<?php $this->output_field_error( 'country' ); ?> |
| 1234 |
</label> |
| 1235 |
<?php $this->output_settings_note( __( 'General > International', 'propertyhive' ) ); ?> |
| 1236 |
</section> |
| 1237 |
|
| 1238 |
<section class="ph-onboarding__panel" data-step="office"> |
| 1239 |
<h2><?php esc_html_e( 'What are your office details?', 'propertyhive' ); ?></h2> |
| 1240 |
<p><?php esc_html_e( 'Tell us about your main office and we\'ll get it set up in Property Hive.', 'propertyhive' ); ?></p> |
| 1241 |
<?php if ( $address_lookup_enabled ) : ?> |
| 1242 |
<div class="ph-onboarding__address-lookup" data-address-lookup> |
| 1243 |
<label class="ph-onboarding__field ph-onboarding__field--wide"> |
| 1244 |
<span><?php esc_html_e( 'Find address', 'propertyhive' ); ?></span> |
| 1245 |
<input type="search" data-address-lookup-input autocomplete="off" placeholder="<?php esc_attr_e( 'Start typing an address or postcode', 'propertyhive' ); ?>"> |
| 1246 |
</label> |
| 1247 |
<div class="ph-onboarding__address-results" data-address-lookup-results hidden></div> |
| 1248 |
<div class="ph-onboarding__address-status" data-address-lookup-status role="status" aria-live="polite"></div> |
| 1249 |
</div> |
| 1250 |
<?php endif; ?> |
| 1251 |
<div class="ph-onboarding__form-grid ph-onboarding__form-grid--office"> |
| 1252 |
<label class="ph-onboarding__field" data-validation-field="office_name"> |
| 1253 |
<span><?php esc_html_e( 'Office name', 'propertyhive' ); ?></span> |
| 1254 |
<input type="text" name="office_name" value="<?php echo esc_attr( $office['name'] ); ?>" data-office-field="office_name" maxlength="120" aria-describedby="ph-onboarding-error-office_name"> |
| 1255 |
<?php $this->output_field_error( 'office_name' ); ?> |
| 1256 |
</label> |
| 1257 |
<label class="ph-onboarding__field" data-validation-field="office_address_1"> |
| 1258 |
<span><?php esc_html_e( 'Address Line 1', 'propertyhive' ); ?></span> |
| 1259 |
<input type="text" name="office_address_1" value="<?php echo esc_attr( $office['address_1'] ); ?>" data-office-field="office_address_1" maxlength="120" aria-describedby="ph-onboarding-error-office_address_1"> |
| 1260 |
<?php $this->output_field_error( 'office_address_1' ); ?> |
| 1261 |
</label> |
| 1262 |
<label class="ph-onboarding__field" data-validation-field="office_address_2"> |
| 1263 |
<span><?php esc_html_e( 'Address Line 2', 'propertyhive' ); ?></span> |
| 1264 |
<input type="text" name="office_address_2" value="<?php echo esc_attr( $office['address_2'] ); ?>" data-office-field="office_address_2" maxlength="120" aria-describedby="ph-onboarding-error-office_address_2"> |
| 1265 |
<?php $this->output_field_error( 'office_address_2' ); ?> |
| 1266 |
</label> |
| 1267 |
<label class="ph-onboarding__field" data-validation-field="office_address_3"> |
| 1268 |
<span><?php esc_html_e( 'Address Line 3', 'propertyhive' ); ?></span> |
| 1269 |
<input type="text" name="office_address_3" value="<?php echo esc_attr( $office['address_3'] ); ?>" data-office-field="office_address_3" maxlength="120" aria-describedby="ph-onboarding-error-office_address_3"> |
| 1270 |
<?php $this->output_field_error( 'office_address_3' ); ?> |
| 1271 |
</label> |
| 1272 |
<label class="ph-onboarding__field" data-validation-field="office_address_4"> |
| 1273 |
<span><?php esc_html_e( 'Address Line 4', 'propertyhive' ); ?></span> |
| 1274 |
<input type="text" name="office_address_4" value="<?php echo esc_attr( $office['address_4'] ); ?>" data-office-field="office_address_4" maxlength="120" aria-describedby="ph-onboarding-error-office_address_4"> |
| 1275 |
<?php $this->output_field_error( 'office_address_4' ); ?> |
| 1276 |
</label> |
| 1277 |
<label class="ph-onboarding__field" data-validation-field="office_postcode"> |
| 1278 |
<span><?php esc_html_e( 'Postcode', 'propertyhive' ); ?></span> |
| 1279 |
<input type="text" name="office_postcode" value="<?php echo esc_attr( $office['postcode'] ); ?>" data-office-field="office_postcode" maxlength="20" aria-describedby="ph-onboarding-error-office_postcode"> |
| 1280 |
<?php $this->output_field_error( 'office_postcode' ); ?> |
| 1281 |
</label> |
| 1282 |
<label class="ph-onboarding__field" data-validation-field="office_telephone_number"> |
| 1283 |
<span><?php esc_html_e( 'Phone number', 'propertyhive' ); ?></span> |
| 1284 |
<input type="tel" name="office_telephone_number" value="<?php echo esc_attr( $office['telephone_number'] ); ?>" data-office-field="office_telephone_number" maxlength="30" inputmode="tel" aria-describedby="ph-onboarding-error-office_telephone_number"> |
| 1285 |
<?php $this->output_field_error( 'office_telephone_number' ); ?> |
| 1286 |
</label> |
| 1287 |
<label class="ph-onboarding__field" data-validation-field="office_email_address"> |
| 1288 |
<span><?php esc_html_e( 'Email address', 'propertyhive' ); ?></span> |
| 1289 |
<input type="email" name="office_email_address" value="<?php echo esc_attr( $office['email_address'] ); ?>" data-office-field="office_email_address" maxlength="100" aria-describedby="ph-onboarding-error-office_email_address"> |
| 1290 |
<?php $this->output_field_error( 'office_email_address' ); ?> |
| 1291 |
</label> |
| 1292 |
</div> |
| 1293 |
<?php $this->output_settings_note( __( 'Offices', 'propertyhive' ) ); ?> |
| 1294 |
</section> |
| 1295 |
|
| 1296 |
<section class="ph-onboarding__panel" data-step="usage"> |
| 1297 |
<h2><?php esc_html_e( 'What else do you need?', 'propertyhive' ); ?></h2> |
| 1298 |
<p><?php esc_html_e( 'You\'ve got the property website foundations. Now choose how else you\'d like to use Property Hive.', 'propertyhive' ); ?></p> |
| 1299 |
<div class="ph-onboarding__cards ph-onboarding__cards--usage" data-input-group="usage" data-validation-field="usage"> |
| 1300 |
<?php $this->output_choice_card( 'usage', 'import_properties', __( 'Import properties', 'propertyhive' ), __( 'Keep your website in sync with properties from your CRM.', 'propertyhive' ), in_array( 'import_properties', $usage, true ), true ); ?> |
| 1301 |
<?php $this->output_choice_card( 'usage', 'crm', __( 'Use it as a CRM', 'propertyhive' ), __( 'Manage contacts, appraisals, viewings, offers and tenancies.', 'propertyhive' ), in_array( 'crm', $usage, true ) ); ?> |
| 1302 |
<?php $this->output_choice_card( 'usage', 'portal_uploads', __( 'Upload to portals', 'propertyhive' ), __( 'Automatically send your properties to third party portals.', 'propertyhive' ), in_array( 'portal_uploads', $usage, true ), true ); ?> |
| 1303 |
<?php $this->output_choice_card( 'usage', 'not_sure', __( 'Just the website for now', 'propertyhive' ), __( 'No problem. You can always choose later.', 'propertyhive' ), in_array( 'not_sure', $usage, true ) ); ?> |
| 1304 |
</div> |
| 1305 |
<?php $this->output_field_error( 'usage' ); ?> |
| 1306 |
<div class="ph-onboarding__pro-note" data-usage-pro-note <?php echo in_array( 'import_properties', $usage, true ) || in_array( 'portal_uploads', $usage, true ) ? '' : 'hidden'; ?>> |
| 1307 |
<p> |
| 1308 |
<strong><?php esc_html_e( 'Pro features are included with our paid plans.', 'propertyhive' ); ?></strong> |
| 1309 |
<?php esc_html_e( 'You can try them free for 7 days.', 'propertyhive' ); ?> |
| 1310 |
</p> |
| 1311 |
<span class="ph-onboarding__pro-note-actions"> |
| 1312 |
<a class="button button-primary" href="<?php echo esc_url( $this->get_url( '/pricing', 'trial-cta' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Start your free 7-day trial', 'propertyhive' ); ?></a> |
| 1313 |
<a class="ph-onboarding__pro-note-link" href="<?php echo esc_url( $this->get_url( '/pricing', 'whats-included' ) ); ?>#compare" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'What\'s included?', 'propertyhive' ); ?></a> |
| 1314 |
</span> |
| 1315 |
</div> |
| 1316 |
<?php /*$this->output_settings_note( __( 'General > Modules', 'propertyhive' ) );*/ ?> |
| 1317 |
</section> |
| 1318 |
|
| 1319 |
<section class="ph-onboarding__panel" data-step="license"> |
| 1320 |
<h2><?php esc_html_e( 'Do you have a Property Hive subscription?', 'propertyhive' ); ?></h2> |
| 1321 |
<?php /*<p><?php esc_html_e( 'If you already have a paid Property Hive plan, enter your licence key below. You can always add it later.', 'propertyhive' ); ?></p>*/ ?> |
| 1322 |
|
| 1323 |
<?php if ( ! empty( $license_render_state['active'] ) ) : ?> |
| 1324 |
<input type="hidden" name="has_license_key" value="yes"> |
| 1325 |
<input type="hidden" name="license_key_type" value="<?php echo esc_attr( $license_render_state['type'] ); ?>"> |
| 1326 |
<div class="ph-onboarding__license-state is-ok"> |
| 1327 |
<strong>✓ <?php echo esc_html( $license_render_state['message'] ); ?></strong> |
| 1328 |
</div> |
| 1329 |
<?php if ( 'old' === $license_render_state['type'] ) : ?> |
| 1330 |
<div class="ph-onboarding__pro-note"> |
| 1331 |
<p> |
| 1332 |
<strong><?php esc_html_e( 'Try Property Hive Pro free for 7 days.', 'propertyhive' ); ?></strong> |
| 1333 |
<?php esc_html_e( 'Pro features need a subscription.', 'propertyhive' ); ?> |
| 1334 |
</p> |
| 1335 |
<span class="ph-onboarding__pro-note-actions"> |
| 1336 |
<a class="button button-primary" href="<?php echo esc_url( $this->get_url( '/pricing', 'license-step-trial' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Start your free 7-day trial', 'propertyhive' ); ?></a> |
| 1337 |
<a class="ph-onboarding__pro-note-link" href="<?php echo esc_url( $this->get_url( '/pricing', 'license-step-whats-included' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'View pricing', 'propertyhive' ); ?></a> |
| 1338 |
</span> |
| 1339 |
</div> |
| 1340 |
<?php endif; ?> |
| 1341 |
<?php else : ?> |
| 1342 |
<div class="ph-onboarding__cards ph-onboarding__cards--radio" data-input-group="license-choice"> |
| 1343 |
<?php $this->output_radio_card( 'has_license_key', 'yes', __( 'Yes, I already have a subscription', 'propertyhive' ), __( 'Enter your licence key to connect it to this site.', 'propertyhive' ), 'yes' === $has_license_key, false ); ?> |
| 1344 |
<?php $this->output_radio_card( 'has_license_key', 'no', __( 'No, not yet', 'propertyhive' ), __( 'That\'s fine. You can carry on with the free version.', 'propertyhive' ), 'no' === $has_license_key, false ); ?> |
| 1345 |
</div> |
| 1346 |
|
| 1347 |
<div class="ph-onboarding__license-panel <?php echo 'yes' === $has_license_key ? 'is-open' : ''; ?>" data-license-panel="yes"> |
| 1348 |
<div class="ph-onboarding__license-type" data-input-group="license-key-type" style="display:none"> |
| 1349 |
<label class="ph-onboarding__choice ph-onboarding__choice--compact"> |
| 1350 |
<input type="radio" name="license_key_type" value="pro" checked <?php /*checked( 'pro', $license_key_type );*/ ?>> |
| 1351 |
<span class="ph-onboarding__choice-check" aria-hidden="true"></span> |
| 1352 |
<span class="ph-onboarding__choice-copy"> |
| 1353 |
<strong><?php esc_html_e( 'Pro subscription key', 'propertyhive' ); ?></strong> |
| 1354 |
</span> |
| 1355 |
</label> |
| 1356 |
<label class="ph-onboarding__choice ph-onboarding__choice--compact"> |
| 1357 |
<input type="radio" name="license_key_type" value="old" <?php checked( 'old', $license_key_type ); ?>> |
| 1358 |
<span class="ph-onboarding__choice-check" aria-hidden="true"></span> |
| 1359 |
<span class="ph-onboarding__choice-copy"> |
| 1360 |
<strong><?php esc_html_e( 'Old-style key (purchased before October 2023)', 'propertyhive' ); ?></strong> |
| 1361 |
</span> |
| 1362 |
</label> |
| 1363 |
</div> |
| 1364 |
|
| 1365 |
<div class="ph-onboarding__license-entry" data-license-entry> |
| 1366 |
<div class="ph-onboarding__field ph-onboarding__field--wide" data-validation-field="license_key"> |
| 1367 |
<label for="ph-onboarding-license-key"><span><?php esc_html_e( 'License key', 'propertyhive' ); ?></span></label> |
| 1368 |
<div class="ph-onboarding__license-entry-row"> |
| 1369 |
<input id="ph-onboarding-license-key" type="text" name="license_key" autocomplete="off" aria-describedby="ph-onboarding-error-license_key"> |
| 1370 |
<button type="button" class="button button-primary" data-license-activate><?php esc_html_e( 'Activate', 'propertyhive' ); ?></button> |
| 1371 |
</div> |
| 1372 |
<?php $this->output_field_error( 'license_key' ); ?> |
| 1373 |
<small><?php esc_html_e( 'You can find your license key in your order confirmation email or your account on the Property Hive website.', 'propertyhive' ); ?></small> |
| 1374 |
</div> |
| 1375 |
</div> |
| 1376 |
|
| 1377 |
<p class="ph-onboarding__license-old-note" data-license-old-note><?php esc_html_e( "Old-style keys enable updates for add-ons you've already purchased. Pro features need a Pro subscription.", 'propertyhive' ); ?></p> |
| 1378 |
<div class="ph-onboarding__license-state" data-license-state role="status" aria-live="polite"></div> |
| 1379 |
</div> |
| 1380 |
|
| 1381 |
<div class="ph-onboarding__license-panel <?php echo 'no' === $has_license_key ? 'is-open' : ''; ?>" data-license-panel="no"> |
| 1382 |
<div class="ph-onboarding__pro-note"> |
| 1383 |
<p> |
| 1384 |
<strong><?php esc_html_e( 'Want to try the paid features?', 'propertyhive' ); ?></strong> |
| 1385 |
<?php esc_html_e( 'Try all Property Hive features free for 7 days.', 'propertyhive' ); ?> |
| 1386 |
</p> |
| 1387 |
<span class="ph-onboarding__pro-note-actions"> |
| 1388 |
<a class="button button-primary" href="<?php echo esc_url( $this->get_url( '/pricing', 'license-step-trial' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Start your free 7-day trial', 'propertyhive' ); ?></a> |
| 1389 |
<a class="ph-onboarding__pro-note-link" href="<?php echo esc_url( $this->get_url( '/pricing', 'license-step-whats-included' ) ); ?>#compare" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'What\'s included?', 'propertyhive' ); ?></a> |
| 1390 |
</span> |
| 1391 |
</div> |
| 1392 |
</div> |
| 1393 |
<?php endif; ?> |
| 1394 |
|
| 1395 |
<?php $this->output_settings_note( __( 'License', 'propertyhive' ) ); ?> |
| 1396 |
</section> |
| 1397 |
|
| 1398 |
<section class="ph-onboarding__panel" data-step="demo-data"> |
| 1399 |
<h2><?php esc_html_e( 'Would you like to import demo data?', 'propertyhive' ); ?></h2> |
| 1400 |
<p><?php esc_html_e( 'Add sample properties, contacts and other data so you can explore how Property Hive works before using your own.', 'propertyhive' ); ?></p> |
| 1401 |
|
| 1402 |
<div class="ph-onboarding__cards ph-onboarding__cards--radio" data-input-group="demo-data-choice" data-validation-field="demo_data_choice"> |
| 1403 |
<?php $this->output_radio_card( 'demo_data_choice', 'no', __( 'No', 'propertyhive' ), __( 'Skip the demo data and use my own.', 'propertyhive' ), 'no' === $demo_choice, false ); ?> |
| 1404 |
<?php $this->output_radio_card( 'demo_data_choice', 'yes', __( 'Yes', 'propertyhive' ), __( 'Add example data so I can explore Property Hive first.', 'propertyhive' ), 'yes' === $demo_choice, false ); ?> |
| 1405 |
</div> |
| 1406 |
<?php $this->output_field_error( 'demo_data_choice' ); ?> |
| 1407 |
|
| 1408 |
<div class="ph-onboarding__demo-box" data-demo-progress-box> |
| 1409 |
<div class="ph-onboarding__demo-status" data-demo-status> |
| 1410 |
<?php echo esc_html( $demo_active ? __( 'Demo data will import when you continue.', 'propertyhive' ) : __( 'The Demo Data feature will be activated and the data imported when you continue.', 'propertyhive' ) ); ?> |
| 1411 |
</div> |
| 1412 |
<div class="ph-onboarding__demo-progress" aria-hidden="true"><span data-demo-progress-bar></span></div> |
| 1413 |
<div class="ph-onboarding__demo-results" data-demo-results></div> |
| 1414 |
</div> |
| 1415 |
<?php $this->output_settings_note( __( 'Demo Data', 'propertyhive' ) ); ?> |
| 1416 |
</section> |
| 1417 |
|
| 1418 |
<section class="ph-onboarding__panel" data-step="complete"> |
| 1419 |
<h2><?php esc_html_e( "You're all set", 'propertyhive' ); ?></h2> |
| 1420 |
<p><?php esc_html_e( 'Property Hive is ready to go. What would you like to do next?', 'propertyhive' ); ?></p> |
| 1421 |
|
| 1422 |
<?php /*if ( ! empty( $video_url ) ) : ?> |
| 1423 |
<div class="ph-onboarding__video"> |
| 1424 |
<iframe src="<?php echo esc_url( $video_url ); ?>" title="<?php esc_attr_e( 'Property Hive getting started video', 'propertyhive' ); ?>" allowfullscreen></iframe> |
| 1425 |
</div> |
| 1426 |
<?php else : ?> |
| 1427 |
<div class="ph-onboarding__video ph-onboarding__video--placeholder"> |
| 1428 |
<span><?php esc_html_e( 'Welcome video coming soon', 'propertyhive' ); ?></span> |
| 1429 |
</div> |
| 1430 |
<?php endif;*/ ?> |
| 1431 |
|
| 1432 |
<div class="ph-onboarding__exits"> |
| 1433 |
|
| 1434 |
<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( $this->get_url( '/import-properties', 'exit' ) ); ?>" target="_blank" rel="noopener noreferrer" data-onboarding-exit data-exit="import" data-recommendation="import" <?php echo ! $license_activated && in_array( 'import_properties', $usage, true ) ? '' : 'hidden'; ?>> |
| 1435 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'Learn about property imports', 'propertyhive' ); ?></span> |
| 1436 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'See how it works and check if your CRM is supported.', 'propertyhive' ); ?></span> |
| 1437 |
</a> |
| 1438 |
|
| 1439 |
<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( $this->get_url( '/export-properties', 'exit' ) ); ?>" target="_blank" rel="noopener noreferrer" data-onboarding-exit data-exit="import" data-recommendation="portal" <?php echo ! $license_activated && in_array( 'portal_uploads', $usage, true ) ? '' : 'hidden'; ?>> |
| 1440 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'Learn about portal exports', 'propertyhive' ); ?></span> |
| 1441 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'See how it works and where you can send your properties.', 'propertyhive' ); ?></span> |
| 1442 |
</a> |
| 1443 |
|
| 1444 |
<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( admin_url( 'admin.php?page=propertyhive_import_properties' ) ); ?>" data-onboarding-exit data-exit="import" data-import-setup <?php echo $import_feature_active ? '' : 'hidden'; ?>> |
| 1445 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'Create a property import', 'propertyhive' ); ?></span> |
| 1446 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'Bring properties in from your CRM', 'propertyhive' ); ?></span> |
| 1447 |
</a> |
| 1448 |
<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( admin_url( 'edit.php?post_type=property' ) ); ?>" data-onboarding-exit data-exit="properties"> |
| 1449 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'View and manage properties', 'propertyhive' ); ?></span> |
| 1450 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'Open the property list in the dashboard', 'propertyhive' ); ?></span> |
| 1451 |
</a> |
| 1452 |
<?php /*<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( $site_preview_url ); ?>" data-onboarding-exit data-exit="site" <?php echo ! empty( $state['demo_data_imported'] ) ? '' : 'hidden'; ?>> |
| 1453 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'See properties on your site', 'propertyhive' ); ?></span> |
| 1454 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'View the demo properties on your website', 'propertyhive' ); ?></span> |
| 1455 |
</a>*/ ?> |
| 1456 |
<a class="button button-primary ph-onboarding__exit" href="<?php echo esc_url( admin_url( 'admin.php?page=ph-settings' ) ); ?>" data-onboarding-exit data-exit="settings"> |
| 1457 |
<span class="ph-onboarding__exit-label"><?php esc_html_e( 'Explore more settings', 'propertyhive' ); ?></span> |
| 1458 |
<span class="ph-onboarding__exit-sub-label"><?php esc_html_e( 'Take a look at your setup and make any changes', 'propertyhive' ); ?></span> |
| 1459 |
</a> |
| 1460 |
</div> |
| 1461 |
</section> |
| 1462 |
|
| 1463 |
<div class="ph-onboarding__message" data-message role="alert" aria-live="polite"></div> |
| 1464 |
|
| 1465 |
<div class="ph-onboarding__actions"> |
| 1466 |
<button type="button" class="button ph-onboarding__back" data-back><?php esc_html_e( 'Back', 'propertyhive' ); ?></button> |
| 1467 |
<button type="button" class="button button-primary ph-onboarding__next" data-next><?php esc_html_e( 'Continue', 'propertyhive' ); ?></button> |
| 1468 |
</div> |
| 1469 |
|
| 1470 |
<div class="ph-onboarding__tracking"> |
| 1471 |
<label class="ph-onboarding__tracking-label"> |
| 1472 |
<input type="checkbox" name="usage_tracking" value="yes" data-usage-tracking <?php checked( $usage_tracking ); ?>> |
| 1473 |
<span class="ph-onboarding__tracking-copy"> |
| 1474 |
<strong><?php esc_html_e( 'Help improve Property Hive', 'propertyhive' ); ?></strong> |
| 1475 |
<small><?php esc_html_e( 'Share non-sensitive setup and feature usage data to help us improve Property Hive. We don\'t collect personal, contact or property details.', 'propertyhive' ); ?></small> |
| 1476 |
</span> |
| 1477 |
</label> |
| 1478 |
</div> |
| 1479 |
</main> |
| 1480 |
</div> |
| 1481 |
</div> |
| 1482 |
<?php |
| 1483 |
} |
| 1484 |
|
| 1485 |
/** |
| 1486 |
* Output a validation error container. |
| 1487 |
* |
| 1488 |
* @param string $field Field key. |
| 1489 |
*/ |
| 1490 |
private function output_field_error( $field ) { |
| 1491 |
?> |
| 1492 |
<span id="ph-onboarding-error-<?php echo esc_attr( $field ); ?>" class="ph-onboarding__field-error" data-field-error="<?php echo esc_attr( $field ); ?>" aria-live="polite"></span> |
| 1493 |
<?php |
| 1494 |
} |
| 1495 |
|
| 1496 |
/** |
| 1497 |
* Output a checkbox card. |
| 1498 |
* |
| 1499 |
* @param string $name Input name. |
| 1500 |
* @param string $value Input value. |
| 1501 |
* @param string $title Card title. |
| 1502 |
* @param string $summary Card summary. |
| 1503 |
* @param bool $checked Default checked state. |
| 1504 |
* @param bool $pro Whether the option requires a Pro subscription. |
| 1505 |
*/ |
| 1506 |
private function output_choice_card( $name, $value, $title, $summary, $checked, $pro = false ) { |
| 1507 |
?> |
| 1508 |
<label class="ph-onboarding__choice"> |
| 1509 |
<input type="checkbox" name="<?php echo esc_attr( $name ); ?>[]" value="<?php echo esc_attr( $value ); ?>" <?php checked( $checked ); ?>> |
| 1510 |
<span class="ph-onboarding__choice-check" aria-hidden="true"></span> |
| 1511 |
<span class="ph-onboarding__choice-copy"> |
| 1512 |
<strong> |
| 1513 |
<?php echo esc_html( $title ); ?> |
| 1514 |
<?php if ( $pro ) : ?> |
| 1515 |
<span class="ph-onboarding__choice-pro"><?php esc_html_e( 'Pro', 'propertyhive' ); ?></span> |
| 1516 |
<?php endif; ?> |
| 1517 |
</strong> |
| 1518 |
<small><?php echo esc_html( $summary ); ?></small> |
| 1519 |
</span> |
| 1520 |
</label> |
| 1521 |
<?php |
| 1522 |
} |
| 1523 |
|
| 1524 |
/** |
| 1525 |
* Output a radio card. |
| 1526 |
* |
| 1527 |
* @param string $name Input name. |
| 1528 |
* @param string $value Input value. |
| 1529 |
* @param string $title Card title. |
| 1530 |
* @param string $summary Card summary. |
| 1531 |
* @param bool $checked Default checked state. |
| 1532 |
* @param bool $disabled Disabled state. |
| 1533 |
*/ |
| 1534 |
private function output_radio_card( $name, $value, $title, $summary, $checked, $disabled ) { |
| 1535 |
?> |
| 1536 |
<label class="ph-onboarding__choice <?php echo $disabled ? 'is-disabled' : ''; ?>"> |
| 1537 |
<input type="radio" name="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_attr( $value ); ?>" <?php checked( $checked ); ?> <?php disabled( $disabled ); ?>> |
| 1538 |
<span class="ph-onboarding__choice-check" aria-hidden="true"></span> |
| 1539 |
<span class="ph-onboarding__choice-copy"> |
| 1540 |
<strong><?php echo esc_html( $title ); ?></strong> |
| 1541 |
<small><?php echo esc_html( $summary ); ?></small> |
| 1542 |
</span> |
| 1543 |
</label> |
| 1544 |
<?php |
| 1545 |
} |
| 1546 |
|
| 1547 |
/** |
| 1548 |
* Output a settings location note. |
| 1549 |
* |
| 1550 |
* @param string $location Settings location. |
| 1551 |
*/ |
| 1552 |
private function output_settings_note( $location ) { |
| 1553 |
?> |
| 1554 |
<p class="ph-onboarding__settings-note"> |
| 1555 |
<?php |
| 1556 |
printf( |
| 1557 |
/* translators: %s: settings location, for example General > International. */ |
| 1558 |
esc_html__( "You can change this later under 'Property Hive > Settings > %s'.", 'propertyhive' ), |
| 1559 |
esc_html( $location ) |
| 1560 |
); |
| 1561 |
?> |
| 1562 |
</p> |
| 1563 |
<?php |
| 1564 |
} |
| 1565 |
|
| 1566 |
/** |
| 1567 |
* Localize onboarding script data. |
| 1568 |
* |
| 1569 |
* @param bool $demo_active Whether demo data add-on is active. |
| 1570 |
* @param array $import_feature Cached Property Import feature data. |
| 1571 |
* @param bool $import_feature_active Whether Property Import is active. |
| 1572 |
* @param bool $license_activated Whether a license is active. |
| 1573 |
*/ |
| 1574 |
private function localize_script( $demo_active, $import_feature = array(), $import_feature_active = false, $license_activated = false ) { |
| 1575 |
wp_localize_script( |
| 1576 |
'propertyhive_admin_onboarding', |
| 1577 |
'propertyhive_onboarding', |
| 1578 |
array( |
| 1579 |
'ajax_url' => admin_url( 'admin-ajax.php' ), |
| 1580 |
'nonce' => wp_create_nonce( self::NONCE_ACTION ), |
| 1581 |
'steps' => $this->steps, |
| 1582 |
'settings_url' => admin_url( 'admin.php?page=ph-settings' ), |
| 1583 |
'features_url' => admin_url( 'admin.php?page=ph-settings&tab=features&profilter=free' ), |
| 1584 |
'import_features_url' => admin_url( 'admin.php?page=ph-settings&tab=features&profilter=import' ), |
| 1585 |
'import_setup_url' => admin_url( 'admin.php?page=propertyhive_import_properties' ), |
| 1586 |
'license_trial_url' => $this->get_url( '/pricing', 'license-step-trial' ), |
| 1587 |
'demo_data_active' => $demo_active ? 'yes' : 'no', |
| 1588 |
'demo_data_nonce' => wp_create_nonce( 'propertyhive_demo_data' ), |
| 1589 |
'demo_data_feature_slug' => 'propertyhive-demo-data', |
| 1590 |
'license_activated' => $license_activated ? 'yes' : 'no', |
| 1591 |
'updates_nonce' => wp_create_nonce( 'updates' ), |
| 1592 |
'can_install_plugins' => current_user_can( 'install_plugins' ) ? 'yes' : 'no', |
| 1593 |
'import_feature_slug' => ! empty( $import_feature['slug'] ) ? $import_feature['slug'] : '', |
| 1594 |
'import_feature_plugin' => ! empty( $import_feature['plugin_file'] ) ? $import_feature['plugin_file'] : '', |
| 1595 |
'import_feature_active' => $import_feature_active ? 'yes' : 'no', |
| 1596 |
'address_lookup_enabled' => ( function_exists( 'ph_is_development_environment' ) && ph_is_development_environment() && '' !== trim( (string) get_option( 'propertyhive_getaddress_api_key', '' ) ) ) ? 'yes' : 'no', |
| 1597 |
'address_lookup_nonce' => wp_create_nonce( 'propertyhive_getaddress_lookup' ), |
| 1598 |
'demo_base_sections' => array( 'applicant', 'property' ), |
| 1599 |
'demo_crm_sub_sections' => apply_filters( 'propertyhive_onboarding_demo_crm_sub_sections', array( 'appraisal', 'viewing', 'offer', 'sale', 'tenancy', 'enquiry' ) ), |
| 1600 |
'i18n' => array( |
| 1601 |
'continue' => __( 'Continue', 'propertyhive' ), |
| 1602 |
'saving' => __( 'Saving...', 'propertyhive' ), |
| 1603 |
'importing' => __( 'Importing demo data...', 'propertyhive' ), |
| 1604 |
'activatingDemoData' => __( 'Activating Demo Data...', 'propertyhive' ), |
| 1605 |
'importComplete' => __( 'Demo data imported.', 'propertyhive' ), |
| 1606 |
'importFailed' => __( 'Demo data could not be imported. You can continue setup and try again later.', 'propertyhive' ), |
| 1607 |
'demoDataInactive' => __( 'The Demo Data feature could not be activated on this site.', 'propertyhive' ), |
| 1608 |
'licenseKeyRequired' => __( 'Please enter your license key.', 'propertyhive' ), |
| 1609 |
'licenseChecking' => __( 'Checking your key... Contacting wp-property-hive.com - a couple of seconds.', 'propertyhive' ), |
| 1610 |
'licenseProSuccess' => __( 'Pro activated. The features in your plan are ready to use.', 'propertyhive' ), |
| 1611 |
'licenseOldSuccess' => __( 'License valid - updates are enabled for your purchased add-ons.', 'propertyhive' ), |
| 1612 |
'licenseError' => __( "That key wasn't recognised. Check for typos, or recover your key. You can keep going and add it later - nothing is lost.", 'propertyhive' ), |
| 1613 |
'licenseOldTrial' => __( 'Want the Pro features too? Start a free 7-day trial.', 'propertyhive' ), |
| 1614 |
'importFeatureEnabled' => __( 'Property Import feature enabled', 'propertyhive' ), |
| 1615 |
'renewLicense' => __( 'Renew License', 'propertyhive' ), |
| 1616 |
'chooseDepartment' => __( 'Please choose at least one property sector.', 'propertyhive' ), |
| 1617 |
'chooseCountry' => __( 'Please choose a valid country.', 'propertyhive' ), |
| 1618 |
'chooseUsage' => __( 'Please choose how you will use Property Hive.', 'propertyhive' ), |
| 1619 |
'chooseDemoData' => __( 'Please choose whether to import demo data.', 'propertyhive' ), |
| 1620 |
'officeNameTooLong' => __( 'Office name must be 120 characters or fewer.', 'propertyhive' ), |
| 1621 |
'officeAddressTooLong' => __( 'Address Line 1 must be 120 characters or fewer.', 'propertyhive' ), |
| 1622 |
'officeAddress2TooLong' => __( 'Address Line 2 must be 120 characters or fewer.', 'propertyhive' ), |
| 1623 |
'officeAddress3TooLong' => __( 'Address Line 3 must be 120 characters or fewer.', 'propertyhive' ), |
| 1624 |
'officeAddress4TooLong' => __( 'Address Line 4 must be 120 characters or fewer.', 'propertyhive' ), |
| 1625 |
'officePostcodeTooLong' => __( 'Postcode must be 20 characters or fewer.', 'propertyhive' ), |
| 1626 |
'officePhoneTooLong' => __( 'Phone number must be 30 characters or fewer.', 'propertyhive' ), |
| 1627 |
'officePhoneInvalid' => __( 'Please enter a valid phone number.', 'propertyhive' ), |
| 1628 |
'officeEmailTooLong' => __( 'Email address must be 100 characters or fewer.', 'propertyhive' ), |
| 1629 |
'officeEmailInvalid' => __( 'Please enter a valid email address.', 'propertyhive' ), |
| 1630 |
'addressLookupSearching' => __( 'Searching addresses...', 'propertyhive' ), |
| 1631 |
'addressLookupNoResults' => __( 'No matching addresses found.', 'propertyhive' ), |
| 1632 |
'addressLookupFailed' => __( 'Address lookup could not be completed. Please enter the address manually.', 'propertyhive' ), |
| 1633 |
'addressLookupSelected' => __( 'Address selected.', 'propertyhive' ), |
| 1634 |
'skipConfirm' => __( 'Skip setup? You can still configure Property Hive from settings later.', 'propertyhive' ), |
| 1635 |
), |
| 1636 |
) |
| 1637 |
); |
| 1638 |
} |
| 1639 |
|
| 1640 |
/** |
| 1641 |
* Get available countries. |
| 1642 |
* |
| 1643 |
* @return array |
| 1644 |
*/ |
| 1645 |
private function get_countries() { |
| 1646 |
$countries = new PH_Countries(); |
| 1647 |
return is_array( $countries->countries ) ? $countries->countries : array(); |
| 1648 |
} |
| 1649 |
|
| 1650 |
/** |
| 1651 |
* Infer the default country for onboarding. |
| 1652 |
* |
| 1653 |
* @return string |
| 1654 |
*/ |
| 1655 |
private function get_default_country() { |
| 1656 |
$countries = $this->get_countries(); |
| 1657 |
$existing = get_option( 'propertyhive_default_country', '' ); |
| 1658 |
if ( isset( $countries[ $existing ] ) ) { |
| 1659 |
return $existing; |
| 1660 |
} |
| 1661 |
|
| 1662 |
$locale = get_locale(); |
| 1663 |
if ( is_string( $locale ) && false !== strpos( $locale, '_' ) ) { |
| 1664 |
$locale_country = strtoupper( substr( strrchr( $locale, '_' ), 1 ) ); |
| 1665 |
if ( isset( $countries[ $locale_country ] ) ) { |
| 1666 |
return $locale_country; |
| 1667 |
} |
| 1668 |
} |
| 1669 |
|
| 1670 |
$timezone = wp_timezone_string(); |
| 1671 |
$mapping = array( |
| 1672 |
'Europe/London' => 'GB', |
| 1673 |
'America/New_York' => 'US', |
| 1674 |
'America/Chicago' => 'US', |
| 1675 |
'America/Denver' => 'US', |
| 1676 |
'America/Los_Angeles' => 'US', |
| 1677 |
'Europe/Dublin' => 'IE', |
| 1678 |
'Europe/Paris' => 'FR', |
| 1679 |
'Europe/Madrid' => 'ES', |
| 1680 |
'Europe/Berlin' => 'DE', |
| 1681 |
'Australia/Sydney' => 'AU', |
| 1682 |
'Pacific/Auckland' => 'NZ', |
| 1683 |
); |
| 1684 |
|
| 1685 |
if ( isset( $mapping[ $timezone ], $countries[ $mapping[ $timezone ] ] ) ) { |
| 1686 |
return $mapping[ $timezone ]; |
| 1687 |
} |
| 1688 |
|
| 1689 |
return isset( $countries['GB'] ) ? 'GB' : key( $countries ); |
| 1690 |
} |
| 1691 |
|
| 1692 |
/** |
| 1693 |
* Sanitize office details from the onboarding form. |
| 1694 |
* |
| 1695 |
* @return array |
| 1696 |
*/ |
| 1697 |
private function sanitize_office_payload() { |
| 1698 |
return array( |
| 1699 |
'name' => isset( $_POST['office_name'] ) ? sanitize_text_field( wp_unslash( $_POST['office_name'] ) ) : '', |
| 1700 |
'address_1' => isset( $_POST['office_address_1'] ) ? sanitize_text_field( wp_unslash( $_POST['office_address_1'] ) ) : '', |
| 1701 |
'address_2' => isset( $_POST['office_address_2'] ) ? sanitize_text_field( wp_unslash( $_POST['office_address_2'] ) ) : '', |
| 1702 |
'address_3' => isset( $_POST['office_address_3'] ) ? sanitize_text_field( wp_unslash( $_POST['office_address_3'] ) ) : '', |
| 1703 |
'address_4' => isset( $_POST['office_address_4'] ) ? sanitize_text_field( wp_unslash( $_POST['office_address_4'] ) ) : '', |
| 1704 |
'postcode' => isset( $_POST['office_postcode'] ) ? sanitize_text_field( wp_unslash( $_POST['office_postcode'] ) ) : '', |
| 1705 |
'telephone_number' => isset( $_POST['office_telephone_number'] ) ? sanitize_text_field( wp_unslash( $_POST['office_telephone_number'] ) ) : '', |
| 1706 |
'email_address' => isset( $_POST['office_email_address'] ) ? sanitize_text_field( wp_unslash( $_POST['office_email_address'] ) ) : '', |
| 1707 |
); |
| 1708 |
} |
| 1709 |
|
| 1710 |
/** |
| 1711 |
* Validate office details. |
| 1712 |
* |
| 1713 |
* @param array $office Office payload. |
| 1714 |
* @return array |
| 1715 |
*/ |
| 1716 |
private function validate_office_payload( $office ) { |
| 1717 |
$errors = array(); |
| 1718 |
|
| 1719 |
$field_labels = array( |
| 1720 |
'name' => __( 'Office name', 'propertyhive' ), |
| 1721 |
'address_1' => __( 'Address Line 1', 'propertyhive' ), |
| 1722 |
'address_2' => __( 'Address Line 2', 'propertyhive' ), |
| 1723 |
'address_3' => __( 'Address Line 3', 'propertyhive' ), |
| 1724 |
'address_4' => __( 'Address Line 4', 'propertyhive' ), |
| 1725 |
'postcode' => __( 'Postcode', 'propertyhive' ), |
| 1726 |
'telephone_number' => __( 'Phone number', 'propertyhive' ), |
| 1727 |
'email_address' => __( 'Email address', 'propertyhive' ), |
| 1728 |
); |
| 1729 |
|
| 1730 |
$field_keys = array( |
| 1731 |
'name' => 'office_name', |
| 1732 |
'address_1' => 'office_address_1', |
| 1733 |
'address_2' => 'office_address_2', |
| 1734 |
'address_3' => 'office_address_3', |
| 1735 |
'address_4' => 'office_address_4', |
| 1736 |
'postcode' => 'office_postcode', |
| 1737 |
'telephone_number' => 'office_telephone_number', |
| 1738 |
'email_address' => 'office_email_address', |
| 1739 |
); |
| 1740 |
|
| 1741 |
$max_lengths = array( |
| 1742 |
'name' => 120, |
| 1743 |
'address_1' => 120, |
| 1744 |
'address_2' => 120, |
| 1745 |
'address_3' => 120, |
| 1746 |
'address_4' => 120, |
| 1747 |
'postcode' => 20, |
| 1748 |
'telephone_number' => 30, |
| 1749 |
'email_address' => 100, |
| 1750 |
); |
| 1751 |
|
| 1752 |
foreach ( $max_lengths as $field => $max_length ) { |
| 1753 |
if ( isset( $office[ $field ] ) && strlen( $office[ $field ] ) > $max_length ) { |
| 1754 |
$errors[ $field_keys[ $field ] ] = sprintf( |
| 1755 |
/* translators: 1: field label, 2: maximum character count. */ |
| 1756 |
__( '%1$s must be %2$d characters or fewer.', 'propertyhive' ), |
| 1757 |
$field_labels[ $field ], |
| 1758 |
$max_length |
| 1759 |
); |
| 1760 |
} |
| 1761 |
} |
| 1762 |
|
| 1763 |
/*if ( ! empty( $office['telephone_number'] ) ) { |
| 1764 |
$digits = preg_replace( '/\D+/', '', $office['telephone_number'] ); |
| 1765 |
if ( strlen( $digits ) < 7 || ! preg_match( '/^[0-9+\-\s().]+$/', $office['telephone_number'] ) ) { |
| 1766 |
$errors['office_telephone_number'] = __( 'Please enter a valid phone number.', 'propertyhive' ); |
| 1767 |
} |
| 1768 |
}*/ |
| 1769 |
|
| 1770 |
if ( ! empty( $office['email_address'] ) && ! is_email( $office['email_address'] ) ) { |
| 1771 |
$errors['office_email_address'] = __( 'Please enter a valid email address.', 'propertyhive' ); |
| 1772 |
} |
| 1773 |
|
| 1774 |
return $errors; |
| 1775 |
} |
| 1776 |
|
| 1777 |
/** |
| 1778 |
* Get defaults for the office address step. |
| 1779 |
* |
| 1780 |
* @param array $state Onboarding state. |
| 1781 |
* @return array |
| 1782 |
*/ |
| 1783 |
private function get_office_defaults( $state ) { |
| 1784 |
$office = array( |
| 1785 |
'name' => '', |
| 1786 |
'address_1' => '', |
| 1787 |
'address_2' => '', |
| 1788 |
'address_3' => '', |
| 1789 |
'address_4' => '', |
| 1790 |
'postcode' => '', |
| 1791 |
'telephone_number' => '', |
| 1792 |
'email_address' => '', |
| 1793 |
); |
| 1794 |
|
| 1795 |
$office_id = $this->get_primary_office_id(); |
| 1796 |
if ( $office_id ) { |
| 1797 |
$office = array( |
| 1798 |
'name' => get_the_title( $office_id ), |
| 1799 |
'address_1' => get_post_meta( $office_id, '_office_address_1', true ), |
| 1800 |
'address_2' => get_post_meta( $office_id, '_office_address_2', true ), |
| 1801 |
'address_3' => get_post_meta( $office_id, '_office_address_3', true ), |
| 1802 |
'address_4' => get_post_meta( $office_id, '_office_address_4', true ), |
| 1803 |
'postcode' => get_post_meta( $office_id, '_office_address_postcode', true ), |
| 1804 |
'telephone_number' => $this->get_primary_office_contact_value( $office_id, 'telephone_number' ), |
| 1805 |
'email_address' => $this->get_primary_office_contact_value( $office_id, 'email_address' ), |
| 1806 |
); |
| 1807 |
} |
| 1808 |
|
| 1809 |
if ( ! empty( $state['office'] ) && is_array( $state['office'] ) ) { |
| 1810 |
foreach ( array_keys( $office ) as $key ) { |
| 1811 |
if ( isset( $state['office'][ $key ] ) && '' !== $state['office'][ $key ] ) { |
| 1812 |
$office[ $key ] = $state['office'][ $key ]; |
| 1813 |
} |
| 1814 |
} |
| 1815 |
} |
| 1816 |
|
| 1817 |
return $office; |
| 1818 |
} |
| 1819 |
|
| 1820 |
/** |
| 1821 |
* Get the active departments that should receive shared office contact details. |
| 1822 |
* |
| 1823 |
* @return array |
| 1824 |
*/ |
| 1825 |
private function get_office_contact_departments() { |
| 1826 |
$state = self::get_state(); |
| 1827 |
$allowed = array( 'residential-sales', 'residential-lettings', 'commercial' ); |
| 1828 |
$departments = ! empty( $state['departments'] ) && is_array( $state['departments'] ) ? array_values( array_intersect( $allowed, $state['departments'] ) ) : array(); |
| 1829 |
|
| 1830 |
if ( empty( $departments ) ) { |
| 1831 |
foreach ( $allowed as $department ) { |
| 1832 |
if ( 'yes' === get_option( 'propertyhive_active_departments_' . str_replace( 'residential-', '', $department ), 'no' ) ) { |
| 1833 |
$departments[] = $department; |
| 1834 |
} |
| 1835 |
} |
| 1836 |
} |
| 1837 |
|
| 1838 |
return ! empty( $departments ) ? $departments : $allowed; |
| 1839 |
} |
| 1840 |
|
| 1841 |
/** |
| 1842 |
* Get the first saved office contact value across active departments. |
| 1843 |
* |
| 1844 |
* @param int $office_id Office post id. |
| 1845 |
* @param string $field Contact field name. |
| 1846 |
* @return string |
| 1847 |
*/ |
| 1848 |
private function get_primary_office_contact_value( $office_id, $field ) { |
| 1849 |
$meta_prefix = 'email_address' === $field ? '_office_email_address_' : '_office_telephone_number_'; |
| 1850 |
|
| 1851 |
foreach ( $this->get_office_contact_departments() as $department ) { |
| 1852 |
$value = get_post_meta( $office_id, $meta_prefix . str_replace( 'residential-', '', $department ), true ); |
| 1853 |
if ( '' !== $value ) { |
| 1854 |
return $value; |
| 1855 |
} |
| 1856 |
} |
| 1857 |
|
| 1858 |
return ''; |
| 1859 |
} |
| 1860 |
|
| 1861 |
/** |
| 1862 |
* Get the primary office id, falling back to the first office. |
| 1863 |
* |
| 1864 |
* @return int |
| 1865 |
*/ |
| 1866 |
private function get_primary_office_id() { |
| 1867 |
$office_ids = get_posts( |
| 1868 |
array( |
| 1869 |
'post_type' => 'office', |
| 1870 |
'post_status' => 'any', |
| 1871 |
'posts_per_page' => 1, |
| 1872 |
'fields' => 'ids', |
| 1873 |
'meta_key' => 'primary', |
| 1874 |
'meta_value' => '1', |
| 1875 |
) |
| 1876 |
); |
| 1877 |
|
| 1878 |
if ( ! empty( $office_ids ) ) { |
| 1879 |
return absint( $office_ids[0] ); |
| 1880 |
} |
| 1881 |
|
| 1882 |
$office_ids = get_posts( |
| 1883 |
array( |
| 1884 |
'post_type' => 'office', |
| 1885 |
'post_status' => 'any', |
| 1886 |
'posts_per_page' => 1, |
| 1887 |
'fields' => 'ids', |
| 1888 |
) |
| 1889 |
); |
| 1890 |
|
| 1891 |
return ! empty( $office_ids ) ? absint( $office_ids[0] ) : 0; |
| 1892 |
} |
| 1893 |
|
| 1894 |
/** |
| 1895 |
* Get current step. |
| 1896 |
* |
| 1897 |
* @param array $state Onboarding state. |
| 1898 |
* @return string |
| 1899 |
*/ |
| 1900 |
private function get_current_step( $state ) { |
| 1901 |
if ( isset( $state['status'] ) && 'completed' === $state['status'] ) { |
| 1902 |
return 'complete'; |
| 1903 |
} |
| 1904 |
|
| 1905 |
if ( ! empty( $state['last_step'] ) && in_array( $state['last_step'], $this->steps, true ) && 'completed' !== $state['status'] ) { |
| 1906 |
return $state['last_step']; |
| 1907 |
} |
| 1908 |
|
| 1909 |
return 'intro'; |
| 1910 |
} |
| 1911 |
|
| 1912 |
/** |
| 1913 |
* Get pricing page URL. |
| 1914 |
* |
| 1915 |
* @param string $utm_content UTM content value identifying which link was clicked. |
| 1916 |
* @return string |
| 1917 |
*/ |
| 1918 |
private function get_url( $url, $utm_content = '' ) { |
| 1919 |
$args = array( |
| 1920 |
'src' => 'plugin-onboarding', |
| 1921 |
'utm_source' => 'propertyhive-plugin', |
| 1922 |
'utm_medium' => 'onboarding', |
| 1923 |
'utm_campaign' => 'setup-wizard', |
| 1924 |
); |
| 1925 |
|
| 1926 |
if ( '' !== $utm_content ) { |
| 1927 |
$args['utm_content'] = $utm_content; |
| 1928 |
} |
| 1929 |
|
| 1930 |
$url = add_query_arg( $args, 'https://wp-property-hive.com' . $url ); |
| 1931 |
|
| 1932 |
return apply_filters( 'propertyhive_onboarding_external_url', $url, $utm_content ); |
| 1933 |
} |
| 1934 |
} |
| 1935 |
|
| 1936 |
endif; |
| 1937 |
|
| 1938 |
return new PH_Admin_Onboarding(); |
| 1939 |
|