PluginProbe
Metricool – Social media and site statistics / 2.1.1
Metricool – Social media and site statistics v2.1.1
2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
← All changes | app/Features/Onboarding/OnboardingController.php +38 -28 2.0.2 → 2.1.1 View file →
@@ -7,10 +7,12 @@
7 7 if (!defined('ABSPATH')) {
8 8 exit;
9 9 }
10 10
11 +use Metricool\Support\Helpers\Storages\RequestStorage;
11 12 use Metricool\Traits\HasRestAccess;
12 13 use Metricool\Services\DashboardService;
14 +use Metricool\Support\Validation\Validator;
13 15 use Metricool\Interfaces\FeatureInterface;
14 16 use Metricool\Services\MetricoolAccountService;
15 17 use Metricool\Features\Onboarding\Services\OAuthService;
16 18 use Metricool\Support\Helpers\Storages\EnvironmentConfig;
@@ -28,8 +30,9 @@
28 30 private CreateAccountService $accounts;
29 31 private OAuthService $oauth;
30 32 private DashboardService $dashboard;
31 33 private MetricoolAccountService $account;
34 + private RequestStorage $request;
32 35
33 36 public function __construct(
34 37 OnboardingService $onboarding,
35 38 CreateAccountService $accounts,
@@ -35,9 +38,10 @@
35 38 CreateAccountService $accounts,
36 39 EnvironmentConfig $env,
37 40 OAuthService $oauth,
38 41 DashboardService $dashboard,
39 - MetricoolAccountService $account
42 + MetricoolAccountService $account,
43 + RequestStorage $request
40 44 ) {
41 45 $this->onboarding = $onboarding;
42 46 $this->accounts = $accounts;
43 47 $this->env = $env;
@@ -43,13 +47,17 @@
43 47 $this->env = $env;
44 48 $this->oauth = $oauth;
45 49 $this->dashboard = $dashboard;
46 50 $this->account = $account;
51 + $this->request = $request;
47 52 }
48 53
49 54 public function register(): void
50 55 {
51 56 add_filter('metricool_rest_routes', [$this, 'addRoutes']);
57 +
58 + // Intercept OAuth callback on the dashboard page
59 + add_action('load-toplevel_page_metricool', [$this, 'oauthCallback']);
52 60 }
53 61
54 62 /**
55 63 * Add onboarding routes to the existing routes of our plugin
@@ -67,16 +75,11 @@
67 75 ];
68 76
69 77 $routes['onboarding/oauth_redirect'] = [
70 78 'methods' => 'GET',
71 - 'callback' => [$this, 'oauthRedirect'],
79 + 'callback' => [$this, 'getOauthRedirectUrl'],
72 80 ];
73 81
74 - $routes['onboarding/oauth_callback'] = [
75 - 'methods' => 'GET',
76 - 'callback' => [$this, 'oauthCallback'],
77 - ];
78 -
79 82 return $routes;
80 83 }
81 84
82 85 /**
@@ -84,26 +87,24 @@
84 87 * automatically.
85 88 */
86 89 public function createAccount(\WP_REST_Request $request): \WP_REST_Response
87 90 {
88 - $email = (string) $request->get_param('email');
89 - $password = (string) $request->get_param('password');
90 - $marketing = (bool) $request->get_param('marketing');
91 - $terms = (bool) $request->get_param('terms');
92 - $captcha = (string) $request->get_param('captcha');
91 + $validated = Validator::validate($request->get_params(), [
92 + 'email' => 'required|email',
93 + 'password' => 'required|string',
94 + 'captcha' => 'required|string',
95 + 'terms' => 'accepted',
96 + 'marketing' => 'boolean',
97 + ]);
93 98
94 - // Validate fields
95 - if (!is_email($email) || empty($password) || empty($captcha) || !$terms) {
96 - return $this->sendHttpErrorResponse(
97 - __('Validation failed.', 'metricool'),
98 - [],
99 - 422
100 - );
101 - }
102 -
103 99 // Attempt to create the account
104 100 try {
105 - $this->accounts->createAccount($captcha, $email, $password, $marketing);
101 + $this->accounts->createAccount(
102 + $validated['captcha'],
103 + $validated['email'],
104 + $validated['password'],
105 + rest_sanitize_boolean($validated['marketing'] ?? false)
106 + );
106 107 } catch (CreateAccountException $e) {
107 108 return $this->sendHttpErrorResponse($e->getMessage(), ['reason' => $e->getReason()], $e->getCode());
108 109 }
109 110
@@ -125,9 +126,13 @@
125 126 // Store the blogId if it was provided by the client, to retrieve the necessary blog information
126 127 try {
127 128 $this->onboarding->finalizeOnboarding($blogId);
128 129 } catch (BrandAccessDeniedException $e) {
129 - return $this->sendHttpErrorResponse(__('Could not retrieve brand. Pick another brand, try again or contact support.', 'metricool'), [], 403);
130 + return $this->sendHttpErrorResponse(
131 + __('You don’t have sufficient permissions to connect this brand. Please pick another brand or sign in with a different account.', 'metricool'),
132 + [],
133 + 403
134 + );
130 135 } catch (Throwable $e) {
131 136 return $this->sendHttpErrorResponse(wp_kses_post(sprintf(
132 137 /* translators: %1$s is opening link and %2$s is closing link */
133 138 __('Something went wrong. Please try again or %1$sleave a support message%2$s.', 'metricool'),
@@ -141,11 +146,10 @@
141 146
142 147 /**
143 148 * Build and return the Metricool OAuth authorize URL.
144 149 */
145 - public function oauthRedirect(\WP_REST_Request $request): \WP_REST_Response
150 + public function getOauthRedirectUrl(\WP_REST_Request $request): \WP_REST_Response
146 151 {
147 - // Check if authorizationUrl is a secure URL, otherwise redirect to the dashboard
148 152 if (is_ssl() === false) {
149 153 return $this->sendHttpErrorResponse(__('HTTPS is required to be able to authorize this website.', 'metricool'), [], 400);
150 154 }
151 155
@@ -157,12 +161,18 @@
157 161 /**
158 162 * Handle the OAuth callback from Metricool. Exchanges the authorization
159 163 * code for tokens, authenticates the user, and redirects to the dashboard.
160 164 */
161 - public function oauthCallback(\WP_REST_Request $request): \WP_REST_Response
165 + public function oauthCallback(): void
162 166 {
163 - $code = (string) $request->get_param('code');
164 - $state = (string) $request->get_param('state');
167 + $action = $this->request->getString('global.metricool_action');
168 +
169 + if ($action !== OAuthService::REDIRECT_ACTION) {
170 + return;
171 + }
172 +
173 + $code = $this->request->getString('global.code');
174 + $state = $this->request->getString('global.state');
165 175
166 176 try {
167 177 $this->oauth->authenticateWithCode($code, $state);
168 178 } catch (Throwable $e) {