PluginProbe
Metricool – Social media and site statistics / trunk
Metricool – Social media and site statistics vtrunk
2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Features / Onboarding / Services / CreateAccountService.php

CreateAccountService.php in Metricool – Social media and site statistics trunk, at app/Features/Onboarding/Services/CreateAccountService.php

129 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Metricool\Features\Onboarding\Services;
4
5 use Metricool\Exceptions\RestDataException;
6 use Metricool\Features\Onboarding\Exceptions\CreateAccountException;
7 use Metricool\Features\Onboarding\OnboardingService;
8 use Metricool\Http\Metricool\Exceptions\ApiException;
9 use Metricool\Http\Metricool\MetricoolApi;
10 use Metricool\Http\RSPAL\RspalApiClient;
11 use Metricool\Services\DashboardService;
12 use Metricool\Support\Helpers\Storages\EnvironmentConfig;
13 use Throwable;
14
15 class CreateAccountService
16 {
17 private RspalApiClient $rspal;
18 private MetricoolApi $api;
19 private EnvironmentConfig $env;
20 private OnboardingService $onboarding;
21 private OAuthService $oauth;
22 private DashboardService $dashboard;
23
24 public function __construct(
25 RspalApiClient $rspal,
26 MetricoolApi $api,
27 EnvironmentConfig $env,
28 OnboardingService $onboarding,
29 OAuthService $oauth,
30 DashboardService $dashboard
31 ) {
32 $this->rspal = $rspal;
33 $this->api = $api;
34 $this->env = $env;
35 $this->onboarding = $onboarding;
36 $this->oauth = $oauth;
37 $this->dashboard = $dashboard;
38 }
39
40 /**
41 * Creates a new Metricool account and authenticate the MetricoolClient.
42 * @throws CreateAccountException
43 */
44 public function createAccount(string $captcha, string $email, string $password, bool $newsletters): bool
45 {
46 $globalError = sprintf(
47 /* translators: %1$s is opening link and %2$s is closing link */
48 __('Something went wrong. Please try again or %1$sleave a support message%2$s.', 'metricool'),
49 '<a href="' . $this->env->get('frontend.trusted_urls.new_support_ticket') . '" target="_blank">',
50 '</a>',
51 );
52
53 // First, check if the password is valid
54 if (!$this->isValidPassword($password)) {
55 throw new CreateAccountException(esc_html__('Password is not valid.', 'metricool'), 'Password is not valid', 422);
56 }
57
58 // Attempt to create the account
59 try {
60 $signupResponse = $this->rspal->signUp([
61 'username' => $email,
62 'newsletters' => $newsletters,
63 ], [
64 'RSPAL-RecaptchaV3Token' => $captcha
65 ]);
66 } catch (RestDataException $e) {
67 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), $e->getResponseCode());
68 }
69
70 // Parse the user ID from the access token
71 $userId = $this->oauth->parseUserIdFromAccessToken($signupResponse->data->accessToken);
72 if (empty($userId)) {
73 throw new CreateAccountException(wp_kses_post($globalError), 'Failed to parse user ID from access token', 500);
74 }
75
76 // Authenticate the Metricool API Client
77 $this->api->authenticate(
78 $userId,
79 (string) $signupResponse->data->accessToken,
80 (string) $signupResponse->data->refreshToken,
81 $signupResponse->data->expires ?? 300
82 );
83
84 // Update the user's password
85 try {
86 $this->api->userCredentials()
87 ->updatePassword('', $password);
88 } catch (ApiException $e) {
89 $this->api->logout();
90
91 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), 500);
92 }
93
94 // Attempt to automatically set the blog information, complete the onboarding process on success
95 try {
96 $this->onboarding->finalizeOnboarding($this->findConnectedBrandId());
97 } catch (Throwable $e) {
98 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), $e->getCode());
99 }
100
101 $this->dashboard->setShowWelcomeScreen();
102
103 return true;
104 }
105
106
107 /**
108 * Get the blogId from the API. This is needed to connect the brand and complete the onboarding process.
109 * @throws ApiException
110 */
111 private function findConnectedBrandId(): ?string
112 {
113 $brands = $this->api->brands()->all();
114
115 // Get the brand when there is only one, abort if there are more
116 $brand = (count($brands) === 1 ? (array) $brands[0] : []);
117
118 return $brand['id'] ?? null;
119 }
120
121 /**
122 * Check if the password is: between 8 and 20 characters long, contains at least one uppercase letter, one lowercase letter, one number, and one special character
123 */
124 protected function isValidPassword(string $password): bool
125 {
126 return (bool) preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{8,20}$/', $password);
127 }
128 }
129