PluginProbe
Tiered Pricing Table for WooCommerce / 8.0.2
Tiered Pricing Table for WooCommerce v8.0.2
8.0.2 7.1.7 7.1.5 7.1.4 7.1.1 6.5.0 6.4.0 6.1.0 trunk 1.0 1.1 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.2.3 2.3.0 2.3.1 2.3.2 2.3.3 All 91 releases
tier-pricing-table / src / Addons / NonLoggedInUsers / Wholesale / Frontend / RegistrationForm.php

RegistrationForm.php in Tiered Pricing Table for WooCommerce 8.0.2, at src/Addons/NonLoggedInUsers/Wholesale/Frontend/RegistrationForm.php

195 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php namespace TierPricingTable\Addons\NonLoggedInUsers\Wholesale\Frontend;
2
3 use TierPricingTable\Addons\NonLoggedInUsers\Wholesale\Models\WholesaleApplication;
4 use TierPricingTable\Addons\NonLoggedInUsers\Wholesale\Services\ApplicationService;
5 use TierPricingTable\Addons\NonLoggedInUsers\Wholesale\Settings\Settings;
6 use TierPricingTable\Core\ServiceContainer;
7 use TierPricingTable\TierPricingTablePlugin;
8 use WP_Error;
9
10 /**
11 * The [tiered_pricing_wholesale_registration] shortcode: renders the application form (or the
12 * applicant's status) and handles its submission.
13 */
14 class RegistrationForm {
15
16 const SHORTCODE = 'tiered_pricing_wholesale_registration';
17 const NONCE = 'tpt_wholesale_register';
18 const QUERY_VAR = 'tpt-wholesale';
19
20 protected ?WP_Error $errors = null;
21
22 protected array $values = array();
23
24 protected bool $rendered = false;
25
26 public function __construct() {
27 add_shortcode( self::SHORTCODE, array( $this, 'render' ) );
28 add_action( 'template_redirect', array( $this, 'maybeHandleSubmission' ) );
29 add_action( 'wp_enqueue_scripts', array( $this, 'registerAssets' ) );
30 }
31
32 public function registerAssets() {
33 wp_register_style( 'tpt-wholesale-registration', plugins_url( '../assets/css/wholesale-registration.css', __FILE__ ), array(), TierPricingTablePlugin::VERSION );
34 }
35
36 /**
37 * Processes the posted form before any output, so the result can be a redirect.
38 */
39 public function maybeHandleSubmission() {
40 if ( empty( $_POST['tpt_wholesale_action'] ) || 'register' !== $_POST['tpt_wholesale_action'] ) {
41 return;
42 }
43
44 if ( ! isset( $_POST[ self::NONCE ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE ] ) ), self::NONCE ) ) {
45 $this->errors = new WP_Error( 'nonce', __( 'The form has expired. Please try again.', 'tier-pricing-table' ) );
46
47 return;
48 }
49
50 $input = wp_unslash( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified above; the validator sanitizes every value it uses
51
52 if ( ! $this->verifyRecaptcha( $input ) ) {
53 $this->errors = new WP_Error( 'recaptcha', __( 'The spam check failed. Please try again.', 'tier-pricing-table' ) );
54 $this->values = $input;
55
56 return;
57 }
58
59 $currentUser = is_user_logged_in() ? wp_get_current_user() : null;
60 $result = ( new ApplicationService() )->submit( $input, $currentUser );
61
62 if ( is_wp_error( $result ) ) {
63 $this->errors = $result;
64 $this->values = $input;
65
66 return;
67 }
68
69 $outcome = $result->isApproved() ? 'approved' : 'submitted';
70
71 if ( $result->isApproved() && ! $currentUser && $result->getUserId() ) {
72 wc_set_customer_auth_cookie( $result->getUserId() );
73 }
74
75 wp_safe_redirect( add_query_arg( self::QUERY_VAR, $outcome, $this->currentUrl() ) );
76 exit;
77 }
78
79 public function render( $atts = array() ): string {
80 $this->rendered = true;
81
82 wp_enqueue_style( 'tpt-wholesale-registration' );
83
84 $user = is_user_logged_in() ? wp_get_current_user() : null;
85 $application = $user ? WholesaleApplication::findForUser( $user->ID ) : null;
86 $outcome = isset( $_GET[ self::QUERY_VAR ] ) ? sanitize_key( $_GET[ self::QUERY_VAR ] ) : '';
87 $fileManager = ServiceContainer::getInstance()->getFileManager();
88 $viewsPath = plugin_dir_path( dirname( __FILE__ ) ) . 'views/';
89
90 ob_start();
91
92 if ( $user && ApplicationService::isWholesaleUser( $user ) ) {
93 $fileManager->includeTemplate( 'frontend/application-status.php', array(
94 'status' => 'approved',
95 'message' => Settings::getApprovedMessage(),
96 'user' => $user,
97 ), $viewsPath );
98 } elseif ( ( $application && $application->isPending() ) || 'submitted' === $outcome ) {
99 $fileManager->includeTemplate( 'frontend/application-status.php', array(
100 'status' => 'pending',
101 'message' => Settings::getPendingMessage(),
102 'user' => $user,
103 ), $viewsPath );
104 } elseif ( $application && $application->isRejected() ) {
105 $fileManager->includeTemplate( 'frontend/application-status.php', array(
106 'status' => 'rejected',
107 'message' => Settings::getRejectedMessage(),
108 'user' => $user,
109 ), $viewsPath );
110 } else {
111 $this->enqueueRecaptcha();
112
113 $fileManager->includeTemplate( 'frontend/registration-form.php', array(
114 'user' => $user,
115 'fields' => Settings::getFormFields(),
116 'needsPassword' => null === $user && ApplicationService::needsPassword(),
117 'termsPageId' => Settings::getTermsPageId(),
118 'errors' => $this->errors,
119 'values' => $this->values,
120 'title' => Settings::getFormTitle(),
121 'intro' => Settings::getFormIntro(),
122 'submitText' => Settings::getSubmitText(),
123 'recaptchaKey' => Settings::useRecaptcha() ? Settings::getRecaptchaKeys()['site'] : '',
124 'loginUrl' => wp_login_url( $this->currentUrl() ),
125 'form' => $this,
126 ), $viewsPath );
127 }
128
129 return (string) ob_get_clean();
130 }
131
132 /**
133 * The posted value of a field, for re-filling the form after a validation error.
134 */
135 public function value( string $field ): string {
136 return isset( $this->values[ $field ] ) && is_scalar( $this->values[ $field ] ) ? (string) $this->values[ $field ] : '';
137 }
138
139 public function error( string $field ): string {
140 return $this->errors instanceof WP_Error ? (string) $this->errors->get_error_message( $field ) : '';
141 }
142
143 /**
144 * The URL of the page the form is on, without the outcome flag.
145 */
146 protected function currentUrl(): string {
147 if ( is_singular() && get_queried_object_id() ) {
148 $url = (string) get_permalink( get_queried_object_id() );
149 } else {
150 // the request URI already contains the site's path, so it is combined with the host only
151 $host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : (string) wp_parse_url( home_url(), PHP_URL_HOST );
152 $path = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/';
153 $url = ( is_ssl() ? 'https://' : 'http://' ) . $host . $path;
154 }
155
156 return remove_query_arg( self::QUERY_VAR, esc_url_raw( $url ) );
157 }
158
159 protected function enqueueRecaptcha() {
160 if ( ! Settings::useRecaptcha() ) {
161 return;
162 }
163
164 wp_enqueue_script( 'google-recaptcha-v3', 'https://www.google.com/recaptcha/api.js?render=' . rawurlencode( Settings::getRecaptchaKeys()['site'] ), array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
165 }
166
167 protected function verifyRecaptcha( array $input ): bool {
168 if ( ! Settings::useRecaptcha() ) {
169 return true;
170 }
171
172 $token = isset( $input['g-recaptcha-response'] ) ? sanitize_text_field( $input['g-recaptcha-response'] ) : '';
173
174 if ( '' === $token ) {
175 return false;
176 }
177
178 $response = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', array(
179 'timeout' => 10,
180 'body' => array(
181 'secret' => Settings::getRecaptchaKeys()['secret'],
182 'response' => $token,
183 ),
184 ) );
185
186 if ( is_wp_error( $response ) ) {
187 return false;
188 }
189
190 $body = json_decode( (string) wp_remote_retrieve_body( $response ), true );
191
192 return ! empty( $body['success'] ) && (float) ( $body['score'] ?? 0 ) >= 0.5;
193 }
194 }
195