From a4b57a93d6313c9d19b81888092a16f479ec228e Mon Sep 17 00:00:00 2001 From: Elena Filippova Date: Wed, 4 Sep 2024 13:16:23 +0300 Subject: [PATCH 1/3] Init weebly site builder --- src/LaravelServiceProvider.php | 2 + src/Providers/Weebly/Data/Configuration.php | 25 ++ src/Providers/Weebly/Helper/WeeblyApi.php | 247 ++++++++++++++++++ src/Providers/Weebly/Provider.php | 262 ++++++++++++++++++++ 4 files changed, 536 insertions(+) create mode 100644 src/Providers/Weebly/Data/Configuration.php create mode 100644 src/Providers/Weebly/Helper/WeeblyApi.php create mode 100644 src/Providers/Weebly/Provider.php diff --git a/src/LaravelServiceProvider.php b/src/LaravelServiceProvider.php index cf4019d..4f06b67 100644 --- a/src/LaravelServiceProvider.php +++ b/src/LaravelServiceProvider.php @@ -8,6 +8,7 @@ use Upmind\ProvisionProviders\WebsiteBuilders\Providers\Example\Provider as Example; use Upmind\ProvisionProviders\WebsiteBuilders\Providers\BaseKit\Provider as BaseKit; use Upmind\ProvisionProviders\WebsiteBuilders\Providers\Websitecom\Provider as Websitecom; +use Upmind\ProvisionProviders\WebsiteBuilders\Providers\Weebly\Provider as Weebly; class LaravelServiceProvider extends ProvisionServiceProvider { @@ -19,5 +20,6 @@ public function boot() $this->bindProvider('website-builders', 'base-kit', BaseKit::class); $this->bindProvider('website-builders', 'websitecom', Websitecom::class); + $this->bindProvider('website-builders', 'weebly', Weebly::class); } } diff --git a/src/Providers/Weebly/Data/Configuration.php b/src/Providers/Weebly/Data/Configuration.php new file mode 100644 index 0000000..1ab2ed4 --- /dev/null +++ b/src/Providers/Weebly/Data/Configuration.php @@ -0,0 +1,25 @@ + ['required', 'string'], + 'api_secret' => ['required', 'string'], + ]); + } +} diff --git a/src/Providers/Weebly/Helper/WeeblyApi.php b/src/Providers/Weebly/Helper/WeeblyApi.php new file mode 100644 index 0000000..3afeabb --- /dev/null +++ b/src/Providers/Weebly/Helper/WeeblyApi.php @@ -0,0 +1,247 @@ +client = $client; + $this->configuration = $configuration; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function makeRequest(string $command, ?array $params = null, ?array $body = null, ?string $method = 'GET'): ?array + { + $requestParams = []; + + $hashData = $method . "\n" . $command . "\n"; + + if ($body) { + $body = json_encode($body); + $requestParams['body'] = $body; + $hashData .= $body; + } + + $hash = hash_hmac('SHA256', $hashData, $this->configuration->api_secret); + $hash = base64_encode($hash); + + $requestParams['headers'] = [ + 'X-Signed-Request-Hash' => $hash + ]; + + $response = $this->client->request($method, $command, $requestParams); + $result = $response->getBody()->getContents(); + + $response->getBody()->close(); + + if ($result === "") { + return null; + } + + return $this->parseResponseData($result); + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + */ + private function parseResponseData(string $result): array + { + $parsedResult = json_decode($result, true); + + if (!$parsedResult) { + throw ProvisionFunctionError::create('Unknown Provider API Error') + ->withData([ + 'response' => $result, + ]); + } + + if ($error = $this->getResponseErrorMessage($parsedResult)) { + throw ProvisionFunctionError::create($error) + ->withData([ + 'response' => $parsedResult, + ]); + } + + return $parsedResult; + } + + protected function getResponseErrorMessage($responseData): ?string + { + if (isset($responseData['error'])) { + return $responseData['error']; + } + + return null; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function createUser(CreateParams $params): string + { + /** + * @var string $firstName + * @var string|null $lastName + */ + @[$firstName, $lastName] = explode(' ', $params->customer_name, 2); + + $body = [ + 'email' => $params->customer_email, + 'first_name' => $firstName, + 'last_name' => $lastName ?? 'UNKNOWN', + "language" => $params->language_code ?? 'en', + ]; + + if ($params->password != null) { + $body['password'] = $params->password; + } + + + $response = $this->makeRequest('user', null, $body, 'POST'); + return $response['user']['user_id']; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function getInfo(string $id, ?string $domain): array + { + $response = $this->makeRequest("user/$id/site"); + + if ($domain !== null) { + foreach ($response['sites'] as $s) { + if ($s['domain'] === $domain) { + $site = $s; + break; + } + } + } else { + if (count($response['sites']) > 0) { + $site = $response['sites'][0]; + } + } + + if ($domain && !isset($site)) { + throw ProvisionFunctionError::create("Domain $domain not found") + ->withData([ + 'response' => $response, + ]); + } + + $plan = '-'; + if (isset($site)) { + $siteId = $site['site_id']; + $plans = $this->makeRequest("user/$id/site/$siteId/plan"); + foreach ($plans['plans'] as $p) { + $plan = $p['plan_id']; + break; + } + } + + return [ + 'site_builder_user_id' => $id, + 'account_reference' => '-', + 'domain_name' => isset($site) ? $site['domain'] : null, + 'package_reference' => $plan, + 'suspended' => isset($site) ? $site['suspended'] : null, + 'ip_address' => null, + 'is_published' => isset($site) ? $site['publish_state'] == 'published' : null, + 'has_ssl' => isset($site) ? $site['allow_ssl'] : null, + 'site_count' => count($response['sites']) + ]; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function suspend(string $id): void + { + $this->makeRequest("user/$id/disable", null, null, 'POST'); + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function unsuspend(string $id): void + { + $this->makeRequest("user/$id/enable", null, null, 'POST'); + } + + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function changePackage(string $id, string $domain, string $packageId, ?int $term): void + { + if ($term && $term != 1 && $term != 12) { + throw ProvisionFunctionError::create('billing_cycle_months must be 1 or 12'); + } + + $siteId = $this->getSiteId($id, $domain); + $this->makeRequest("user/$id/site/$siteId/plan", null, ['plan_id' => $packageId, 'term' => $term ?? 1], 'POST'); + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function login(string $id): string + { + $response = $this->makeRequest("user/$id/loginLink"); + + return $response['link']; + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function terminate(string $id): void + { + $this->makeRequest("user/$id", null, null, 'DELETE'); + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + public function createDomain(string $userId, string $domain): void + { + $this->makeRequest("user/$userId/site", null, ['domain' => $domain], 'POST'); + } + + /** + * @param string $id + * @param string $domain + * @return void + */ + private function getSiteId(string $id, string $domain): ?string + { + $response = $this->makeRequest("user/$id/site"); + + foreach ($response['sites'] as $s) { + if ($s['domain'] === $domain) { + $siteId = $s['site_id']; + break; + } + } + + if (!isset($siteId)) { + throw ProvisionFunctionError::create("Domain $domain not found") + ->withData([ + 'response' => $response, + ]); + } + + return $siteId; + } +} diff --git a/src/Providers/Weebly/Provider.php b/src/Providers/Weebly/Provider.php new file mode 100644 index 0000000..10646e6 --- /dev/null +++ b/src/Providers/Weebly/Provider.php @@ -0,0 +1,262 @@ +configuration = $configuration; + } + + public static function aboutProvider(): AboutData + { + return AboutData::create() + ->setName('Weebly') + ->setDescription('Create, manage and log into Weebly site builder accounts') + ->setLogoUrl('https://api.upmind.io/images/logos/provision/weebly-logo@2x.png'); + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public function create(CreateParams $params): AccountInfo + { + try { + $userId = $this->api()->createUser($params); + } catch (\Throwable $e) { + $this->handleException($e, $params); + } + + try { + if ($params->domain_name != null) { + $this->api()->createDomain($userId, $params->domain_name); + $this->api()->changePackage($userId, $params->domain_name, $params->package_reference, (int)$params->billing_cycle_months); + } + + return $this->_getInfo($userId, null, 'Account data obtained'); + } catch (\Throwable $e) { + if (($e instanceof RequestException) && $e->hasResponse()) { + $response = $e->getResponse(); + + $body = trim($response === null ? '' : $response->getBody()->__toString()); + $responseData = json_decode($body, true); + + $errorMessage = $responseData["error"]['message'] ?? $response->getReasonPhrase(); + } + return $this->_getInfo($userId, null, $errorMessage ?? $e->getMessage()); + } + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public + function getInfo(AccountIdentifier $params): AccountInfo + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + return $this->_getInfo($params->site_builder_user_id, $params->domain_name, 'Account data obtained'); + } catch (\Throwable $e) { + $this->handleException($e, $params); + } + } + + /** + * @throws \GuzzleHttp\Exception\GuzzleException + */ + private + function _getInfo(string $id, ?string $site, string $message): AccountInfo + { + $accountInfo = $this->api()->getInfo($id, $site); + + return AccountInfo::create($accountInfo)->setMessage($message); + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public + function login(AccountIdentifier $params): LoginResult + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + $url = $this->api()->login($params->site_builder_user_id); + + return new LoginResult(['login_url' => $url]); + } catch (Throwable $e) { + $this->handleException($e); + } + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public function changePackage(ChangePackageParams $params): AccountInfo + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + if (!isset($params->domain_name)) { + $this->errorResult('Domain name is required!'); + } + + $this->api()->changePackage($params->site_builder_user_id, $params->domain_name, $params->package_reference, (int)$params->billing_cycle_months); + + return $this->_getInfo($params->site_builder_user_id, null, 'Package changed'); + } catch (Throwable $e) { + $this->handleException($e); + } + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public + function suspend(AccountIdentifier $params): AccountInfo + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + $this->api()->suspend($params->site_builder_user_id); + + return $this->_getInfo($params->site_builder_user_id, null, 'Account suspended'); + } catch (\Throwable $e) { + $this->handleException($e, $params); + } + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public + function unSuspend(UnSuspendParams $params): AccountInfo + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + $this->api()->unsuspend($params->site_builder_user_id); + + return $this->_getInfo($params->site_builder_user_id, null, 'Account unsuspended'); + } catch (\Throwable $e) { + $this->handleException($e, $params); + } + } + + /** + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + public + function terminate(AccountIdentifier $params): ResultData + { + try { + if (!isset($params->site_builder_user_id)) { + $this->errorResult('User id is required!'); + } + + $this->api()->terminate($params->site_builder_user_id); + + return $this->okResult('Account Terminated'); + } catch (\Throwable $e) { + $this->handleException($e, $params); + } + } + + /** + * @return no-return + * + * @throws \Upmind\ProvisionBase\Exception\ProvisionFunctionError + * @throws \Throwable + */ + protected + function handleException(\Throwable $e, $params = null): void + { + if (($e instanceof RequestException) && $e->hasResponse()) { + $response = $e->getResponse(); + + $body = trim($response === null ? '' : $response->getBody()->__toString()); + $responseData = json_decode($body, true); + + $errorMessage = $responseData["error"]['message'] ?? $response->getReasonPhrase(); + + $this->errorResult( + sprintf('Provider API Error: %s', $errorMessage), + ['response_data' => $responseData], + [], + $e + ); + } + + throw $e; + } + + public + function api(): WeeblyApi + { + if (isset($this->api)) { + return $this->api; + } + + $client = new Client([ + 'base_uri' => 'https://api.weeblycloud.com/', + RequestOptions::HEADERS => [ + 'User-Agent' => 'upmind/provision-provider-website-builders v1.0', + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + 'X-Public-Key' => $this->configuration->api_key, + ], + RequestOptions::TIMEOUT => 30, // seconds + RequestOptions::CONNECT_TIMEOUT => 5, // seconds + 'handler' => $this->getGuzzleHandlerStack() + ]); + + return $this->api = new WeeblyApi($client, $this->configuration); + } +} From 66f68a5cfa081ace932443a6527a35c56c8e1e36 Mon Sep 17 00:00:00 2001 From: Elena Filippova Date: Wed, 4 Sep 2024 13:32:12 +0300 Subject: [PATCH 2/3] Code refactoring --- src/Providers/Weebly/Helper/WeeblyApi.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Providers/Weebly/Helper/WeeblyApi.php b/src/Providers/Weebly/Helper/WeeblyApi.php index 3afeabb..e7f2435 100644 --- a/src/Providers/Weebly/Helper/WeeblyApi.php +++ b/src/Providers/Weebly/Helper/WeeblyApi.php @@ -91,10 +91,6 @@ protected function getResponseErrorMessage($responseData): ?string */ public function createUser(CreateParams $params): string { - /** - * @var string $firstName - * @var string|null $lastName - */ @[$firstName, $lastName] = explode(' ', $params->customer_name, 2); $body = [ @@ -222,9 +218,9 @@ public function createDomain(string $userId, string $domain): void /** * @param string $id * @param string $domain - * @return void + * @return string */ - private function getSiteId(string $id, string $domain): ?string + private function getSiteId(string $id, string $domain): string { $response = $this->makeRequest("user/$id/site"); From 05be5680ffbe611908c036c5c11a207a03841d21 Mon Sep 17 00:00:00 2001 From: Elena Filippova Date: Wed, 4 Sep 2024 13:49:41 +0300 Subject: [PATCH 3/3] Code refactoring --- src/Providers/Weebly/Helper/WeeblyApi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Providers/Weebly/Helper/WeeblyApi.php b/src/Providers/Weebly/Helper/WeeblyApi.php index e7f2435..1f41b41 100644 --- a/src/Providers/Weebly/Helper/WeeblyApi.php +++ b/src/Providers/Weebly/Helper/WeeblyApi.php @@ -96,7 +96,7 @@ public function createUser(CreateParams $params): string $body = [ 'email' => $params->customer_email, 'first_name' => $firstName, - 'last_name' => $lastName ?? 'UNKNOWN', + 'last_name' => $lastName, "language" => $params->language_code ?? 'en', ];