diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..775f18f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.php_cs.cache \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 244806f..e5e32a0 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -6,7 +6,7 @@ $finder = PhpCsFixer\Finder::create() $config = new PhpCsFixer\Config(); return $config->setRules([ - '@PHP70Migration' => true, + '@PHP71Migration' => true, '@PSR12' => true ]) ->setFinder($finder) diff --git a/CHANGELOG.md b/CHANGELOG.md index 680c5a4..9430e5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,30 @@ # Changelog +All notable changes to this project will be documented in this file. ---- +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 1.0.0.0 (2022-07-13) +## [Unreleased](https://git.d3data.de/D3Private/linkmobility4oxid/compare/1.1.0.1...rel_1.x) +## [1.1.0.1](https://git.d3data.de/D3Private/linkmobility4oxid/compare/1.1.0.0...1.1.0.1) - 2022-09-29 +### Changed +- adjust readme + +## [1.1.0.0](https://git.d3data.de/D3Private/linkmobility4oxid/compare/1.0.0.0...1.1.0.0) - 2022-07-28 +### Added +- phpstan code checks + +### Changed +- improved changelog + +### Fixed +- type in IntelliSenseHelper class name + +### Removed +- PHP 7.0 support + +## [1.0.0.0](https://git.d3data.de/D3Private/linkmobility4oxid/releases/tag/1.0.0.0) - 2022-07-13 +### Added - initial implementation Send message on: - Order completion, sending order confirmation message. diff --git a/README.en.md b/README.en.md index ef366ca..09a6aed 100644 --- a/README.en.md +++ b/README.en.md @@ -1,7 +1,7 @@ [![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md) [![english version](https://logos.oxidmodule.com/en2_xs.svg)](README.en.md) -# Integration of the LINK Mobility Austria service into the OXID eShop +# Integration of the LINK Mobility service into the OXID eShop [LINK Mobility](https://www.linkmobility.de/) provides a service for sending mobile messages (SMS, Whatsapp, RCS, Chatbot, ...). @@ -25,8 +25,9 @@ Message dispatch (currently SMS) can be activated individually for the following This package requires an OXID eShop installed with Composer in one of the following versions: - 6.2.4 or above -- 6.3.x +- 6.3.1 or above - 6.4.x +- 6.5.x and its requirements. @@ -54,6 +55,12 @@ If you have a suggestion that would make this better, please fork the repo and c - Push to the Branch (git push origin feature/AmazingFeature) - Open a Pull Request +## Support + +If you have any questions about the *messaging service* and its *contracts*, please contact the [LINK Mobility Team](https://www.linkmobility.de/kontakt). + +For *technical inquiries* you will find the contact options in the [composer.json](composer.json). + ## License (status: 2022-07-13) diff --git a/README.md b/README.md index 4eb2aa8..6f13f5b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![deutsche Version](https://logos.oxidmodule.com/de2_xs.svg)](README.md) [![english version](https://logos.oxidmodule.com/en2_xs.svg)](README.en.md) -# Integration des LINK Mobility Austria Dienstes in den OXID eShop +# Integration des LINK Mobility Dienstes in den OXID eShop [LINK Mobility](https://www.linkmobility.de/) stellt einen Service zum Versenden von mobilen Nachrichten (SMS, Whatsapp, RCS, Chatbot, ...) zur Verfügung. @@ -25,8 +25,9 @@ Bei folgenden Aktionen kann der Nachrichtenversand (derzeit SMS) einzeln aktivie Dieses Paket erfordert einen mit Composer installierten OXID eShop in einer der folgenden Versionen: - 6.2.4 oder höher -- 6.3.x +- 6.3.1 oder höher - 6.4.x +- 6.5.x und dessen Anforderungen. @@ -54,6 +55,12 @@ Wenn Sie eine Verbesserungsvorschlag haben, legen Sie einen Fork des Repositorie - Übertragen Sie den Branch (git push origin feature/AmazingFeature) - Öffnen Sie einen Pull Request +## Support + +Bei Fragen zum *Messaging Service* und dessen *Verträgen* kontaktieren Sie bitte das [LINK Mobility Team](https://www.linkmobility.de/kontakt). + +Zu *technischen Anfragen* finden Sie die Kontaktmöglichkeiten in der [composer.json](composer.json). + ## Lizenz (Stand: 13.07.2022) diff --git a/composer.json b/composer.json index c9b9f32..d16a1e2 100644 --- a/composer.json +++ b/composer.json @@ -17,10 +17,18 @@ "role": "Owner" } ], + "license": [ + "GPL-3.0-or-later" + ], "require": { - "php": "^7.0 || ^8.0", - "oxid-esales/oxideshop-ce": "6.7 - 6.10", - "d3/linkmobility-php-client": "^1.2.0" + "php": "^7.1 || ^8.0", + "oxid-esales/oxideshop-ce": "6.7 - 6.13", + "d3/linkmobility-php-client": "^1.2.0 || ^2.0.0" + }, + "require-dev": { + "oxid-esales/oxideshop-ce": "~6.10.0", + "friendsofphp/php-cs-fixer": "^2.19", + "phpstan/phpstan": "^1.8" }, "require-dev": { "d3/modcfg": "^5.3.7.0 || ^6.0" diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..6c8b9a1 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,10 @@ +parameters: + scanFiles: + - src/IntelliSenseHelper.php + - ../../oxid-esales/oxideshop-ce/source/oxfunctions.php + paths: + - src + level: 9 + phpVersion: 70100 + ignoreErrors: + - '#Psr\\Container\\ContainerExceptionInterface is not subtype of Throwable#' \ No newline at end of file diff --git a/src/Application/Controller/Admin/AdminOrder.php b/src/Application/Controller/Admin/AdminOrder.php index 7f276c1..d9d70c3 100644 --- a/src/Application/Controller/Admin/AdminOrder.php +++ b/src/Application/Controller/Admin/AdminOrder.php @@ -58,24 +58,25 @@ class AdminOrder extends AdminController try { return oxNew(OrderRecipients::class, $this->order)->getSmsRecipient(); } catch (noRecipientFoundException $e) { - Registry::getUtilsView()->addErrorToDisplay( - Registry::getLang()->translateString($e->getMessage()) - ); + /** @var string $message */ + $message = Registry::getLang()->translateString($e->getMessage()); + Registry::getUtilsView()->addErrorToDisplay($message); } return false; } /** + * @return void * @throws Exception */ - public function send() + public function send(): void { $messageBody = Registry::getRequest()->getRequestEscapedParameter('messagebody'); if (false === is_string($messageBody) || strlen($messageBody) <= 1) { - Registry::getUtilsView()->addErrorToDisplay( - Registry::getLang()->translateString('D3LM_EXC_MESSAGE_NO_LENGTH') - ); + /** @var string $message */ + $message = Registry::getLang()->translateString('D3LM_EXC_MESSAGE_NO_LENGTH'); + Registry::getUtilsView()->addErrorToDisplay($message); return; } @@ -85,17 +86,15 @@ class AdminOrder extends AdminController try { $sms = oxNew(Sms::class, $messageBody); if ($sms->sendOrderMessage($order)) { + $smsCount = $sms->getResponse() ? $sms->getResponse()->getSmsCount() : 0; Registry::getUtilsView()->addErrorToDisplay( - oxNew(successfullySentException::class, $sms->getResponse()->getSmsCount()) + oxNew(successfullySentException::class, $smsCount) ); } else { $errorMsg = $sms->getResponse() instanceof ResponseInterface ? $sms->getResponse()->getErrorMessage() : 'no response'; - Registry::getUtilsView()->addErrorToDisplay( - sprintf( - Registry::getLang()->translateString('D3LM_EXC_MESSAGE_UNEXPECTED_ERR_SEND'), - $errorMsg - ) - ); + /** @var string $format */ + $format = Registry::getLang()->translateString('D3LM_EXC_MESSAGE_UNEXPECTED_ERR_SEND'); + Registry::getUtilsView()->addErrorToDisplay(sprintf($format, $errorMsg)); } } catch (noRecipientFoundException $e) { Registry::getUtilsView()->addErrorToDisplay($e); diff --git a/src/Application/Controller/Admin/AdminUser.php b/src/Application/Controller/Admin/AdminUser.php index e4a27b6..3aba0fb 100644 --- a/src/Application/Controller/Admin/AdminUser.php +++ b/src/Application/Controller/Admin/AdminUser.php @@ -57,24 +57,26 @@ class AdminUser extends AdminController try { return oxNew(UserRecipients::class, $this->user)->getSmsRecipient(); } catch (noRecipientFoundException $e) { - Registry::getUtilsView()->addErrorToDisplay( - Registry::getLang()->translateString($e->getMessage()) - ); + /** @var string $message */ + $message = Registry::getLang()->translateString($e->getMessage()); + Registry::getUtilsView()->addErrorToDisplay($message); } return false; } /** + * @return void * @throws Exception */ - public function send() + public function send(): void { + /** @var string $messageBody */ $messageBody = Registry::getRequest()->getRequestEscapedParameter('messagebody'); if (strlen($messageBody) <= 1) { - Registry::getUtilsView()->addErrorToDisplay( - Registry::getLang()->translateString('D3LM_EXC_MESSAGE_NO_LENGTH') - ); + /** @var string $message */ + $message = Registry::getLang()->translateString('D3LM_EXC_MESSAGE_NO_LENGTH'); + Registry::getUtilsView()->addErrorToDisplay($message); return; } @@ -83,20 +85,15 @@ class AdminUser extends AdminController $sms = oxNew(Sms::class, $messageBody); if ($sms->sendUserAccountMessage($user)) { - Registry::getUtilsView()->addErrorToDisplay( - sprintf( - Registry::getLang()->translateString('D3LM_EXC_SMS_SUCC_SENT'), - $sms->getResponse()->getSmsCount() - ) - ); + /** @var string $format */ + $format = Registry::getLang()->translateString('D3LM_EXC_SMS_SUCC_SENT'); + $smsCount = $sms->getResponse() ? $sms->getResponse()->getSmsCount() : 0; + Registry::getUtilsView()->addErrorToDisplay(sprintf($format, $smsCount)); } else { $errorMsg = $sms->getResponse() instanceof ResponseInterface ? $sms->getResponse()->getErrorMessage() : 'no response'; - Registry::getUtilsView()->addErrorToDisplay( - sprintf( - Registry::getLang()->translateString('D3LM_EXC_MESSAGE_UNEXPECTED_ERR_SEND'), - $errorMsg - ) - ); + /** @var string $format */ + $format = Registry::getLang()->translateString('D3LM_EXC_MESSAGE_UNEXPECTED_ERR_SEND'); + Registry::getUtilsView()->addErrorToDisplay(sprintf($format, $errorMsg)); } } } diff --git a/src/Application/Model/Configuration.php b/src/Application/Model/Configuration.php index d611e1c..1e46abb 100644 --- a/src/Application/Model/Configuration.php +++ b/src/Application/Model/Configuration.php @@ -16,18 +16,36 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Application\Model; use Assert\Assert; +use OxidEsales\Eshop\Application\Model\Order; +use OxidEsales\Eshop\Application\Model\User; use OxidEsales\Eshop\Core\Registry; class Configuration { - public const DEBUG = "d3linkmobility_debug"; + public const GENERAL_APITOKEN = "d3linkmobility_apitoken"; + public const GENERAL_DEBUG = "d3linkmobility_debug"; + + public const ORDER_RECFIELDS = "d3linkmobility_smsOrderRecipientsFields"; + public const USER_RECFIELDS = "d3linkmobility_smsUserRecipientsFields"; + + public const SMS_SENDERNR = "d3linkmobility_smsSenderNumber"; + public const SMS_SENDERCOUNTRY = "d3linkmobility_smsSenderCountry"; + + public const SENDBY_ORDERED = "d3linkmobility_orderActive"; + public const SENDBY_SENDEDNOW = "d3linkmobility_sendedNowActive"; + public const SENDBY_CANCELED = "d3linkmobility_cancelOrderActive"; + + public const ARGS_CHECKKEYS = "checkKeys"; + public const ARGS_CHECKCLASS = "checkClassName"; /** * @return string */ public function getApiToken(): string { - $token = trim((string) Registry::getConfig()->getConfigParam('d3linkmobility_apitoken')); + /** @var string $token */ + $token = Registry::getConfig()->getConfigParam(self::GENERAL_APITOKEN); + $token = trim($token); Assert::that($token)->string()->notEmpty(); @@ -39,7 +57,7 @@ class Configuration */ public function getTestMode(): bool { - return (bool) Registry::getConfig()->getConfigParam(self::DEBUG); + return (bool) Registry::getConfig()->getConfigParam(self::GENERAL_DEBUG); } /** @@ -47,7 +65,9 @@ class Configuration */ public function getSmsSenderNumber() { - $number = trim(Registry::getConfig()->getConfigParam('d3linkmobility_smsSenderNumber')); + /** @var string $number */ + $number = Registry::getConfig()->getConfigParam(self::SMS_SENDERNR); + $number = trim($number); return strlen($number) ? $number : null; } @@ -55,13 +75,102 @@ class Configuration /** * @return string|null */ - public function getSmsSenderCountry(): string + public function getSmsSenderCountry(): ?string { - $country = trim(Registry::getConfig()->getConfigParam('d3linkmobility_smsSenderCountry')); + /** @var string $country */ + $country = Registry::getConfig()->getConfigParam(self::SMS_SENDERCOUNTRY); + $country = trim($country); $country = strlen($country) ? strtoupper($country) : null; Assert::that($country)->nullOr()->string()->length(2); return $country; } + + /** + * @return string[] + */ + public function getOrderRecipientFields(): array + { + /** @var string[] $customFields */ + $customFields = Registry::getConfig()->getConfigParam(self::ORDER_RECFIELDS) ?: []; + + array_walk( + $customFields, + [$this, 'checkFieldExists'], + [self::ARGS_CHECKKEYS => true, self::ARGS_CHECKCLASS => Order::class] + ); + $customFields = array_filter($customFields); + + Assert::that($customFields)->isArray(); + + return $customFields; + } + + /** + * @return string[] + */ + public function getUserRecipientFields(): array + { + /** @var string[] $customFields */ + $customFields = Registry::getConfig()->getConfigParam(self::USER_RECFIELDS) ?: []; + + array_walk( + $customFields, + [$this, 'checkFieldExists'], + [self::ARGS_CHECKKEYS => false, self::ARGS_CHECKCLASS => User::class] + ); + $customFields = array_filter($customFields); + + Assert::that($customFields)->isArray(); + + return $customFields; + } + + /** + * @template T + * @param string $checkPhoneFieldName + * @param string $checkCountryFieldName + * @param array{checkKeys: bool, checkClassName: class-string} $args + * @return void + */ + protected function checkFieldExists(string &$checkPhoneFieldName, string $checkCountryFieldName, array $args): void + { + $checkCountryFieldName = $args[self::ARGS_CHECKKEYS] ? trim($checkCountryFieldName) : $checkCountryFieldName; + $checkPhoneFieldName = trim($checkPhoneFieldName); + $allFieldNames = oxNew($args[self::ARGS_CHECKCLASS])->getFieldNames() ?: []; + + array_walk($allFieldNames, function (&$value) { + $value = strtolower($value); + }); + + $checkPhoneFieldName = in_array(strtolower($checkPhoneFieldName), $allFieldNames) && ( + false === $args[self::ARGS_CHECKKEYS] || + in_array(strtolower($checkCountryFieldName), $allFieldNames) + ) ? $checkPhoneFieldName : null; + } + + /** + * @return bool + */ + public function sendOrderFinishedMessage(): bool + { + return (bool) Registry::getConfig()->getConfigParam(self::SENDBY_ORDERED); + } + + /** + * @return bool + */ + public function sendOrderSendedNowMessage(): bool + { + return (bool) Registry::getConfig()->getConfigParam(self::SENDBY_SENDEDNOW); + } + + /** + * @return bool + */ + public function sendOrderCanceledMessage(): bool + { + return (bool) Registry::getConfig()->getConfigParam(self::SENDBY_CANCELED); + } } diff --git a/src/Application/Model/Exceptions/abortSendingExceptionInterface.php b/src/Application/Model/Exceptions/abortSendingExceptionInterface.php index 1c3e20d..6c4f2a4 100644 --- a/src/Application/Model/Exceptions/abortSendingExceptionInterface.php +++ b/src/Application/Model/Exceptions/abortSendingExceptionInterface.php @@ -15,6 +15,6 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Application\Model\Exceptions; -interface abortSendingExceptionInterface +interface abortSendingExceptionInterface extends \Throwable { } diff --git a/src/Application/Model/Exceptions/successfullySentException.php b/src/Application/Model/Exceptions/successfullySentException.php index a219dd8..9a37909 100644 --- a/src/Application/Model/Exceptions/successfullySentException.php +++ b/src/Application/Model/Exceptions/successfullySentException.php @@ -16,19 +16,21 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Application\Model\Exceptions; use Exception; +use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Registry; -use Throwable; -class successfullySentException extends Exception +class successfullySentException extends StandardException { /** * @param int $messageCount * @param int $code - * @param Throwable|null $previous + * @param Exception|null $previous */ - public function __construct($messageCount = 1, $code = 0, Throwable $previous = null) + public function __construct($messageCount = 1, $code = 0, Exception $previous = null) { - $message = sprintf(Registry::getLang()->translateString('D3LM_EXC_SMS_SUCC_SENT'), $messageCount); + /** @var string $format */ + $format = Registry::getLang()->translateString('D3LM_EXC_SMS_SUCC_SENT'); + $message = sprintf($format, $messageCount); parent::__construct($message, $code, $previous); } diff --git a/src/Application/Model/MessageSender.php b/src/Application/Model/MessageSender.php index 8aa7c18..6a72eb0 100644 --- a/src/Application/Model/MessageSender.php +++ b/src/Application/Model/MessageSender.php @@ -19,51 +19,57 @@ use D3\Linkmobility4OXID\Application\Model\Exceptions\noRecipientFoundException; use D3\Linkmobility4OXID\Application\Model\MessageTypes\Sms; use Exception; use OxidEsales\Eshop\Application\Model\Order; -use OxidEsales\Eshop\Core\Registry; class MessageSender { /** * @param Order $order - * @param $messageBody + * @param string $messageBody + * @return void * @throws Exception */ - public function sendOrderFinishedMessage(Order $order, $messageBody) + public function sendOrderFinishedMessage(Order $order, string $messageBody): void { - $this->sendMessageByOrder('d3linkmobility_orderActive', $order, $messageBody); + if ((oxNew(Configuration::class))->sendOrderFinishedMessage()) { + $this->sendMessageByOrder($order, $messageBody); + } } /** * @param Order $order - * @param $messageBody + * @param string $messageBody + * @return void * @throws Exception */ - public function sendSendedNowMessage(Order $order, $messageBody) + public function sendSendedNowMessage(Order $order, string $messageBody): void { - $this->sendMessageByOrder('d3linkmobility_sendedNowActive', $order, $messageBody); + if ((oxNew(Configuration::class))->sendOrderSendedNowMessage()) { + $this->sendMessageByOrder($order, $messageBody); + } } /** * @param Order $order - * @param $messageBody + * @param string $messageBody + * @return void * @throws Exception */ - public function sendCancelOrderMessage(Order $order, $messageBody) + public function sendCancelOrderMessage(Order $order, string $messageBody): void { - $this->sendMessageByOrder('d3linkmobility_cancelOrderActive', $order, $messageBody); + if ((oxNew(Configuration::class))->sendOrderCanceledMessage()) { + $this->sendMessageByOrder($order, $messageBody); + } } /** - * @param $configParam * @param Order $order - * @param $messageBody + * @param string $messageBody + * @return void * @throws Exception */ - public function sendMessageByOrder($configParam, Order $order, $messageBody) + public function sendMessageByOrder(Order $order, string $messageBody): void { - if (false === (bool) Registry::getConfig()->getConfigParam($configParam) - || (bool) strlen(trim($messageBody)) === false - ) { + if ((bool) strlen(trim($messageBody)) === false) { return; } diff --git a/src/Application/Model/MessageTypes/AbstractMessage.php b/src/Application/Model/MessageTypes/AbstractMessage.php index 1bc8c70..b8c9f18 100644 --- a/src/Application/Model/MessageTypes/AbstractMessage.php +++ b/src/Application/Model/MessageTypes/AbstractMessage.php @@ -22,13 +22,18 @@ use OxidEsales\Eshop\Application\Model\Remark; abstract class AbstractMessage { - const REMARK_IDENT = 'LINKMOB'; + public const REMARK_IDENT = 'LINKMOB'; + /** @var string */ protected $message; + /** @var bool */ protected $removeLineBreaks = true; + /** @var bool */ protected $removeMultipleSpaces = true; + /** @var ResponseInterface */ protected $response; + /** @var Recipient[] */ protected $recipients = []; /** @@ -48,13 +53,14 @@ abstract class AbstractMessage } /** - * @param $userId - * @param $recipients - * @param $message + * @param string $userId + * @param string $recipients + * @param string $message * + * @return void * @throws Exception */ - protected function setRemark($userId, $recipients, $message) + protected function setRemark(string $userId, string $recipients, string $message): void { $remark = oxNew(Remark::class); $remark->assign([ @@ -74,7 +80,7 @@ abstract class AbstractMessage } /** - * @param array $recipients + * @param array $recipients * @return void */ protected function setRecipients(array $recipients) @@ -97,17 +103,17 @@ abstract class AbstractMessage } /** - * @param $message + * @param string $message * * @return string */ - protected function sanitizeMessage($message): string + protected function sanitizeMessage(string $message): string { $message = trim(strip_tags($message)); $message = $this->removeLineBreaks ? str_replace(["\r", "\n"], ' ', $message) : $message; $regexp = '/\s{2,}/m'; - return $this->removeMultipleSpaces ? preg_replace($regexp, ' ', $message) : $message; + return $this->removeMultipleSpaces ? (string) preg_replace($regexp, ' ', $message) : $message; } - abstract public function getTypeName() : string; + abstract public function getTypeName(): string; } diff --git a/src/Application/Model/MessageTypes/Sms.php b/src/Application/Model/MessageTypes/Sms.php index b928691..8cfe6bf 100644 --- a/src/Application/Model/MessageTypes/Sms.php +++ b/src/Application/Model/MessageTypes/Sms.php @@ -98,7 +98,7 @@ class Sms extends AbstractMessage } /** - * @param array $recipientsArray + * @param array $recipientsArray * * @return bool */ @@ -137,16 +137,17 @@ class Sms extends AbstractMessage return $response->isSuccessful(); } catch (abortSendingExceptionInterface $e) { Registry::getLogger()->warning($e->getMessage()); - Registry::getUtilsView()->addErrorToDisplay($e); + // Oxid does not accept throwable interface only exceptions according by definition + Registry::getUtilsView()->addErrorToDisplay($e->getMessage()); } catch (GuzzleException $e) { Registry::getLogger()->warning($e->getMessage()); - Registry::getUtilsView()->addErrorToDisplay($e); + Registry::getUtilsView()->addErrorToDisplay($e->getMessage()); } catch (ApiException $e) { Registry::getLogger()->warning($e->getMessage()); - Registry::getUtilsView()->addErrorToDisplay($e); + Registry::getUtilsView()->addErrorToDisplay($e->getMessage()); } catch (InvalidArgumentException $e) { Registry::getLogger()->warning($e->getMessage()); - Registry::getUtilsView()->addErrorToDisplay($e); + Registry::getUtilsView()->addErrorToDisplay($e->getMessage()); } return false; diff --git a/src/Application/Model/OrderRecipients.php b/src/Application/Model/OrderRecipients.php index 41e833f..b77bed2 100644 --- a/src/Application/Model/OrderRecipients.php +++ b/src/Application/Model/OrderRecipients.php @@ -16,10 +16,12 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Application\Model; use D3\Linkmobility4OXID\Application\Model\Exceptions\noRecipientFoundException; +use D3\LinkmobilityClient\Exceptions\RecipientException; +use D3\LinkmobilityClient\LoggerHandler; use D3\LinkmobilityClient\ValueObject\Recipient; +use libphonenumber\NumberParseException; use OxidEsales\Eshop\Application\Model\Country; use OxidEsales\Eshop\Application\Model\Order; -use OxidEsales\Eshop\Core\Registry; class OrderRecipients { @@ -40,17 +42,28 @@ class OrderRecipients public function getSmsRecipient(): Recipient { foreach ($this->getSmsRecipientFields() as $phoneFieldName => $countryIdFieldName) { - $content = trim((string) $this->order->getFieldData($phoneFieldName)); + /** @var string $content */ + $content = $this->order->getFieldData($phoneFieldName) ?: ''; + $content = trim($content); + $country = oxNew(Country::class); - if (strlen($content)) { - $country = oxNew(Country::class); - $country->load($this->order->getFieldData($countryIdFieldName)); - - return oxNew(Recipient::class, $content, $country->getFieldData('oxisoalpha2')); + try { + if (strlen($content)) { + /** @var string $countryId */ + $countryId = $this->order->getFieldData(trim($countryIdFieldName)); + $country->load($countryId); + return oxNew(Recipient::class, $content, $country->getFieldData('oxisoalpha2')); + } + } catch (NumberParseException $e) { + LoggerHandler::getInstance()->getLogger()->info($e->getMessage(), [$content, $country->getFieldData('oxisoalpha2')]); + } catch (RecipientException $e) { + LoggerHandler::getInstance()->getLogger()->info($e->getMessage(), [$content, $country->getFieldData('oxisoalpha2')]); } } - throw oxNew(noRecipientFoundException::class); + /** @var noRecipientFoundException $exc */ + $exc = oxNew(noRecipientFoundException::class); + throw $exc; } /** @@ -58,7 +71,7 @@ class OrderRecipients */ public function getSmsRecipientFields(): array { - $customFields = $this->getSanitizedCustomFields(); + $customFields = (oxNew(Configuration::class))->getOrderRecipientFields(); return count($customFields) ? $customFields : @@ -67,28 +80,4 @@ class OrderRecipients 'oxbillfon' => 'oxbillcountryid' ]; } - - /** - * @return array - */ - public function getSanitizedCustomFields(): array - { - $customFields = (array) Registry::getConfig()->getConfigParam('d3linkmobility_smsOrderRecipientsFields'); - array_walk($customFields, [$this, 'checkFieldExists']); - return array_filter($customFields); - } - - public function checkFieldExists(&$checkPhoneFieldName, $checkCountryFieldName) - { - $checkCountryFieldName = trim($checkCountryFieldName); - $checkPhoneFieldName = trim($checkPhoneFieldName); - $allFieldNames = oxNew(Order::class)->getFieldNames(); - - array_walk($allFieldNames, function (&$value) { - $value = strtolower($value); - }); - - $checkPhoneFieldName = in_array(strtolower($checkPhoneFieldName), $allFieldNames) && - in_array(strtolower($checkCountryFieldName), $allFieldNames) ? $checkPhoneFieldName : null; - } } diff --git a/src/Application/Model/RequestFactory.php b/src/Application/Model/RequestFactory.php index 7f0004c..fe47e19 100644 --- a/src/Application/Model/RequestFactory.php +++ b/src/Application/Model/RequestFactory.php @@ -25,6 +25,7 @@ class RequestFactory extends \D3\LinkmobilityClient\SMS\RequestFactory { $configuration = oxNew(Configuration::class); + /** @var SmsRequestInterface $request */ $request = parent::getSmsRequest(); $request->setTestMode($configuration->getTestMode()) ->setSenderAddress( diff --git a/src/Application/Model/UserRecipients.php b/src/Application/Model/UserRecipients.php index 3d72c0f..def0a47 100644 --- a/src/Application/Model/UserRecipients.php +++ b/src/Application/Model/UserRecipients.php @@ -16,10 +16,12 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Application\Model; use D3\Linkmobility4OXID\Application\Model\Exceptions\noRecipientFoundException; +use D3\LinkmobilityClient\Exceptions\RecipientException; +use D3\LinkmobilityClient\LoggerHandler; use D3\LinkmobilityClient\ValueObject\Recipient; +use libphonenumber\NumberParseException; use OxidEsales\Eshop\Application\Model\Country; use OxidEsales\Eshop\Application\Model\User; -use OxidEsales\Eshop\Core\Registry; class UserRecipients { @@ -40,16 +42,28 @@ class UserRecipients public function getSmsRecipient(): Recipient { foreach ($this->getSmsRecipientFields() as $fieldName) { - $content = trim($this->user->getFieldData($fieldName)); - if (strlen($content)) { - $country = oxNew(Country::class); - $country->load($this->user->getFieldData('oxcountryid')); + /** @var string $content */ + $content = $this->user->getFieldData($fieldName) ?: ''; + $content = trim($content); + $country = oxNew(Country::class); - return oxNew(Recipient::class, $content, $country->getFieldData('oxisoalpha2')); + try { + if (strlen($content)) { + /** @var string $countryId */ + $countryId = $this->user->getFieldData('oxcountryid'); + $country->load($countryId); + return oxNew(Recipient::class, $content, $country->getFieldData('oxisoalpha2')); + } + } catch (NumberParseException $e) { + LoggerHandler::getInstance()->getLogger()->info($e->getMessage(), [$content, $country->getFieldData('oxisoalpha2')]); + } catch (RecipientException $e) { + LoggerHandler::getInstance()->getLogger()->info($e->getMessage(), [$content, $country->getFieldData('oxisoalpha2')]); } } - throw oxNew(noRecipientFoundException::class); + /** @var noRecipientFoundException $exc */ + $exc = oxNew(noRecipientFoundException::class); + throw $exc; } /** @@ -57,7 +71,7 @@ class UserRecipients */ public function getSmsRecipientFields(): array { - $customFields = $this->getSanitizedCustomFields(); + $customFields = (oxNew(Configuration::class))->getUserRecipientFields(); return count($customFields) ? $customFields : @@ -67,29 +81,4 @@ class UserRecipients 'oxprivfon' ]; } - - /** - * @return array - */ - public function getSanitizedCustomFields(): array - { - $customFields = (array) Registry::getConfig()->getConfigParam('d3linkmobility_smsUserRecipientsFields'); - array_walk($customFields, [$this, 'checkFieldExists']); - return array_filter($customFields); - } - - /** - * @param $checkFieldName - */ - public function checkFieldExists(&$checkFieldName) - { - $checkFieldName = trim($checkFieldName); - $allFieldNames = oxNew(User::class)->getFieldNames(); - - array_walk($allFieldNames, function (&$value) { - $value = strtolower($value); - }); - - $checkFieldName = in_array(strtolower($checkFieldName), $allFieldNames) ? $checkFieldName : null; - } } diff --git a/src/IntelliSenseHelper.php b/src/IntelliSenseHelper.php index b097939..50cf93b 100644 --- a/src/IntelliSenseHelper.php +++ b/src/IntelliSenseHelper.php @@ -13,7 +13,7 @@ declare(strict_types=1); -namespace D3\Linkmobility4OXID\Modules\Aplication\Model { +namespace D3\Linkmobility4OXID\Modules\Application\Model { use OxidEsales\Eshop\Application\Model\Order; diff --git a/src/Modules/Application/Model/OrderModel.php b/src/Modules/Application/Model/OrderModel.php index f8b2199..3e5fc61 100644 --- a/src/Modules/Application/Model/OrderModel.php +++ b/src/Modules/Application/Model/OrderModel.php @@ -15,15 +15,24 @@ declare(strict_types=1); namespace D3\Linkmobility4OXID\Modules\Application\Model; +use D3\Linkmobility4OXID\Modules\Core\EmailCore; use OxidEsales\Eshop\Core\Email; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class OrderModel extends OrderModel_parent { - public function cancelOrder() + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function cancelOrder(): void { parent::cancelOrder(); - if ($this->getFieldData('oxstorno') === 1) { + if ((bool) $this->getFieldData('oxstorno') === true) { + /** @var EmailCore $Email */ $Email = oxNew(Email::class); $Email->d3SendCancelMessage($this); } diff --git a/src/Modules/Core/EmailCore.php b/src/Modules/Core/EmailCore.php index 62c4af3..58182b7 100644 --- a/src/Modules/Core/EmailCore.php +++ b/src/Modules/Core/EmailCore.php @@ -21,18 +21,25 @@ use OxidEsales\Eshop\Application\Model\Order; use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory; use OxidEsales\EshopCommunity\Internal\Framework\Templating\TemplateRendererBridgeInterface; use OxidEsales\EshopCommunity\Internal\Framework\Templating\TemplateRendererInterface; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class EmailCore extends EmailCore_parent { + /** @var string */ protected $d3OrderCustSmsTemplate = 'd3sms_ordercust.tpl'; + /** @var string */ protected $d3OrderSendedNowSmsTemplate = 'd3sms_sendednow.tpl'; + /** @var string */ protected $d3OrderCanceledSmsTemplate = 'd3sms_ordercanceled.tpl'; /** * @param Order $order - * @param null $subject + * @param string $subject * * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function sendOrderEmailToUser($order, $subject = null) { @@ -45,9 +52,11 @@ class EmailCore extends EmailCore_parent /** * @param Order $order - * @param null $subject + * @param string $subject * * @return bool + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ public function sendSendedNowMail($order, $subject = null) { @@ -60,8 +69,13 @@ class EmailCore extends EmailCore_parent /** * @param Order $order + * + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws Exception */ - public function d3SendOrderFinishedMessageToUser(Order $order) + public function d3SendOrderFinishedMessageToUser(Order $order): void { $messageSender = oxNew(MessageSender::class); $messageSender->sendOrderFinishedMessage($order, $this->d3GetOrderFinishedSmsMessageBody($order)); @@ -71,6 +85,8 @@ class EmailCore extends EmailCore_parent * @param Order $order * * @return string + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ protected function d3GetOrderFinishedSmsMessageBody(Order $order): string { @@ -83,9 +99,12 @@ class EmailCore extends EmailCore_parent /** * @param Order $order * + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface * @throws Exception */ - public function d3SendedNowMessage(Order $order) + public function d3SendedNowMessage(Order $order): void { $messageSender = oxNew(MessageSender::class); $messageSender->sendSendedNowMessage($order, $this->d3GetSendedNowSmsMessageBody($order)); @@ -95,6 +114,8 @@ class EmailCore extends EmailCore_parent * @param Order $order * * @return string + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ protected function d3GetSendedNowSmsMessageBody(Order $order): string { @@ -104,7 +125,15 @@ class EmailCore extends EmailCore_parent return $renderer->renderTemplate($this->d3OrderSendedNowSmsTemplate, $this->getViewData()); } - public function d3SendCancelMessage($order) + /** + * @param Order $order + * + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws Exception + */ + public function d3SendCancelMessage(Order $order): void { $messageSender = oxNew(MessageSender::class); $messageSender->sendCancelOrderMessage($order, $this->d3GetCancelOrderSmsMessageBody($order)); @@ -114,6 +143,8 @@ class EmailCore extends EmailCore_parent * @param Order $order * * @return string + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ protected function d3GetCancelOrderSmsMessageBody(Order $order): string { @@ -127,9 +158,12 @@ class EmailCore extends EmailCore_parent * Templating instance getter * * @return TemplateRendererInterface + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface */ protected function d3GetTplRenderer(): TemplateRendererInterface { + /** @var TemplateRendererBridgeInterface $bridge */ $bridge = ContainerFactory::getInstance()->getContainer() ->get(TemplateRendererBridgeInterface::class); $bridge->setEngine($this->_getSmarty()); diff --git a/src/Setup/Actions.php b/src/Setup/Actions.php new file mode 100644 index 0000000..f565eea --- /dev/null +++ b/src/Setup/Actions.php @@ -0,0 +1,147 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\Linkmobility4OXID\Setup; + +use D3\Linkmobility4OXID\Application\Model\MessageTypes\AbstractMessage; +use Doctrine\DBAL\Driver\Exception as DoctrineDriverException; +use Doctrine\DBAL\Exception as DoctrineException; +use Doctrine\DBAL\Query\QueryBuilder; +use OxidEsales\Eshop\Core\DatabaseProvider; +use OxidEsales\Eshop\Core\DbMetaDataHandler; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; +use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; +use OxidEsales\Eshop\Core\Exception\DatabaseException; +use OxidEsales\Eshop\Core\Registry; +use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory; +use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +class Actions +{ + /** + * @return void + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function setupDatabase() + { + try { + if (!$this->hasRemarkTypeEnumValue()) { + $this->addRemarkTypeEnumValue(); + } + } catch (DatabaseException|DoctrineDriverException|DoctrineException $e) { + Registry::getLogger()->error($e->getMessage()); + Registry::getUtilsView()->addErrorToDisplay($e); + } + } + + + + /** + * Regenerate views for changed tables + */ + public function regenerateViews() + { + $oDbMetaDataHandler = oxNew(DbMetaDataHandler::class); + $oDbMetaDataHandler->updateViews(); + } + + /** + * @return bool + * @throws ContainerExceptionInterface + * @throws DoctrineDriverException + * @throws DoctrineException + * @throws NotFoundExceptionInterface + */ + protected function hasRemarkTypeEnumValue(): bool + { + $fieldType = $this->getRemarkTypeFieldType(); + + $patternEnumCheck = '/^\b(enum)\b/mi'; + if (!preg_match($patternEnumCheck, $fieldType)) { + throw oxNew(DatabaseException::class, 'remark type field has not the expected enum type'); + } + + $patternValueCheck = '/\b('.preg_quote(AbstractMessage::REMARK_IDENT).')\b/mi'; + + return (bool) preg_match($patternValueCheck, $fieldType); + } + + /** + * @return string + * @throws DoctrineDriverException + * @throws DoctrineException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + protected function getRemarkTypeFieldType(): string + { + /** @var QueryBuilder $qb */ + $qb = ContainerFactory::getInstance()->getContainer()->get(QueryBuilderFactoryInterface::class)->create(); + $qb->select('column_type') + ->from('INFORMATION_SCHEMA.COLUMNS') + ->where( + $qb->expr()->and( + $qb->expr()->eq( + 'table_schema', + $qb->createNamedParameter(Registry::getConfig()->getConfigParam('dbName')) + ), + $qb->expr()->eq( + 'table_name', + $qb->createNamedParameter('oxremark') + ), + $qb->expr()->eq( + 'COLUMN_NAME', + $qb->createNamedParameter('oxtype') + ) + ) + ); + + return (string) $qb->execute()->fetchOne(); + } + + /** + * @return void + * @throws DoctrineDriverException + * @throws DoctrineException + * @throws DatabaseConnectionException + * @throws DatabaseErrorException + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + protected function addRemarkTypeEnumValue() + { + $valuePattern = '/(?<=enum\().*(?=\))/i'; + preg_match($valuePattern, $this->getRemarkTypeFieldType(), $matches); + + $items = array_unique( + array_merge( + str_getcsv($matches[0], ',', "'"), + [AbstractMessage::REMARK_IDENT] + ) + ); + + $db = DatabaseProvider::getDb(); + + $query = 'ALTER TABLE '.$db->quoteIdentifier('oxremark'). + ' CHANGE '.$db->quoteIdentifier('OXTYPE'). ' '.$db->quoteIdentifier('OXTYPE') . + ' enum('.implode(',', $db->quoteArray($items)).')'. + ' COLLATE '.$db->quote('utf8_general_ci').' NOT NULL DEFAULT '.$db->quote('r'); + + $db->execute($query); + } +} \ No newline at end of file diff --git a/src/Setup/Events.php b/src/Setup/Events.php new file mode 100644 index 0000000..9cc4892 --- /dev/null +++ b/src/Setup/Events.php @@ -0,0 +1,48 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +// @codeCoverageIgnoreStart + +namespace D3\Linkmobility4OXID\Setup; + +use D3\ModCfg\Application\Model\Exception\d3ShopCompatibilityAdapterException; +use D3\ModCfg\Application\Model\Install\d3install; +use Doctrine\DBAL\DBALException; +use OxidEsales\Eshop\Core\Exception\DatabaseConnectionException; +use OxidEsales\Eshop\Core\Exception\DatabaseErrorException; +use OxidEsales\Eshop\Core\Exception\StandardException; +use OxidEsales\Eshop\Core\Exception\SystemComponentException; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +class Events +{ + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public static function onActivate() + { + /** @var Actions $actions */ + $actions = oxNew(Actions::class); + $actions->setupDatabase(); + $actions->regenerateViews(); + } + + public static function onDeactivate() + { + } +} +// @codeCoverageIgnoreEnd diff --git a/src/metadata.php b/src/metadata.php index e3a1efa..3e53c9d 100644 --- a/src/metadata.php +++ b/src/metadata.php @@ -17,6 +17,7 @@ use D3\Linkmobility4OXID\Application\Controller\Admin\AdminOrder; use D3\Linkmobility4OXID\Application\Controller\Admin\AdminUser; use D3\Linkmobility4OXID\Modules\Application\Model\OrderModel; use D3\Linkmobility4OXID\Modules\Core\EmailCore; +use D3\Linkmobility4OXID\Setup\Events; use OxidEsales\Eshop\Application\Model\Order; use OxidEsales\Eshop\Core\Email; @@ -34,7 +35,7 @@ $aModule = [ 'de' => 'Anbindung der LINK Mobility API (Nachrichtenversand per SMS) an den Shop', 'en' => 'Connection of the LINK Mobility API ( sending messages via SMS) to the shop', ], - 'version' => '1.0.0.0', + 'version' => '1.1.0.0', 'thumbnail' => 'picture.png', 'author' => 'D³ Data Development (Inh.: Thomas Dartsch)', 'email' => 'support@shopmodule.com', @@ -54,7 +55,10 @@ $aModule = [ 'd3sms_sendednow.tpl' => 'd3/linkmobility/Application/views/tpl/SMS/sendednow.tpl', 'd3sms_ordercanceled.tpl' => 'd3/linkmobility/Application/views/tpl/SMS/ordercanceled.tpl', ], - 'events' => [], + 'events' => [ + 'onActivate' => Events::class.'::onActivate', + 'onDeactivate' => Events::class.'::onDeactivate', + ], 'blocks' => [ [ 'template' => 'order_remark.tpl',