diff --git a/.docker/Dockerfile b/.docker/Dockerfile new file mode 100644 index 0000000..2363111 --- /dev/null +++ b/.docker/Dockerfile @@ -0,0 +1,57 @@ +FROM php:8.2-apache +ARG XDEBUG_VERSION=3.4.0 +ARG OC_VERSION=4.1.0.0 + +LABEL org.opencontainers.image.source=https://github.com/bulkgate/cartsms +LABEL opencart_version=${OC_VERSION} + +ENV OC_VERSION=${OC_VERSION} + +RUN echo "Opencart version: ${OC_VERSION}" + +WORKDIR /tmp + +RUN curl -fL "https://github.com/opencart/opencart/archive/refs/tags/${OC_VERSION}.tar.gz" -o opencart.tar.gz && \ + curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php && \ + mv composer.phar /usr/local/bin/composer + +RUN tar -xzf opencart.tar.gz + +RUN rm opencart.tar.gz + +USER www-data:www-data + +RUN cp -r opencart-*/upload/* /var/www/html + +WORKDIR /var/www/html + +RUN cp config-dist.php config.php && \ + cp admin/config-dist.php admin/config.php + +USER root + +VOLUME /var/www/html + +RUN apt-get update \ + && apt-get install -y \ + wait-for-it \ + unzip \ + libicu-dev \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libzip-dev \ + libcurl3-dev \ + libwebp-dev \ + && pecl install xdebug-${XDEBUG_VERSION} \ + && docker-php-ext-enable xdebug \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j$(nproc) gd zip mysqli curl intl \ + && docker-php-ext-enable gd zip mysqli curl intl + +RUN a2enmod rewrite + +COPY --chmod=777 entrypoint.sh /usr/sbin +COPY install-extension.php /tmp + +ENTRYPOINT ["entrypoint.sh"] diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh new file mode 100644 index 0000000..008fd4a --- /dev/null +++ b/.docker/entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +if [ ! -f /var/www/html/install.lock ]; then + wait-for-it db:3306 -t 60 && + php /var/www/html/install/cli_install.php install \ + --username admin \ + --password admin \ + --email admin@example.com \ + --http_server http://localhost:8083/ \ + --db_driver mysqli \ + --db_hostname db \ + --db_username root \ + --db_password admin \ + --db_database opencart \ + --db_port 3306 \ + --db_prefix oc_ && + touch /var/www/html/install.lock && + php /tmp/install-extension.php +fi + +exec apache2-foreground \ No newline at end of file diff --git a/.docker/install-extension.php b/.docker/install-extension.php new file mode 100644 index 0000000..7f93725 --- /dev/null +++ b/.docker/install-extension.php @@ -0,0 +1,77 @@ +register('Opencart\\' . APPLICATION, DIR_APPLICATION); +$autoloader->register('Opencart\Extension', DIR_EXTENSION); +$autoloader->register('Opencart\System', DIR_SYSTEM); + +require_once DIR_SYSTEM . 'vendor.php'; + +// Registry +$registry = new \Opencart\System\Engine\Registry(); +$registry->set('autoloader', $autoloader); + +// Config +$config = new \Opencart\System\Engine\Config(); +$config->addPath(DIR_CONFIG); +// Load the default config +$config->load('default'); +$config->load(strtolower(APPLICATION)); +$registry->set('config', $config); + +// Set the default application +$config->set('application', APPLICATION); + +define('IS_4_1_x', class_exists('Opencart\System\Engine\Factory')); + +// Factory - opencart 4.1.x +if (IS_4_1_x) { + $registry->set('factory', new \Opencart\System\Engine\Factory($registry)); +} + +// Loader +$loader = new \Opencart\System\Engine\Loader($registry); +$registry->set('load', $loader); + +// Event +$event = new \Opencart\System\Engine\Event($registry); +$registry->set('event', $event); + +// Cache +//$registry->set('cache', new \Opencart\System\Library\Cache($config->get('cache_engine'), $config->get('cache_expire'))); + +$db = new \Opencart\System\Library\DB($config->get('db_engine'), $config->get('db_hostname'), $config->get('db_username'), $config->get('db_password'), $config->get('db_database'), $config->get('db_port'), $config->get('db_ssl_key'), $config->get('db_ssl_cert'), $config->get('db_ssl_ca')); +$registry->set('db', $db); + + +$response = new \Opencart\System\Library\Response(); +$response->addHeader('Content-Type: text/plain; charset=utf-8'); +$registry->set('response', $response); + +$registry->load->model('setting/extension'); + +/** @var \Opencart\Admin\Model\Setting\Extension $install*/ +$install = $registry->get('model_setting_extension'); +$install_info = json_decode(file_get_contents('/var/www/html/extension/oc_cartsms/install.json'), true); + +$id = $install->addInstall([ + 'extension_id' => 0, + 'extension_download_id' => 0, + 'name' => $install_info['name'], + 'code' => 'oc_cartsms', + 'description' => $install_info['description'], + 'version' => $install_info['version'], + 'author' => $install_info['author'], + 'link' => $install_info['link'] +]); + +$install->addPath($id, 'oc_cartsms/admin/controller/module/cartsms.php'); +$install->editStatus($id, true); + +$response->setOutput('Extension ' . $install_info['name'] . ' installed successfully.'); + +$response->output(); \ No newline at end of file diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..f16d80e --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,25 @@ +name: Run tests + +on: [push] + +jobs: + tests: + runs-on: ubuntu-latest + + container: ghcr.io/bulkgate/cartsms:4.1.0.0 + + steps: + - uses: actions/checkout@v1 + + - run: composer install --prefer-dist --no-progress --no-suggest + + - run: composer run tester + + - run: composer run coverage + + - uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.html + + #- run: composer run phpstan \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4eb1183 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +.idea +vendor +opencart +tests/**/output/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 5833171..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "upload/system/library/cartsms/extensions"] - path = upload/system/library/cartsms/extensions - url = https://github.com/BulkGate/extensions diff --git a/README.md b/README.md index fcfe729..bf3c550 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,22 @@ http://www.cart-sms.com/ + +# Development + +https://webkul.com/blog/how-to-create-a-module-in-opencart/ + + +## Category extension +URL: http://localhost:8083/admin/index.php?route=extension/opencart/module/category&user_token=886a6f6378b7a94cbccad4acd55abdf1 +FILE: /extension/opencart/admin/controller/module/category.php +view: /extension/opencart/admin/view/template/module/category.twig +NAMESPACE: Opencart\Admin\Controller\Extension\Opencart\Module; +CLASS: Category + +## Cartsms extension +URL: http://localhost:8083/admin/index.php?route=extension/cartsms/module/cartsms&user_token=886a6f6378b7a94cbccad4acd55abdf1 +FILE: /extension/cartsms/admin/controller/module/cartsms.php +view: /extension/cartsms/admin/view/template/module/cartsms.twig +NAMESPACE: Opencart\Admin\Controller\Extension\Cartsms\Module; +CLASS: Cartsms \ No newline at end of file diff --git a/admin/controller/event/hook.php b/admin/controller/event/hook.php new file mode 100644 index 0000000..c6ab083 --- /dev/null +++ b/admin/controller/event/hook.php @@ -0,0 +1,128 @@ +language->load('extension/oc_cartsms/module/cartsms'); + + $data['menus'][] = [ + 'id' => 'menu-cartsms', + 'icon' => 'fas fa-envelope', + 'name' => $this->language->get('extension_name_menu'), + 'href' => $this->url->link('extension/oc_cartsms/module/cartsms', 'user_token=' . $this->session->data['user_token']), + 'children' => [] + ]; + } + + public function hookRenderSendMessageBox(string $route, array &$data) + { + $order_id = (int) $data['order_id']; + + if ($order_id === 0 || !$this->di_container->getByClass(Plugin\Settings\Settings::class)->load('static:application_token')) { + return; + } + + $data['extensions'][] = $this->load->controller('extension/oc_cartsms/module/send_message', $order_id); + } + + public function hookSendSms(array $params) + { + $number = $params['number'] ?? null; + $template = $params['template'] ?? null; + $variables = $params['variables'] ?? []; + $settings = $params['settings'] ?? []; + + $hook = $this->di_container->getByClass(Plugin\Event\Hook::class); + + $hook->send('/api/2.0/advanced/transactional', [ + 'number' => $number, + 'application_product' => 'oc', + 'tag' => 'module_custom', + 'variables' => $variables, + 'country' => $settings['country'] ?? null, + 'channel' => [ + 'sms' => [ + 'sender_id' => $settings['senderType'] ?? 'gSystem', + 'sender_id_value' => $settings['senderValue'] ?? '', + 'unicode' => $settings['unicode'] ?? false, + 'text' => $template, + ], + ], + ]); + } + + //OK + public function hookAddCustomer(string $route, array $params, int $id_customer) + { + $this->runHook('customer', 'new', new Plugin\Event\Variables([ + 'customer_id' => $id_customer, + //'data' => $params, + ])); + } + + public function hookProductOutOfStockBefore(string $route, array $params) + { + [$id_product] = $params; + $this->load->model('catalog/product'); + + $this->product_out_of_stock_state = (new State(fn() => (int) $this->model_catalog_product->getProduct($id_product)['quantity'])) + ->captureInitial() + ->setExpected(0); + } + + //OK + public function hookProductOutOfStockAfter(string $route, array $params) + { + [$id_product, $data] = $params; + + $this->product_out_of_stock_state->captureActual(); + + if (!$this->product_out_of_stock_state->shouldRunHook()) { + return; + } + + $this->runHook('product', 'out-of-stock', new Plugin\Event\Variables([ + 'product_id' => $id_product, + 'shop_id' => $data['product_store'][0], + //'data' => $params, + ])); + } + + public function hookChangeReturnStatusBefore(string $route, array $params) + { + [$id_return, $id_return_status] = $params; + + $this->load->model('sale/returns'); + + $this->return_status_state = (new State(fn() => (int) $this->model_sale_returns->getReturn($id_return)['return_status_id'])) + ->captureInitial() + ->setExpected((int) $id_return_status); + } + + public function hookChangeReturnStatusAfter(string $route, array $params) + { + [$id_return, $id_return_status] = $params; + + $this->return_status_state->captureActual(); + + if (!$this->return_status_state->shouldRunHook()) { + return; + } + + $this->runHook('return', 'change-status', new Plugin\Event\Variables([ + 'return_id' => $id_return, + 'return_status_id' => $id_return_status, + ])); + } +} \ No newline at end of file diff --git a/admin/controller/module/cartsms.php b/admin/controller/module/cartsms.php new file mode 100644 index 0000000..2188c37 --- /dev/null +++ b/admin/controller/module/cartsms.php @@ -0,0 +1,317 @@ +di_container->getByClass(Plugin\Eshop\EshopSynchronizer::class)->run(); + + $jwt = $this->di_container->getByClass(Plugin\User\Sign::class)->authenticate(false, ['expire' => time() + 300]); + + $this->load->language('extension/oc_cartsms/module/cartsms'); + $this->document->setTitle($this->language->get('heading_title')); + + $this->response->setOutput($this->load->view('extension/oc_cartsms/module/cartsms', [ + 'header' => $this->load->controller('common/header'), + 'column_left' => $this->load->controller('common/column_left'), + 'footer' => $this->load->controller('common/footer'), + 'synchronizer' => $this->di_container->getByClass(Plugin\Settings\Synchronizer::class), + 'settings' => $this->di_container->getByClass(Plugin\Settings\Settings::class), + 'url' => $this->di_container->getByClass(Plugin\IO\Url::class), + 'path' => new class($this->url, $this->session->data['user_token']) { + public function __construct(private $url, private $token) + { + } + + public function get(string $route, array $args = []) + { + return $this->url->link($route, [...['user_token' => $this->token], ...$args], true); + } + }, + 'token' => $jwt, + ])); + } + + public function install() + { + $this->di_container->getByClass(Plugin\Settings\Settings::class)->install(); + + // enable access to module backoffice UI + $this->load->model('user/user_group'); + $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/oc_cartsms/module/cartsms'); + $this->model_user_user_group->addPermission($this->user->getGroupId(), 'modify', 'extension/oc_cartsms/module/cartsms'); + + // we need to enable phone number fields + $this->load->model('setting/setting'); + $this->model_setting_setting->editValue('config', 'config_telephone_display', 1); + $this->model_setting_setting->editValue('config', 'config_telephone_required', 1); + + // we register cron script to process async tasks + $this->load->model('setting/cron'); + $this->model_setting_cron->addCron('cartsms_asynchronous', '', 'minute', 'extension/oc_cartsms/asynchronous/task', true); + + + $this->load->model('setting/event'); + + // register custom fields for marketing messages opt-in + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_custom_fields', + 'description' => '', + 'trigger' => 'catalog/model/account/custom_field.getCustomFields/after', + 'action' => 'extension/oc_cartsms/event/hook.hookCustomFields', + 'status' => '1', + 'sort_order' => '1' + ]); + + // register asynchronous script to process async tasks + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_asynchronous', + 'description' => '', + 'trigger' => 'catalog/view/common/header/before', + 'action' => 'extension/oc_cartsms/event/hook.hookAsynchronousAsset', + 'status' => '1', + 'sort_order' => '1' + ]); + + // register module into menu + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_menu', + 'description' => '', + 'trigger' => 'admin/view/common/column_left/before', + 'action' => 'extension/oc_cartsms/event/hook.hookMenu', + 'status' => '1', + 'sort_order' => '1' + ]); + + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_send_message_box', + 'description' => '', + 'trigger' => 'admin/view/sale/order_info/before', + 'action' => 'extension/oc_cartsms/event/hook.hookRenderSendMessageBox', + 'status' => '1', + 'sort_order' => '1' + ]); + + // user can programmatically send sms + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_send_sms', + 'description' => '', + 'trigger' => 'system/cartsms.send_sms', + 'action' => 'extension/oc_cartsms/event/hook.hookSendSms', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // RETURN:NEW + // todo: z back office nelze vytvorit return, protoze to hlasi chybu product_id ... z nejakeho duvodu je to 0 + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_return', + 'description' => '', + 'trigger' => 'catalog/model/account/returns.addReturn/after', + 'action' => 'extension/oc_cartsms/event/hook.hookAddReturn', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // RETURN:CHANGE-STATUS + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_change_return_status', + 'description' => '', + 'trigger' => 'admin/model/sale/returns.addHistory/before', + 'action' => 'extension/oc_cartsms/event/hook.hookChangeReturnStatusBefore', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_change_return_status', + 'description' => '', + 'trigger' => 'admin/model/sale/returns.addHistory/after', + 'action' => 'extension/oc_cartsms/event/hook.hookChangeReturnStatusAfter', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // ORDER:NEW + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_order', + 'description' => '', + 'trigger' => 'catalog/controller/checkout/success/before', + 'action' => 'extension/oc_cartsms/event/hook.hookAddOrderCheckoutSuccess', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_order', + 'description' => '', + 'trigger' => 'catalog/controller/api/order/after', + 'action' => 'extension/oc_cartsms/event/hook.hookAddOrderApi', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // PRODUCT:OUT-OF-STOCK + // catalog + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_order_history', + 'description' => '', + 'trigger' => 'catalog/model/checkout/order.addHistory/before', + 'action' => 'extension/oc_cartsms/event/hook.hookProductOutOfStockBefore', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_order_history', + 'description' => '', + 'trigger' => 'catalog/model/checkout/order.addHistory/after', + 'action' => 'extension/oc_cartsms/event/hook.hookProductOutOfStockAfter', + 'status' => '1', + 'sort_order' => '1' + ]); + // admin + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_edit_product', + 'description' => '', + 'trigger' => 'admin/model/catalog/product.editProduct/before', + 'action' => 'extension/oc_cartsms/event/hook.hookProductOutOfStockBefore', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_edit_product', + 'description' => '', + 'trigger' => 'admin/model/catalog/product.editProduct/after', + 'action' => 'extension/oc_cartsms/event/hook.hookProductOutOfStockAfter', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // ORDER:CHANGE-STATUS + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_change_order_status', + 'description' => '', + 'trigger' => 'catalog/controller/api/order/before', + 'action' => 'extension/oc_cartsms/event/hook.hookChangeOrderStatusBefore', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_change_order_status', + 'description' => '', + 'trigger' => 'catalog/controller/api/order/after', + 'action' => 'extension/oc_cartsms/event/hook.hookChangeOrderStatusAfter', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // CUSTOMER:NEW + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_customer', + 'description' => '', + 'trigger' => 'catalog/model/account/customer.addCustomer/after', + 'action' => 'extension/oc_cartsms/event/hook.hookAddCustomer', + 'status' => '1', + 'sort_order' => '1' + ]); + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_add_customer', + 'description' => '', + 'trigger' => 'admin/model/customer/customer.addCustomer/after', + 'action' => 'extension/oc_cartsms/event/hook.hookAddCustomer', + 'status' => '1', + 'sort_order' => '1' + ]); + + + // CONTACT:FORM + $this->model_setting_event->addEvent([ + 'code' => 'cartsms_contact_form', + 'description' => '', + 'trigger' => 'catalog/controller/information/contact.send/after', + 'action' => 'extension/oc_cartsms/event/hook.hookContactForm', + 'status' => '1', + 'sort_order' => '1' + ]); + } + + public function uninstall() + { + $this->di_container->getByClass(Plugin\Settings\Settings::class)->uninstall(); + + $this->load->model('user/user_group'); + $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'extension/oc_cartsms/module/cartsms'); + $this->model_user_user_group->removePermission($this->user->getGroupId(), 'modify', 'extension/oc_cartsms/module/cartsms'); + + $this->load->model('setting/cron'); + $this->model_setting_cron->deleteCronByCode('cartsms_asynchronous'); + + $this->load->model('setting/event'); + $this->model_setting_event->deleteEventByCode('cartsms_custom_fields'); + $this->model_setting_event->deleteEventByCode('cartsms_asynchronous'); + $this->model_setting_event->deleteEventByCode('cartsms_menu'); + $this->model_setting_event->deleteEventByCode('cartsms_send_message_box'); + $this->model_setting_event->deleteEventByCode('cartsms_send_sms'); + $this->model_setting_event->deleteEventByCode('cartsms_add_order'); + $this->model_setting_event->deleteEventByCode('cartsms_add_return'); + $this->model_setting_event->deleteEventByCode('cartsms_change_return_status'); + $this->model_setting_event->deleteEventByCode('cartsms_add_order_history'); + $this->model_setting_event->deleteEventByCode('cartsms_change_order_status'); + $this->model_setting_event->deleteEventByCode('cartsms_add_customer'); + $this->model_setting_event->deleteEventByCode('cartsms_edit_product'); + $this->model_setting_event->deleteEventByCode('cartsms_contact_form'); + } + + public function proxy() + { + $this->response->addHeader('Content-Type: application/json'); + + switch ($this->request->get('action')) { + case "login": + ['email' => $email, 'password' => $password] = $this->request->post; + + return $this->response->setOutput(json_encode($this->di_container->getByClass(Plugin\User\Sign::class)->in($email, $password, '/dashboard'))); + case "logout": + return $this->response->setOutput(json_encode($this->di_container->getByClass(Plugin\User\Sign::class)->out('/sign/in'))); + case "authenticate": + return $this->response->setOutput(json_encode($this->di_container->getByClass(Ajax\Authenticate::class)->run('/sign/in'))); + case "module-settings": + return $this->response->setOutput(json_encode($this->di_container->getByClass(Ajax\PluginSettings::class)->run($this->request->post, fn(string $lang) => $this->url->link('extension/oc_cartsms/module/cartsms', ['user_token' => $this->session->data['user_token'], 'reload' => $lang], true) . '#/dashboard'))); + } + } + + public function debug() + { + $requirements = $this->di_container->getByClass(Plugin\Debug\Requirements::class); + $logger = $this->di_container->getByClass(Plugin\Debug\Logger::class); + $url = $this->di_container->getByClass(Plugin\IO\Url::class); + + $requirements = $requirements->run([ + $requirements->same('{"message":"BulkGate API"}', file_get_contents($url->get('api/welcome')), 'Api Connection'), + $requirements->same(true, version_compare(VERSION, '4.0.0', '>='), 'Opencart ver. >= 4.0.0'), + ]); + + $this->response->setOutput($this->load->view('extension/oc_cartsms/module/debug', [ + 'header' => $this->load->controller('common/header'), + 'column_left' => $this->load->controller('common/column_left'), + 'footer' => $this->load->controller('common/footer'), + 'requirements' => $requirements, + 'errors' => array_reverse($logger->getList()), + 'opencart_version' => VERSION, + 'php_version' => phpversion(), + 'url' => $url->get(), + ])); + } +} + diff --git a/admin/controller/module/send_message.php b/admin/controller/module/send_message.php new file mode 100644 index 0000000..f639fcc --- /dev/null +++ b/admin/controller/module/send_message.php @@ -0,0 +1,41 @@ +di_container->getByClass(Plugin\User\Sign::class); + $url = $this->di_container->getByClass(Plugin\IO\Url::class); + $loader = $this->di_container->getByClass(Plugin\Event\Loader::class); + + $this->load->model('sale/order'); + + $order = $this->model_sale_order->getOrder($order_id); + + $variables = new Plugin\Event\Variables([ + 'order_id' => $order_id, + 'customer_id' => $order['customer_id'], + 'lang_id' => $order['language_id'], + ]); + + $loader->load($variables); + + return $this->load->view('extension/oc_cartsms/module/send_message', [ + 'token' => $sign->authenticate(), + 'url' => $url, + 'variables' => [ + ...$variables->toArray(), + // these variables are for web component + 'first_name' => Helpers::priorityValues(['customer_firstname', 'customer_invoice_firstname'], $variables), + 'last_name' => Helpers::priorityValues(['customer_lastname', 'customer_invoice_lastname'], $variables), + 'phone_mobile' => Helpers::priorityValues(['customer_mobile', 'customer_phone', 'customer_invoice_mobile', 'customer_invoice_phone'], $variables), + 'phone_number_iso' => Helpers::priorityValues(['customer_country_id', 'customer_invoice_country_id'], $variables) + ] + ]); + } +} \ No newline at end of file diff --git a/admin/language/en-gb/module/cartsms.php b/admin/language/en-gb/module/cartsms.php new file mode 100644 index 0000000..c368f8e --- /dev/null +++ b/admin/language/en-gb/module/cartsms.php @@ -0,0 +1,7 @@ + + @keyframes logo { + 0% { + filter: grayscale(1) opacity(.2); + transform: scale(.6); + } + 25% { + filter: none; + transform: scale(.65); + } + 70% { + transform: none; + } + } + + @keyframes heading { + 0% { + opacity: .1; + } + 50% { + opacity: .8; + } + 100% { + opacity: 1; + } + } + + @keyframes progress { + 100% { + opacity: 1 + } + } + + @keyframes progress-processing { + 0% { + transform: translateX(-300px) + } + 5% { + transform: translateX(-240px) + } + 15% { + transform: translateX(-30px) + } + 25% { + transform: translateX(-30px) + } + 30% { + transform: translateX(-20px) + } + 45% { + transform: translateX(-20px) + } + 50% { + transform: translateX(-15px) + } + 65% { + transform: translateX(-15px) + } + 70% { + transform: translateX(-10px) + } + 95% { + transform: translateX(-10px) + } + 100% { + transform: translateX(-5px) + } + } + + #bulkgate-plugin { + will-change: transform; + z-index: 0; + } + + #bulkgate-plugin #loading { + position: fixed; + contain: layout; + height: calc(100vh - 64px); + left: 0; + top: 0; + right: 0; + bottom: 0; + background: #fff; + z-index: 2999; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + } + + #bulkgate-plugin #loading img { + width: 160px; + animation: logo 1.5s .3s both; + margin: 24px 0; + } + + #bulkgate-plugin #loading h3 { + font-size: 32px; + color: #606469; + animation: heading .5s .675s both; + } + + #bulkgate-plugin #progress { + animation: progress .5s 2.5s 1 both; + height: 4px; + width: 100%; + opacity: 0; + background: #ddd; + position: relative; + overflow: hidden; + } + + #bulkgate-plugin #progress:before { + animation: progress-processing 20s 3s linear both; + background-color: var(--secondary); + content: ''; + display: block; + height: 100%; + position: absolute; + transform: translateX(-300px); + width: 100%; + } + + gate-ecommerce-plugin { + box-sizing: border-box; /* realne se tyka pouze web-componenty */ + } + +{{ column_left }} +
+ + + +
+ +
+
+ BulkGate +
+
+
+
+
+{{ footer }} diff --git a/admin/view/template/module/debug.twig b/admin/view/template/module/debug.twig new file mode 100644 index 0000000..25d6a97 --- /dev/null +++ b/admin/view/template/module/debug.twig @@ -0,0 +1,71 @@ +{{ header }} +{{ column_left }} +
+
+

 BulkGate Debug

+

This page serves as a comprehensive tool for users to monitor, analyze, and troubleshoot the plugin, including tracking errors in the log. It also provides essential information and troubleshooting capabilities.

+ +

Requirements test

+ + + + + + + + + + + + + + + {% for requirement in requirements %} + + + + + {% endfor %} + +
+ PHP Version + + {{ php_version }} +
+ OpenCart Version + + {{ opencart_version }} +
+ URL + + {{ url }} +
+ {{ requirement.description }} + + {{ requirement.passed ? "OK" : "FAIL " ~requirement.error }} +
+ +

Error log

+ {% if errors %} +
+ + + {% for error in errors %} + + + + + {% endfor %} + +
+ {{ error.created | date('d.m. Y H:i:s') }} + + {{ error.message }} +
+
+ {% else %} +

No errors found.

+ {% endif %} +
+
+{{ footer }} \ No newline at end of file diff --git a/admin/view/template/module/send_message.twig b/admin/view/template/module/send_message.twig new file mode 100644 index 0000000..9b93d2d --- /dev/null +++ b/admin/view/template/module/send_message.twig @@ -0,0 +1,14 @@ + + + + diff --git a/catalog/controller/asynchronous/task.php b/catalog/controller/asynchronous/task.php new file mode 100644 index 0000000..78c7690 --- /dev/null +++ b/catalog/controller/asynchronous/task.php @@ -0,0 +1,23 @@ +di_container->getByClass(Plugin\Settings\Settings::class); + + if (in_array($settings->load('main:dispatcher'), [Plugin\Event\Dispatcher::Cron, Plugin\Event\Dispatcher::Asset])) { + $count = $this->di_container->getByClass(Plugin\Event\Asynchronous::class)->run(max(5, (int) ($settings->load('main:cron-limit') ?? 10))); + + echo "// Asynchronous task consumer has processed $count tasks"; + } else { + echo '// Asynchronous task consumer is disabled'; + } + } +} \ No newline at end of file diff --git a/catalog/controller/event/hook.php b/catalog/controller/event/hook.php new file mode 100644 index 0000000..25856ff --- /dev/null +++ b/catalog/controller/event/hook.php @@ -0,0 +1,181 @@ +url->link('extension/oc_cartsms/asynchronous/task') .'">'; //todo: udelat twig template? + } + + // OK + public function hookCustomFields(string $route, array $params, array &$data) + { + $settings = $this->di_container->getByClass(Plugin\Settings\Settings::class); + + if (!$settings->load('main:marketing_message_opt_in_enabled')) { + return; + } + + $data[] = [ + 'custom_field_id' => 'bulkgate_marketing_message_opt_in', + 'location' => 'account', + 'type' => 'checkbox', + 'required' => false, + //'name' => 'abc', + 'custom_field_value' => [[ + 'custom_field_value_id' => 'bulkgate_marketing_message_opt_in', // == + 'custom_field_id' => 'bulkgate_marketing_message', + 'name' => 'I consent to receiving marketing communications via SMS, Viber, RCS, WhatsApp, and other similar channels.' + ]] + ]; + } + + //OK + public function hookAddOrderCheckoutSuccess(string $route) + { + if (!isset($this->session->data['order_id'])) { + return; + } + + $this->runHook('order', 'new', new Plugin\Event\Variables([ + 'order_id' => $this->session->data['order_id'], + //'data' => $this->session->data, todo: tady jeste muzeme ziskat data o adrese v pripade, ze uzivatel neni prihlaseny. Prednastavime promenne? Chtelo by to asi nejaky helper... + ])); + } + + public function hookAddOrderApi(string $route) + { + if ($this->request->get['call'] !== 'confirm') { + return; + } + + $output = Plugin\Utils\JsonArray::decode($this->response->getOutput()); + + if (!$output || (int) $this->request->post['order_id'] === (int) $output['order_id']) { + return; + } + + $this->runHook('order', 'new', new Plugin\Event\Variables([ + 'order_id' => $output['order_id'], + //'response' => $output, + //'_GET' => $this->request->get, + //'_POST' => $this->request->post, + //'data' => $data, //todo: api/order.index neprebira zadne parametry... + //'output' => $output, todo: provolava se pres API, takze $outptut neni nastaveny, protoze api/order.index nic nevraci. Muzeme maximalne vyuzit response objektu + //'store_url' => $shop_domain, + ])); + } + + //OK + public function hookAddReturn(string $route, array $params, int $id_return) + { + $this->runHook('return', 'new', new Plugin\Event\Variables([ + 'return_id' => $id_return, + ])); + } + + public function hookProductOutOfStockBefore(string $route, array $params) + { + [$id_order] = $params; + + $this->load->model('checkout/order'); + $this->load->model('catalog/product'); + + $products = $this->model_checkout_order->getProducts($id_order); + + $this->product_out_of_stock_state = (new State(fn () => array_map(fn($item) => (int) $this->model_catalog_product->getProduct($item['product_id'])['quantity'] ?? 0, array_combine(array_column($products, 'product_id'), $products)))) + ->captureInitial(); + } + + //OK + public function hookProductOutOfStockAfter(string $route, array $params) + { + [$id_order] = $params; + + $this->product_out_of_stock_state->captureActual(); + + if (!$this->product_out_of_stock_state->isChanged()) { + return; + } + + $initial_products = $this->product_out_of_stock_state->getInitial(); + $actual_products = $this->product_out_of_stock_state->getActual(); + + foreach($actual_products as $product_id => $quantity) { + $initial_quantity = $initial_products[$product_id]; + + if ($initial_quantity && $initial_quantity !== $quantity && $quantity === 0) { + $this->runHook('product', 'out-of-stock', new Plugin\Event\Variables([ + 'order_id' => $id_order, + 'product_id' => $product_id, + //'data' => $params, + ])); + } + } + } + + //OK + public function hookChangeOrderStatusBefore(string $route, array $params) + { + if ($this->request->get['call'] !== 'history_add') { + return; + } + + $this->load->model('checkout/order'); + + $this->order_status_state = (new State(fn() => (int) $this->model_checkout_order->getOrder($this->request->post['order_id'])['order_status_id'])) + ->captureInitial() + ->setExpected((int) $this->request->post['order_status_id']); + } + + public function hookChangeOrderStatusAfter(string $route, array $params) + { + if ($this->order_status_state === null) { + return; + } + + $this->order_status_state->captureActual(); + + if (!$this->order_status_state->shouldRunHook()) { + return; + } + + $this->runHook('order', 'change-status', new Plugin\Event\Variables([ + 'order_id' => $this->request->post['order_id'], + 'order_status_id' => $this->request->post['order_status_id'], + //'debug' => $this->order_status_state->debug(), + ])); + } + + //OK + public function hookAddCustomer(string $route, array $params, int $id_customer) + { + $this->runHook('customer', 'new', new Plugin\Event\Variables([ + 'customer_id' => $id_customer, + //'data' => $params, + ])); + } + + //OK + public function hookContactForm(string $route) + { + $this->runHook('contact', 'form', new Plugin\Event\Variables([ + 'shop_id' => $this->config->get('config_store_id'), + 'customer_email' => $this->request->post['email'], + 'customer_name' => $this->request->post['name'], + 'customer_message' => $this->request->post['enquiry'], + //'data' => $this->request->post, + ])); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4f4a5c7 --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "bulkgate/cartsms", + "type": "opencart-module", + "minimum-stability": "RC", + "require": { + "php": ">= 8.0", + "bulkgate/plugin": "dev-master" + }, + "require-dev": { + "tracy/tracy": "^2.9", + "nette/tester": "^2.4.3", + "phpstan/phpstan": "^1.10", + "mockery/mockery": "^1.6" + }, + "autoload": { + "psr-4": { + "BulkGate\\CartSms\\": "src/" + }, + "classmap": [ + "src/" + ] + }, + "scripts": { + "tester": "tester -C tests --colors=1", + "coverage": "tester -C --coverage=coverage.html --coverage-src=src --stop-on-fail", + "phpstan": "phpstan analyse -c phpstan.neon --memory-limit=1G", + "prestashop-lint": "php-cs-fixer fix" + }, + "config": { + "prepend-autoloader": false + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3f7a883 --- /dev/null +++ b/composer.lock @@ -0,0 +1,416 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "53d0de7a304084b8818d70d44295ce87", + "packages": [ + { + "name": "bulkgate/plugin", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/BulkGate/plugin.git", + "reference": "f1fbe7b48261462bfc87456c71d74c5824169377" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/BulkGate/plugin/zipball/f1fbe7b48261462bfc87456c71d74c5824169377", + "reference": "f1fbe7b48261462bfc87456c71d74c5824169377", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-zlib": "*", + "php": ">=7.4" + }, + "require-dev": { + "mockery/mockery": "1.4.2", + "nette/tester": "^2", + "phpstan/phpstan": "^1.9", + "rector/rector": "^0.16", + "tracy/tracy": "^2.9" + }, + "default-branch": true, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "authors": [ + { + "name": "Lukáš Piják", + "email": "lukaspijak@gmail.com" + } + ], + "description": "Meta package for BulkGate plugins", + "support": { + "issues": "https://github.com/BulkGate/plugin/issues", + "source": "https://github.com/BulkGate/plugin/tree/1.0.1" + }, + "time": "2023-12-06T12:19:36+00:00" + } + ], + "packages-dev": [ + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "nette/tester", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/nette/tester.git", + "reference": "c11863785779e87b40adebf150364f2e5938c111" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/tester/zipball/c11863785779e87b40adebf150364f2e5938c111", + "reference": "c11863785779e87b40adebf150364f2e5938c111", + "shasum": "" + }, + "require": { + "php": "8.0 - 8.4" + }, + "require-dev": { + "ext-simplexml": "*", + "phpstan/phpstan": "^1.0" + }, + "bin": [ + "src/tester" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Miloslav Hůla", + "homepage": "https://github.com/milo" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "Nette Tester: enjoyable unit testing in PHP with code coverage reporter. 🍏🍏🍎🍏", + "homepage": "https://tester.nette.org", + "keywords": [ + "Xdebug", + "assertions", + "clover", + "code coverage", + "nette", + "pcov", + "phpdbg", + "phpunit", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/nette/tester/issues", + "source": "https://github.com/nette/tester/tree/v2.5.4" + }, + "time": "2024-10-23T23:57:10+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.12.16", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-01-21T14:50:05+00:00" + }, + { + "name": "tracy/tracy", + "version": "v2.10.9", + "source": { + "type": "git", + "url": "https://github.com/nette/tracy.git", + "reference": "e7af75205b184ca8895bc57fafd331f8d5022d26" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/tracy/zipball/e7af75205b184ca8895bc57fafd331f8d5022d26", + "reference": "e7af75205b184ca8895bc57fafd331f8d5022d26", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-session": "*", + "php": "8.0 - 8.4" + }, + "conflict": { + "nette/di": "<3.0" + }, + "require-dev": { + "latte/latte": "^2.5 || ^3.0", + "nette/di": "^3.0", + "nette/http": "^3.0", + "nette/mail": "^3.0 || ^4.0", + "nette/tester": "^2.2", + "nette/utils": "^3.0 || ^4.0", + "phpstan/phpstan": "^1.0", + "psr/log": "^1.0 || ^2.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.10-dev" + } + }, + "autoload": { + "files": [ + "src/Tracy/functions.php" + ], + "classmap": [ + "src" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.", + "homepage": "https://tracy.nette.org", + "keywords": [ + "Xdebug", + "debug", + "debugger", + "nette", + "profiler" + ], + "support": { + "issues": "https://github.com/nette/tracy/issues", + "source": "https://github.com/nette/tracy/tree/v2.10.9" + }, + "time": "2024-11-07T14:48:00+00:00" + } + ], + "aliases": [], + "minimum-stability": "RC", + "stability-flags": { + "bulkgate/plugin": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">= 7.4" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/dist/CartSMS_6.00_RC1_for_OpenCart_30xx_1535027082.zip b/dist/CartSMS_6.00_RC1_for_OpenCart_30xx_1535027082.zip deleted file mode 100644 index b60d487..0000000 Binary files a/dist/CartSMS_6.00_RC1_for_OpenCart_30xx_1535027082.zip and /dev/null differ diff --git a/distribution.php b/distribution.php new file mode 100644 index 0000000..4921331 --- /dev/null +++ b/distribution.php @@ -0,0 +1,16 @@ + Affiliate program. +const BulkGateAffiliateId = ''; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..728ac38 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +services: + opencart: + image: ghcr.io/bulkgate/cartsms:${OC_VERSION:-latest} + depends_on: + - db + ports: + - "8083:80" + volumes: + - ./admin:/var/www/html/extension/oc_cartsms/admin + - ./catalog:/var/www/html/extension/oc_cartsms/catalog + - ./src:/var/www/html/extension/oc_cartsms/src + - ./tests:/var/www/html/extension/oc_cartsms/tests + - ./vendor:/var/www/html/extension/oc_cartsms/vendor + - ./install.json:/var/www/html/extension/oc_cartsms/install.json + - ./distribution.php:/var/www/html/extension/oc_cartsms/distribution.php + + db: + image: mysql:5.7 + environment: + MYSQL_DATABASE: opencart + MYSQL_ROOT_PASSWORD: admin + volumes: + - db:/var/lib/mysql + + adminer: + image: adminer + environment: + ADMINER_DESIGN: "nette" + ADMINER_DEFAULT_SERVER: "db" + ports: + - "8090:8080" + +volumes: + db: \ No newline at end of file diff --git a/install.json b/install.json new file mode 100644 index 0000000..27887e6 --- /dev/null +++ b/install.json @@ -0,0 +1,7 @@ +{ + "name": "BulkGate SMS", + "description": "Make your SMS messages stand out from the crowd! In a world of overflowing inboxes, SMS, RCS, and WhatsApp offer a unique way to grab your customers' attention and ensure your important notifications are seen.", + "version": "4.0", + "author": "BulkGate", + "link": "https://www.bulkgate.com/en/integrations/cartsms-sms-module-for-opencart/" +} \ No newline at end of file diff --git a/install.php b/install.php deleted file mode 100644 index 415ec25..0000000 --- a/install.php +++ /dev/null @@ -1,17 +0,0 @@ -registry); - @$modification_controller->install(); - - $this->load->model('extension/extension'); - $this->model_extension_extension->uninstall('module', "cartsms"); - @$this->model_extension_extension->install('module', "cartsms"); - -die; - - diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..95fb9c4 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 8 + paths: + - src + scanDirectories: + - /var/www/html # opencart installation \ No newline at end of file diff --git a/src/Ajax/Authenticate.php b/src/Ajax/Authenticate.php new file mode 100644 index 0000000..52b78a3 --- /dev/null +++ b/src/Ajax/Authenticate.php @@ -0,0 +1,27 @@ + + */ + public function run(string $invalid_redirect): array + { + + return $this->settings->load('static:application_token') === null + ? + ['redirect' => $invalid_redirect] + : + ['token' => $this->sign->authenticate(false, ['expire' => time() + 300])]; + } +} \ No newline at end of file diff --git a/src/Ajax/PluginSettings.php b/src/Ajax/PluginSettings.php new file mode 100644 index 0000000..26f8228 --- /dev/null +++ b/src/Ajax/PluginSettings.php @@ -0,0 +1,67 @@ + $unsafe_post_data + * @return array{data:array} + */ + public function run(array $unsafe_post_data, callable $on_language_change): array + { + $output = []; + + $actual_language = $this->settings->load('main:language'); + + if (isset($unsafe_post_data['marketing_message_opt_in_url']) && is_scalar($unsafe_post_data['marketing_message_opt_in_url'])) + { + $unsafe_post_data['marketing_message_opt_in_url'] = self::formatUrl((string) $unsafe_post_data['marketing_message_opt_in_url']); + } + + $this->change('dispatcher', $unsafe_post_data, $output); + $this->change('synchronization', $unsafe_post_data, $output); + $this->change('language', $unsafe_post_data, $output); + $this->change('language_mutation', $unsafe_post_data, $output, 'bool'); + $this->change('delete_db', $unsafe_post_data, $output, 'bool'); + $this->change('address_preference', $unsafe_post_data, $output); + $this->change('marketing_message_opt_in_enabled', $unsafe_post_data, $output, 'bool'); + $this->change('marketing_message_opt_in_label', $unsafe_post_data, $output); + $this->change('marketing_message_opt_in_default', $unsafe_post_data, $output, 'bool'); + $this->change('marketing_message_opt_in_url', $unsafe_post_data, $output); + + $this->synchronizer->synchronize(true); + + if (isset($unsafe_post_data['language']) && $actual_language !== $unsafe_post_data['language']) { + // language change requires hard reload for re-initialization languages. + // we are using reload_lang parameter to handle url change and effectively reloads the page + return ['data' => ['redirect' => $on_language_change($unsafe_post_data['language'])]]; + } + + return ['data' => ['layout' => ['server' => ['application_settings' => $output]]], 'success' => ['saved']]; + } + + /** + * @param array $unsafe_data + * @param array $output + */ + private function change(string $key, array $unsafe_data, array &$output, string $type = 'string'): void + { + if (isset($unsafe_data[$key]) && \is_scalar($unsafe_data[$key])) { + $value = Plugin\Settings\Helpers::deserializeValue((string) $unsafe_data[$key], $type); + + $this->settings->set("main:$key", $output[$key] = $value, ['type' => $type]); + } + } + + private static function formatUrl(string $url): string + { + return ((int) preg_match('~^[A-Za-z]+?://~', $url)) !== 0 ? $url : "https://$url"; + } +} \ No newline at end of file diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php new file mode 100644 index 0000000..8ff8d8d --- /dev/null +++ b/src/Controller/Controller.php @@ -0,0 +1,77 @@ + [ + 'registry' => $this->registry, + 'debug' => true, + 'dispatcher' => Plugin\Event\Dispatcher::Asset, + 'api_version' => '1.0', + 'module_version' => '4.0', + /** @phpstan-ignore property.notFound */ + 'name' => $this->model_setting_setting->getValue('config_name'), + 'url' => '', + 'gate_url' => 'http://192.168.16.1', //BulkGateWhiteLabelUrl, + 'default_settings' => [ + "main:dispatcher" => 'asset', + "main:synchronization" => 'all', + "main:language" => 'auto', + "main:language_mutation" => false, + "main:delete_db" => false, + "main:address_preference" => 'delivery', + "main:marketing_message_opt_in_enabled" => false, + "main:marketing_message_opt_in_label" => '', + "main:marketing_message_opt_in_default" => false, + "main:marketing_message_opt_in_url" => '' + ] + ]); + + $this->di_container = Factory::get(); + } + + /** + * @param array $parameters + */ + protected function runHook(string $category, string $endpoint, Plugin\Event\Variables $variables, array $parameters = [], ?callable $success_callback = null): void + { + $hook = join('.', [$category, $endpoint]); + $run = true; + + // automatically get admin + if ($this->registry->has('user') && $this->user->isLogged()) { + $variables['employee_id'] = $this->user->getId(); + } + + // we give chance to stop hook + $this->event->trigger('cartsms.hook.run', [$hook, $variables->toArray(), &$run]); + + if ($run === false) + { + $this->di_container->getByClass(Plugin\Debug\Logger::class)->log("Hook event filter: 'cartsms.hook.run' stop execution of hook '$hook'", 'warning'); + return; + } + + $dispatcher = $this->di_container->getByClass(Plugin\Event\Dispatcher::class); + + $dispatcher->dispatch($category, $endpoint, $variables, $parameters, $success_callback); + } +} \ No newline at end of file diff --git a/src/DI/Factory.php b/src/DI/Factory.php new file mode 100644 index 0000000..052ef98 --- /dev/null +++ b/src/DI/Factory.php @@ -0,0 +1,244 @@ + $parameters + */ + protected static function createContainer(array $parameters = []): Plugin\DI\Container + { + ['registry' => $registry] = $parameters; + //bdump($parameters); + //bdump($registry->has('user'), 'HAS_USER'); + + $iso = $registry->language->get('code'); + + $container = new Plugin\DI\Container($parameters['mode'] ?? 'strict'); + + if (($parameters['debug'] ?? false) && class_exists(Debugger::class)) + { + Debugger::$strictMode = true; + Debugger::$maxDepth = 10; + Debugger::$logDirectory = DIR_LOGS; + Debugger::enable(Debugger::Development); + } + + // Database + $container['database.connection'] = ['factory' => Database\Connection::class, 'parameters' => ['db' => $registry->get('db')]]; + + // Debug + $container['debug.repository.logger'] = ['factory' => Plugin\Debug\Repository\LoggerSettings::class, 'factory_method' => function () use ($container, $parameters): Plugin\Debug\Repository\LoggerSettings + { + $logger = new Plugin\Debug\Repository\LoggerSettings($container->getByClass(Plugin\Settings\Settings::class)); + $logger->setup(is_int($parameters['logger_limit'] ?? null) ? $parameters['logger_limit'] : 100); + + return $logger; + }]; + $container['debug.logger'] = Plugin\Debug\Logger::class; + $container['debug.requirements'] = Plugin\Debug\Requirements::class; + + // Ajax + $container['ajax.authenticate'] = Ajax\Authenticate::class; + $container['ajax.plugin_settings'] = Ajax\PluginSettings::class; + + // Eshop + $container['eshop.configuration'] = ['factory' => Eshop\Configuration::class, 'factory_method' => fn () => new Eshop\Configuration($parameters['module_version'], $parameters['url'], $parameters['name'] ?? 'Store')]; + $container['eshop.synchronizer'] = Plugin\Eshop\EshopSynchronizer::class; + $container['eshop.order_status'] = ['factory' => Eshop\OrderStatus::class, 'factory_method' => function() use ($registry) + { + $registry->load->model('localisation/order_status'); + + return new Eshop\OrderStatus($registry->model_localisation_order_status); + }]; + $container['eshop.return_status'] = ['factory' => Eshop\ReturnStatus::class, 'factory_method' => function() use ($registry) + { + $registry->load->model('localisation/return_status'); + + return new Eshop\ReturnStatus($registry->model_localisation_return_status); + }]; + $container['eshop.language'] = ['factory' => Eshop\Language::class, 'factory_method' => function() use ($registry) + { + $registry->load->model('localisation/language'); + + return new Eshop\Language($registry->model_localisation_language); + }]; + $container['eshop.multistore'] = ['factory' => Eshop\MultiStore::class, 'factory_method' => function() use ($registry, $container) + { + $registry->load->model('setting/store'); + + return new Eshop\MultiStore($container->getByClass(Eshop\Configuration::class), $registry->model_setting_store); + }]; + + // Event loaders + $container['event.loader.extension'] = ['factory' => Event\Loader\Extension::class, 'auto_wiring' => false, 'parameters' => ['event' => $registry->event]/*, 'factory_method' => fn () => new Event\Loader\Extension($registry->event)*/]; + $container['event.loader.shop'] = ['factory' => Event\Loader\Shop::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry) + { + $registry->load->model('setting/setting'); + $registry->load->model('localisation/language'); + + return new Event\Loader\Shop($registry->model_setting_setting, $registry->model_localisation_language, $registry->request); + }]; + $container['event.loader.order'] = ['factory' => Event\Loader\Order::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry, $container) + { + if ($registry->has('user')) //admin + { + $registry->load->model('sale/order'); + $order_model = $registry->model_sale_order; + } + else + { + $registry->load->model('checkout/order'); + $order_model = $registry->model_checkout_order; + } + + return new Event\Loader\Order($order_model, $container->getByClass(Plugin\Localization\Formatter::class)); + }]; + $container['event.loader.order_return'] = ['factory' => Event\Loader\OrderReturn::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry, $container) + { + if ($registry->has('user')) //admin + { + $registry->load->model('sale/returns'); + $return_model = $registry->model_sale_returns; + } + else + { + $registry->load->model('account/returns'); + $return_model = $registry->model_account_returns; + } + + return new Event\Loader\OrderReturn($return_model, $container->getByClass(Plugin\Localization\Formatter::class)); + }]; + $container['event.loader.order_return_status'] = ['factory' => Event\Loader\OrderReturnStatus::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry) + { + $registry->load->model('localisation/return_status'); + + return new Event\Loader\OrderReturnStatus($registry->model_localisation_return_status); + }]; + $container['event.loader.order_status'] = ['factory' => Event\Loader\OrderStatus::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry) + { + $registry->load->model('localisation/order_status'); + + return new Event\Loader\OrderStatus($registry->model_localisation_order_status); + }]; + $container['event.loader.customer'] = ['factory' => Event\Loader\Customer::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry) + { + if ($registry->has('user')) //admin + { + $registry->load->model('customer/customer'); + $customer_model = $registry->model_customer_customer; + } + else + { + $registry->load->model('account/customer'); + $registry->load->model('account/address'); + + $customer_model = new class ($registry->model_account_customer, $registry->model_account_address) { + private $customer_id; + + public function __construct(private $customer_model, private $address_model) + { + } + + public function getCustomer(int $customer_id): array + { + $this->customer_id = $customer_id; + return $this->customer_model->getCustomer($customer_id); + } + + public function getAddress(int $address_id): array + { + return $this->address_model->getAddress($this->customer_id, $address_id); + } + }; + } + + return new Event\Loader\Customer($customer_model); + }]; + $container['event.loader.admin'] = ['factory' => Event\Loader\Admin::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry) + { + $registry->load->model('user/user'); + + return new Event\Loader\Admin($registry->model_user_user); + }]; + $container['event.loader.product'] = ['factory' => Event\Loader\Product::class, 'auto_wiring' => false, 'factory_method' => function() use ($registry, $container) + { + $registry->load->model('catalog/product'); + $registry->load->model('catalog/manufacturer'); + + return new Event\Loader\Product($registry->model_catalog_product, $registry->model_catalog_manufacturer, $container->getByClass(Plugin\Localization\Formatter::class)); + }]; + + // Event + $container['event.hook'] = ['factory' => Plugin\Event\Hook::class, 'parameters' => ['version' => $parameters['api_version'] ?? '1.0']]; + $container['event.asynchronous.repository'] = Plugin\Event\Repository\AsynchronousDatabase::class; + $container['event.asynchronous'] = Plugin\Event\Asynchronous::class; + + $container['event.loader'] = ['factory' => Plugin\Event\Loader::class, 'factory_method' => function () use ($registry, $container) + { + $loaders = [ + $container->getByClass(Event\Loader\OrderReturn::class), + $container->getByClass(Event\Loader\Order::class), + $container->getByClass(Event\Loader\OrderStatus::class), + $container->getByClass(Event\Loader\Customer::class), + $container->getByClass(Event\Loader\Product::class), + $container->getByClass(Event\Loader\Shop::class), + ]; + + if ($registry->has('user')) //admin + { + $loaders[] = $container->getByClass(Event\Loader\OrderReturnStatus::class); + $loaders[] = $container->getByClass(Event\Loader\Admin::class); + } + + $loaders[] = $container->getByClass(Event\Loader\Extension::class); + + return new Plugin\Event\Loader($loaders); + }]; + $container['event.dispatcher'] = Plugin\Event\Dispatcher::class; + + // IO + $container['io.connection.factory'] = ['factory' => Plugin\IO\ConnectionFactory::class, 'factory_method' => function () use ($container): Plugin\IO\ConnectionFactory + { + $configuration = $container->getByClass(Eshop\Configuration::class); + + return new Plugin\IO\ConnectionFactory($configuration->url(), $configuration->product(), $container->getByClass(Plugin\Settings\Settings::class)); + }]; + $container['io.connection'] = ['factory' => Plugin\IO\Connection::class, 'factory_method' => fn () => $container->getByClass(Plugin\IO\ConnectionFactory::class)->create()]; + $container['io.url'] = ['factory' => Plugin\IO\Url::class, 'parameters' => ['url' => $parameters['gate_url'] ?? 'https://portal.bulkgate.com']]; + + // Localization + $container['localization.language'] = ['factory' => Plugin\Localization\LanguageSettings::class, 'parameters' => ['iso' => $iso]]; + $container['localization.translator'] = Plugin\Localization\TranslatorSettings::class; + $container['localization.formatter'] = extension_loaded('intl') ? ['factory' => Plugin\Localization\FormatterIntl::class, 'factory_method' => fn () => new Plugin\Localization\FormatterIntl($iso)] : Plugin\Localization\FormatterBasic::class; + + // Settings + $container['settings.repository.database'] = Plugin\Settings\Repository\SettingsDatabase::class; + $container['settings.settings'] = ['factory' => Plugin\Settings\Settings::class, 'factory_method' => function () use ($container, $parameters): Plugin\Settings\Settings + { + $settings = new Plugin\Settings\Settings($container->getByClass(Plugin\Settings\Repository\SettingsDatabase::class)); + $settings->setDefaultSettings($parameters['default_settings']); + + return $settings; + }]; + $container['settings.repository.synchronizer'] = Plugin\Settings\Repository\SynchronizationDatabase::class; + $container['settings.synchronizer'] = Plugin\Settings\Synchronizer::class; + + // User + $container['user.sign'] = Plugin\User\Sign::class; + + return $container; + } +} \ No newline at end of file diff --git a/src/Database/Connection.php b/src/Database/Connection.php new file mode 100644 index 0000000..5abf94e --- /dev/null +++ b/src/Database/Connection.php @@ -0,0 +1,74 @@ + + */ + private array $sql = []; + + public function __construct(OpencartDB $db) + { + $this->db = $db; + } + + public function execute(string $sql): Plugin\Database\ResultCollection + { + $output = new Plugin\Database\ResultCollection(); + + $this->sql[] = $sql; + + $result = (array) $this->db->query($sql); + + foreach ($result['rows'] ?? [] as $key => $item) { + $output[$key] = (array) $item; + } + + return $output; + } + + public function lastId(): int + { + return $this->db->getLastId(); + } + + public function prefix(): string + { + return DB_PREFIX; + } + + public function getSqlList(): array + { + return $this->sql; + } + + public function table(string $table): string + { + return $this->prefix() . $table; + } + + public function prepare(string $sql, ...$parameters): string + { + return sprintf($sql, ...array_map(fn ($value) => "'$value'", array_map([$this->db, 'escape'], $parameters))); + } + + public function escape(string $string): string + { + return $this->db->escape($string); + } +} diff --git a/src/Eshop/Configuration.php b/src/Eshop/Configuration.php new file mode 100644 index 0000000..42527d8 --- /dev/null +++ b/src/Eshop/Configuration.php @@ -0,0 +1,53 @@ +version_number = $version_number; + $this->site_url = $site_url; + $this->site_name = $site_name; + } + + + public function url(): string + { + return $this->site_url; + } + + + public function product(): string + { + return 'oc'; + } + + + public function version(): string + { + return $this->version_number; + } + + + public function name(): string + { + return $this->site_name; + } +} \ No newline at end of file diff --git a/src/Eshop/Language.php b/src/Eshop/Language.php new file mode 100644 index 0000000..91d3042 --- /dev/null +++ b/src/Eshop/Language.php @@ -0,0 +1,46 @@ +language->getLanguages() as $language) + { + $language_list[$language['code']] = $language['name']; + } + + return $language_list; + } + + + public function get(?int $id = 1): string + { + return $this->language->getLanguage((int) $id)['code']; + } + + + public function hasMultiLanguageSupport(): bool + { + return true; + } +} \ No newline at end of file diff --git a/src/Eshop/MultiStore.php b/src/Eshop/MultiStore.php new file mode 100644 index 0000000..02855ea --- /dev/null +++ b/src/Eshop/MultiStore.php @@ -0,0 +1,35 @@ + $this->configuration->name()]; + + foreach($this->store->getStores() as $store) + { + $store_list[$store['store_id']] = $store['name']; + } + + return $store_list; + } +} \ No newline at end of file diff --git a/src/Eshop/OrderStatus.php b/src/Eshop/OrderStatus.php new file mode 100644 index 0000000..4e11bc6 --- /dev/null +++ b/src/Eshop/OrderStatus.php @@ -0,0 +1,32 @@ +order_status->getOrderStatuses() as $status) + { + $status_list[$status['order_status_id']] = $status['name']; + } + + return $status_list; + } +} \ No newline at end of file diff --git a/src/Eshop/ReturnStatus.php b/src/Eshop/ReturnStatus.php new file mode 100644 index 0000000..8c5670b --- /dev/null +++ b/src/Eshop/ReturnStatus.php @@ -0,0 +1,33 @@ +return_status->getReturnStatuses() as $return_status) { + $return_status_list[$return_status['return_status_id']] = $return_status['name']; + } + + return $return_status_list; + } +} \ No newline at end of file diff --git a/src/Event/Helpers.php b/src/Event/Helpers.php new file mode 100644 index 0000000..e29357f --- /dev/null +++ b/src/Event/Helpers.php @@ -0,0 +1,33 @@ + $priority + * @param ArrayAccess|array $values + */ + public static function priorityValues(array $priority, ArrayAccess|array $values, mixed $default = null): mixed + { + foreach ($priority as $key) + { + if ($values[$key] ?? false) + { + return $values[$key]; + } + } + + return $default; + } +} diff --git a/src/Event/Loader/Admin.php b/src/Event/Loader/Admin.php new file mode 100644 index 0000000..2762895 --- /dev/null +++ b/src/Event/Loader/Admin.php @@ -0,0 +1,28 @@ +admin_model->getUser((int) $variables['employee_id']); + + $variables['employee_email'] = $admin['email']; + $variables['employee_firstname'] = $admin['firstname']; + $variables['employee_lastname'] = $admin['lastname']; + } +} \ No newline at end of file diff --git a/src/Event/Loader/Customer.php b/src/Event/Loader/Customer.php new file mode 100644 index 0000000..bbf46d7 --- /dev/null +++ b/src/Event/Loader/Customer.php @@ -0,0 +1,54 @@ +customer_model->getCustomer((int) $variables['customer_id']); + + $variables['shop_id'] ??= $customer['store_id'] ?? null; + $variables['lang_id'] ??= $customer['language_id'] ?? null; + + $variables['customer_mobile'] ??= $customer['telephone']; + $variables['customer_email'] ??= $customer['email']; + + /*$billing = $this->customer_model->getAddress((int) $variables['id_address_invoice']); + $shipping = $this->customer_model->getAddress((int) $variables['id_address_delivery']); + + $variables['customer_firstname'] = Plugin\Event\Helpers::address('firstname', $shipping, $billing); + $variables['customer_lastname'] = Plugin\Event\Helpers::address('lastname', $shipping, $billing); + $variables['customer_company'] = Plugin\Event\Helpers::address('company', $shipping, $billing); + $variables['customer_address'] = Plugin\Event\Helpers::joinStreet('address_1', 'address_2', $shipping, $billing); + $variables['customer_city'] = Plugin\Event\Helpers::address('city', $shipping, $billing); + $variables['customer_state'] = Plugin\Event\Helpers::address('zone', $shipping, $billing); + $variables['customer_postcode'] = Plugin\Event\Helpers::address('postcode', $shipping, $billing); + $variables['customer_country'] = Plugin\Event\Helpers::address('country', $shipping, $billing); + $variables['customer_country_id'] = Plugin\Utils\Strings::lower(Plugin\Event\Helpers::address('iso_code_2', $shipping, $billing) ?? ""); + + $variables['customer_invoice_firstname'] = Plugin\Event\Helpers::address('firstname', $billing, $shipping); + $variables['customer_invoice_lastname'] = Plugin\Event\Helpers::address('lastname', $billing, $shipping); + $variables['customer_invoice_company'] = Plugin\Event\Helpers::address('company', $billing, $shipping); + $variables['customer_invoice_address'] = Plugin\Event\Helpers::joinStreet('address_1', 'address_2', $billing, $shipping); + $variables['customer_invoice_city'] = Plugin\Event\Helpers::address('city', $billing, $shipping); + $variables['customer_invoice_state'] = Plugin\Event\Helpers::address('zone', $billing, $shipping); + $variables['customer_invoice_postcode'] = Plugin\Event\Helpers::address('postcode', $billing, $shipping); + $variables['customer_invoice_country'] = Plugin\Event\Helpers::address('country', $billing, $shipping); + $variables['customer_invoice_country_id'] = Plugin\Utils\Strings::lower(Plugin\Event\Helpers::address('iso_code_2', $billing, $shipping) ?? "");*/ + + } +} \ No newline at end of file diff --git a/src/Event/Loader/Extension.php b/src/Event/Loader/Extension.php new file mode 100644 index 0000000..8fe5aa4 --- /dev/null +++ b/src/Event/Loader/Extension.php @@ -0,0 +1,20 @@ +event->trigger('cartsms.hook.extension', [$variables, $parameters]); + } +} \ No newline at end of file diff --git a/src/Event/Loader/Order.php b/src/Event/Loader/Order.php new file mode 100644 index 0000000..7d5c3e0 --- /dev/null +++ b/src/Event/Loader/Order.php @@ -0,0 +1,134 @@ +order_model->getOrder((int) $variables['order_id']); + + $shipping_address = $this->address($order, 'shipping_'); + $billing_address = $this->address($order, 'payment_'); + + foreach ($shipping_address as $key => $value) { + if ($key === 'address_2') { + continue; + } + if ($key === 'address_1') { + $variables["customer_address"] = Plugin\Event\Helpers::joinStreet('address_1', 'address_2', $shipping_address, $billing_address); + $variables["customer_invoice_address"] = Plugin\Event\Helpers::joinStreet('address_1', 'address_2', $billing_address, $shipping_address); + continue; + } + $variables["customer_{$key}"] = Plugin\Event\Helpers::address($key, $shipping_address, $billing_address); + $variables["customer_invoice_{$key}"] = Plugin\Event\Helpers::address($key, $billing_address, $shipping_address); + } + + $variables['customer_mobile'] = $order['telephone']; + $variables['customer_email'] = $order['email']; + + $variables['shop_id'] ??= $order['store_id'] ?? null; + $variables['lang_id'] ??= $order['language_id'] ?? null; + $variables['customer_id'] ??= $order['customer_id'] ?? null; + $variables['id_address_delivery'] ??= $order['shipping_address_id'] ?? null; + $variables['id_address_invoice'] ??= $order['payment_address_id'] ?? null; + $variables['order_status_id'] ??= $order['order_status_id'] ?? null; + + $variables['order_currency'] = $order['currency_code']; + $variables['long_order_id'] = sprintf("%06d", $variables['order_id']); + $variables['order_total_locale'] = $this->formatter->format('price', $order['total'], $variables['order_currency']); + $variables['order_total_paid'] = $order['total']; + $variables['order_payment'] = $order['payment_method']['name']; + $variables['order_tracking'] = $order['tracking']; + $variables['order_message'] = $order['comment']; + + $timestamp = strtotime($order['date_added']) ?: time(); + + $variables['order_date'] = $this->formatter->format('date', $order['date_added']); + $variables['order_date1'] = date('d.m.Y', $timestamp); + $variables['order_date2'] = date('d/m/Y', $timestamp); + $variables['order_date3'] = date('d-m-Y', $timestamp); + $variables['order_date4'] = date('Y-m-d', $timestamp); + $variables['order_date5'] = date('m.d.Y', $timestamp); + $variables['order_date6'] = date('m/d/Y', $timestamp); + $variables['order_date7'] = date('m-d-Y', $timestamp); + $variables['order_datetime'] = $this->formatter->format('datetime', $order['date_added']); + $variables['order_time'] = $this->formatter->format('time', $order['date_added']); + $variables['order_time1'] = date('H:i:s', $timestamp); + + $variables['order_carrier_name'] = $order['shipping_method']['name'] ?? null; + $variables['order_carrier_price'] = $order['shipping_method']['cost'] ?? null; + $variables['order_carrier_price_locale'] = $this->formatter->format('price', $variables['order_carrier_price'], $variables['order_currency']); + $variables['order_carrier_code'] = $order['shipping_method']['code'] ?? null; + + $v1 = $v2 = $v3 = $v4 = $p1 = $p2 = []; + + foreach ($order['products'] as $product) + { + $qty = $product['quantity']; + $name = $product['name']; + $model = $product['model']; + $total = $product['total'] + $product['tax']; + + $product_id = $product['order_product_id']; + $total_formatted = $this->formatter->format('price', $total, $variables['order_currency']); + + $v1[] = "{$qty}x $name $model $total_formatted"; + $v2[] = "{$qty}x $name $total_formatted"; + $v3[] = "{$qty}x ($product_id) $name $model $total_formatted"; + $v4[] = "{$qty}x $model $total_formatted"; + + $p1[] = "$qty,$name,$total"; + $p2[] = "$qty;$name;$total"; + } + + $variables['order_products1'] = implode('; ', $v1); + $variables['order_products2'] = implode('; ', $v2); + $variables['order_products3'] = implode('; ', $v3); + $variables['order_products4'] = implode('; ', $v4); + + $variables['order_products5'] = implode("\n", $v1); + $variables['order_products6'] = implode("\n", $v2); + $variables['order_products7'] = implode("\n", $v3); + $variables['order_products8'] = implode("\n", $v4); + + $variables['order_smsprinter1'] = implode(';', $p1); + $variables['order_smsprinter2'] = implode(';', $p2); + } + + /** + * @param array $order + * @return array + */ + private function address(array $order, string $order_prefix): array + { + $address = []; + + $address['firstname'] = $order["{$order_prefix}firstname"]; + $address['lastname'] = $order["{$order_prefix}lastname"]; + $address['company'] = $order["{$order_prefix}company"]; + $address['address_1'] = $order["{$order_prefix}address_1"]; + $address['address_2'] = $order["{$order_prefix}address_2"] ?? ''; + $address['city'] = $order["{$order_prefix}city"]; + $address['state'] = $order["{$order_prefix}zone"]; + $address['postcode'] = $order["{$order_prefix}postcode"]; + $address['country'] = $order["{$order_prefix}country"]; + $address['country_id'] = Plugin\Utils\Strings::lower($order["{$order_prefix}iso_code_2"] ?? ""); + + return $address; + + } +} \ No newline at end of file diff --git a/src/Event/Loader/OrderReturn.php b/src/Event/Loader/OrderReturn.php new file mode 100644 index 0000000..e629382 --- /dev/null +++ b/src/Event/Loader/OrderReturn.php @@ -0,0 +1,44 @@ +order_return_model->getReturn((int) $variables['return_id']); + + $variables['lang_id'] ??= $order_return['language_id'] ?? null; + $variables['order_id'] ??= $order_return['order_id'] ?? null; + $variables['customer_id'] ??= $order_return['customer_id'] ?? null; + $variables['return_status_id'] ??= $order_return['return_status_id'] ?? null; + + $variables['return_customer_message'] = $order_return['comment']; + $variables['return_date'] = $this->formatter->format('date', $order_return['date_added']); + + // pro customera + administratora + $variables['return_action'] = $order_return['action'] ?? null; + $variables['return_action_id'] = $order_return['return_action_id']; + $variables['return_reason_id'] = $order_return['return_reason_id']; + $variables['return_reason'] = $order_return['reason'] ?? null; + + //pouze pro administratora + $variables['return_status'] = $order_return['return_status'] ?? null; + + $variables['return_products1'] = $order_return['quantity'] . 'x ' . $order_return['product'] . ' ' . $order_return['product_id']; + $variables['return_products2'] = $order_return['quantity'] . 'x ' . $order_return['product']; + $variables['return_products3'] = $order_return['quantity'] . 'x (' . $order_return['product_id'] . ') ' . $order_return['product']; + $variables['return_products4'] = $order_return['quantity'] . 'x ' . $order_return['product_id']; + } +} \ No newline at end of file diff --git a/src/Event/Loader/OrderReturnStatus.php b/src/Event/Loader/OrderReturnStatus.php new file mode 100644 index 0000000..ab23c60 --- /dev/null +++ b/src/Event/Loader/OrderReturnStatus.php @@ -0,0 +1,26 @@ +order_return_status_model->getReturnStatus((int) $variables['return_status_id']); + + $variables['return_status'] = $return_status['name']; + } +} \ No newline at end of file diff --git a/src/Event/Loader/OrderStatus.php b/src/Event/Loader/OrderStatus.php new file mode 100644 index 0000000..60c4e64 --- /dev/null +++ b/src/Event/Loader/OrderStatus.php @@ -0,0 +1,26 @@ +order_status_model->getOrderStatus((int) $variables['order_status_id']); + + $variables['order_status'] = $status['name']; + } +} \ No newline at end of file diff --git a/src/Event/Loader/Product.php b/src/Event/Loader/Product.php new file mode 100644 index 0000000..37a07c6 --- /dev/null +++ b/src/Event/Loader/Product.php @@ -0,0 +1,50 @@ +product_model->getProduct((int) $variables['product_id']); + + $variables['shop_id'] ??= $product['store_id'] ?? null; + $variables['lang_id'] ??= $product['language_id'] ?? null; + + $variables['product_quantity'] = $product['quantity']; + $variables['product_minimal_quantity'] = $product['minimum']; + $variables['product_name'] = $product['name']; + $variables['product_model'] = $product['model']; + $variables['product_description'] = \strip_tags(html_entity_decode($product['description'])); + $manufacturer_id = (int) $product['manufacturer_id']; + + if ($manufacturer_id) { + $variables['product_manufacturer'] = $this->manufacturer_model->getManufacturer($manufacturer_id)['name']; + } + + //todo: TAX - ceny jsou uvedeny bez DPH + $variables['product_price'] = $product['price']; + $variables['product_price_locale'] = $this->formatter->format('price', (float) $product['price'], $variables['order_currency'] ?? $variables['shop_currency']); + + $variables['product_ean'] = $product['ean']; + $variables['product_upc'] = $product['upc']; + $variables['product_isbn'] = $product['isbn']; + $variables['product_jan'] = $product['jan']; + $variables['product_mpn'] = $product['mpn']; + $variables['product_sku'] = $product['sku']; + } +} \ No newline at end of file diff --git a/src/Event/Loader/Shop.php b/src/Event/Loader/Shop.php new file mode 100644 index 0000000..21b3460 --- /dev/null +++ b/src/Event/Loader/Shop.php @@ -0,0 +1,35 @@ +settings_model->getSetting('config', (int) $variables['shop_id']); + + $variables['shop_email'] = $settings['config_email']; + $variables['shop_name'] = $settings['config_name']; + $variables['shop_domain'] ??= $settings['config_url'] ?? ""; //?? HTTP_CATALOG; //todo: shop 0 tuto polozku nema ... tady musime vzit aktualni url? + $variables['shop_currency'] = $settings['config_currency']; + $variables['shop_phone'] = $settings['config_telephone']; + $variables['lang_id'] ??= ($this->language_model->getLanguageByCode($this->request->get['language'] ?? $this->request->cookie['language'] ?? $settings['config_language_catalog']))['language_id']; + $variables['lang_iso'] = $this->language_model->getLanguage((int) $variables['lang_id'])['code']; + } +} \ No newline at end of file diff --git a/src/Event/State.php b/src/Event/State.php new file mode 100644 index 0000000..576b9a4 --- /dev/null +++ b/src/Event/State.php @@ -0,0 +1,91 @@ +loader = $loader; + } + + public function setExpected(mixed $expected): self + { + $this->expected = $expected; + + return $this; + } + + public function captureInitial(): self + { + $this->initial = $this->callLoader(); + + return $this; + } + + public function captureActual(): self + { + $this->actual = $this->callLoader(); + + return $this; + } + + public function shouldRunHook(): bool + { + return $this->isExpected() && $this->isChanged(); + } + + public function getActual(): mixed + { + return $this->actual; + } + + public function getInitial(): mixed + { + return $this->initial; + } + + public function getExpected(): mixed + { + return $this->expected; + } + + private function callLoader(mixed ...$loader_params): mixed + { + return call_user_func($this->loader, ...$loader_params); + } + + public function isExpected(): bool + { + return $this->expected === $this->actual; + } + + public function isChanged(): bool + { + return $this->initial !== $this->actual; + } + + /** @return array{initial: mixed, expected: mixed, actual: mixed} */ + public function debug(): array + { + return [ + 'initial' => $this->initial, + 'expected' => $this->expected, + 'actual' => $this->actual, + ]; + } +} \ No newline at end of file diff --git a/tests/Database/ConnectionTest.phpt b/tests/Database/ConnectionTest.phpt new file mode 100644 index 0000000..f40fdbe --- /dev/null +++ b/tests/Database/ConnectionTest.phpt @@ -0,0 +1,92 @@ +shouldReceive('query')->with('SQL')->once()->andReturn([ + 'rows' => [ + ['id' => 4], + ['id' => 5] + ] + ]); + + [$e1, $e2] = $connection->execute('SQL')->toArray(); + + Assert::same([['id' => 4], ['id' => 5]], [$e1->toArray(), $e2->toArray()]); + Assert::same(['SQL'], $connection->getSqlList()); + } + + + public function testPrepare(): void + { + $connection = new Connection($db = Mockery::mock(DB::class)); + + $db->shouldReceive('escape')->with('hello')->once()->andReturn('hello'); + + Assert::same("SQL ('hello')", $connection->prepare('SQL (%s)', 'hello')); + } + + + public function testLastId(): void + { + $connection = new Connection($db = Mockery::mock(DB::class)); + $db->shouldReceive('getLastId')->withNoArgs()->once()->andReturn(451); + + Assert::same(451, $connection->lastId()); + } + + + public function testEscape(): void + { + $connection = new Connection($db = Mockery::mock(DB::class)); + $db->shouldReceive('escape')->with('dangerous string')->once()->andReturn('safe string'); + + Assert::same('safe string', $connection->escape('dangerous string')); + } + + + public function testPrefix(): void + { + $connection = new Connection(Mockery::mock(DB::class)); + + Assert::same('oc_', $connection->prefix()); + } + + + public function testTable(): void + { + $connection = new Connection(Mockery::mock(DB::class)); + + Assert::same('oc_users', $connection->table('users')); + } + + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new ConnectionTest())->run(); diff --git a/tests/Eshop/Configuration.phpt b/tests/Eshop/Configuration.phpt new file mode 100644 index 0000000..f4bda9c --- /dev/null +++ b/tests/Eshop/Configuration.phpt @@ -0,0 +1,24 @@ +version()); + Assert::same('https://www.example.com', $configuration->url()); + Assert::same('Example Store', $configuration->name()); + Assert::same('oc', $configuration->product()); + } +} + +(new ConfigurationTest())->run(); \ No newline at end of file diff --git a/tests/Eshop/Language.phpt b/tests/Eshop/Language.phpt new file mode 100644 index 0000000..0601829 --- /dev/null +++ b/tests/Eshop/Language.phpt @@ -0,0 +1,51 @@ +shouldReceive('getLanguages')->withNoArgs()->andReturn([ + ['code' => 'en-gb', 'name' => 'English'], + ['code' => 'cs-cz', 'name' => 'Czech'], + ]); + + $language_loader = new Language($language_model); + + Assert::same([ + 'en-gb' => 'English', + 'cs-cz' => 'Czech', + ], $language_loader->load()); + } + + public function testMultiLanguageSupport(): void + { + $language_model = Mockery::mock(\Opencart\Admin\Model\Localisation\Language::class); + $language_loader = new Language($language_model); + + Assert::true($language_loader->hasMultiLanguageSupport()); + } + + public function testGetLanguage(): void + { + $language_model = Mockery::mock(\Opencart\Admin\Model\Localisation\Language::class); + $language_model->shouldReceive('getLanguage')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn(['code' => 'en-gb']); + + $language_loader = new Language($language_model); + + Assert::same('en-gb', $language_loader->get()); + Assert::same('en-gb', $language_loader->get(1)); + Assert::same('en-gb', $language_loader->get("1")); + } +} + +(new LanguageTest())->run(); \ No newline at end of file diff --git a/tests/Eshop/MultiStore.phpt b/tests/Eshop/MultiStore.phpt new file mode 100644 index 0000000..1ba7bd0 --- /dev/null +++ b/tests/Eshop/MultiStore.phpt @@ -0,0 +1,34 @@ +shouldReceive('name')->withNoArgs()->andReturn('Store'); + + $store_model = Mockery::mock(\Opencart\Admin\Model\Setting\Store::class); + $store_model->shouldReceive('getStores')->withNoArgs()->andReturn([ + ['store_id' => 1, 'name' => 'Store 2'], + ]); + + $multistore_loader = new MultiStore($configuration, $store_model); + + Assert::same([ + 0 => 'Store', + 1 => 'Store 2', + ], $multistore_loader->load()); + } +} + +(new MultiStoreTest())->run(); \ No newline at end of file diff --git a/tests/Eshop/OrderStatus.phpt b/tests/Eshop/OrderStatus.phpt new file mode 100644 index 0000000..9cc38b1 --- /dev/null +++ b/tests/Eshop/OrderStatus.phpt @@ -0,0 +1,31 @@ +shouldReceive('getOrderStatuses')->withNoArgs()->andReturn([ + ['order_status_id' => 1, 'name' => 'Pending'], + ['order_status_id' => 2, 'name' => 'Completed'], + ]); + + $order_status_loader = new OrderStatus($order_status_model); + + Assert::same([ + 1 => 'Pending', + 2 => 'Completed', + ], $order_status_loader->load()); + } +} + +(new OrderStatusTest())->run(); \ No newline at end of file diff --git a/tests/Eshop/ReturnStatus.phpt b/tests/Eshop/ReturnStatus.phpt new file mode 100644 index 0000000..4b07262 --- /dev/null +++ b/tests/Eshop/ReturnStatus.phpt @@ -0,0 +1,31 @@ +shouldReceive('getReturnStatuses')->withNoArgs()->andReturn([ + ['return_status_id' => 1, 'name' => 'Pending'], + ['return_status_id' => 2, 'name' => 'Completed'], + ]); + + $return_status_loader = new ReturnStatus($return_status_model); + + Assert::same([ + 1 => 'Pending', + 2 => 'Completed', + ], $return_status_loader->load()); + } +} + +(new ReturnStatusTest())->run(); \ No newline at end of file diff --git a/tests/Event/Helpers.phpt b/tests/Event/Helpers.phpt new file mode 100644 index 0000000..bb38c96 --- /dev/null +++ b/tests/Event/Helpers.phpt @@ -0,0 +1,34 @@ + 'value1', + 'key2' => null, + ]; + $value_array_access = new Plugin\Event\Variables($value_array); + + Assert::same('value1', Helpers::priorityValues(['key1', 'key2'], $value_array)); + Assert::same('value1', Helpers::priorityValues(['key2', 'key1'], $value_array)); + Assert::same('value1', Helpers::priorityValues(['key3', 'key1'], $value_array)); + Assert::same('default_value', Helpers::priorityValues(['key2', 'key3'], $value_array, 'default_value')); + + Assert::same('value1', Helpers::priorityValues(['key1', 'key2'], $value_array_access)); + Assert::same('value1', Helpers::priorityValues(['key2', 'key1'], $value_array_access)); + Assert::same('value1', Helpers::priorityValues(['key3', 'key1'], $value_array_access)); + Assert::same('default_value', Helpers::priorityValues(['key2', 'key3'], $value_array_access, 'default_value')); + } +} + +(new HelpersTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Admin.phpt b/tests/Event/Loader/Admin.phpt new file mode 100644 index 0000000..0064c08 --- /dev/null +++ b/tests/Event/Loader/Admin.phpt @@ -0,0 +1,53 @@ +shouldReceive('getUser')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'user@example.com' + ]); + + $admin_loader = new \BulkGate\CartSms\Event\Loader\Admin($admin_model); + $admin_loader->load($variables = new Plugin\Event\Variables(['employee_id' => 1])); + $admin_loader->load(new Plugin\Event\Variables(['employee_id' => "1"])); + + Assert::same([ + 'employee_id' => 1, + 'employee_email' => 'user@example.com', + 'employee_firstname' => 'John', + 'employee_lastname' => 'Doe' + ], $variables->toArray()); + } + + public function testNoLoad(): void + { + $admin_model = Mockery::mock(\Opencart\Admin\Model\User\User::class); + $admin_model->shouldNotReceive('getUser'); + + $admin_loader = new \BulkGate\CartSms\Event\Loader\Admin($admin_model); + $admin_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new AdminTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Customer.phpt b/tests/Event/Loader/Customer.phpt new file mode 100644 index 0000000..46273f5 --- /dev/null +++ b/tests/Event/Loader/Customer.phpt @@ -0,0 +1,79 @@ +shouldReceive('getCustomer')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'telephone' => '777888999', + 'email' => 'user@example.com' + ]); + + $customer_loader = new \BulkGate\CartSms\Event\Loader\Customer($customer_model); + $customer_loader->load($variables = new Plugin\Event\Variables(['customer_id' => 1])); + $customer_loader->load(new Plugin\Event\Variables(['customer_id' => "1"])); + + Assert::same([ + 'customer_id' => 1, + 'shop_id' => 1, + 'lang_id' => 1, + 'customer_mobile' => '777888999', + 'customer_email' => 'user@example.com' + ], $variables->toArray()); + } + + public function testOverwrite() + { + $customer_model = Mockery::mock(\Opencart\Catalog\Model\Account\Customer::class); + $customer_model->shouldReceive('getCustomer')->with(1)->once()->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'telephone' => '777888999', + 'email' => 'user@example.com' + ]); + + $customer_loader = new \BulkGate\CartSms\Event\Loader\Customer($customer_model); + $customer_loader->load($variables = new Plugin\Event\Variables([ + 'customer_id' => 1, + 'shop_id' => 2, + 'lang_id' => 3, + 'customer_mobile' => '111', + 'customer_email' => 'test@opencart.com' + ])); + + Assert::same(1, $variables['customer_id']); + Assert::same(2, $variables['shop_id']); + Assert::same(3, $variables['lang_id']); + } + + public function testNoLoad(): void + { + $customer_model = Mockery::mock(\Opencart\Catalog\Model\Account\Customer::class); + $customer_model->shouldNotReceive('getCustomer'); + + $customer_loader = new \BulkGate\CartSms\Event\Loader\Customer($customer_model); + $customer_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new CustomerTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Extension.phpt b/tests/Event/Loader/Extension.phpt new file mode 100644 index 0000000..0f9e442 --- /dev/null +++ b/tests/Event/Loader/Extension.phpt @@ -0,0 +1,35 @@ +shouldReceive('trigger') + ->with('cartsms.hook.extension', Mockery::on(fn ($args) => $args[0] instanceof Plugin\Event\Variables && $args[1] === [])) + ->once(); + + $extension_loader = new Loader\Extension($event); + $extension_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 1, 'lang_id' => 1])); + + Assert::same(['shop_id' => 1, 'lang_id' => 1], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new ExtensionTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Order.phpt b/tests/Event/Loader/Order.phpt new file mode 100644 index 0000000..6a88cec --- /dev/null +++ b/tests/Event/Loader/Order.phpt @@ -0,0 +1,238 @@ +shouldReceive('getOrder')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'customer_id' => 1, + 'shipping_address_id' => 1, + 'payment_address_id' => 1, + 'order_status_id' => 1, + + 'shipping_firstname' => 'firstname_shipping', + 'shipping_lastname' => 'lastname_shipping', + 'shipping_company' => 'company_shipping', + 'shipping_address_1' => 'address_1_shipping', + 'shipping_city' => 'city_shipping', + 'shipping_zone' => 'state_shipping', + 'shipping_postcode' => 'postcode_shipping', + 'shipping_country' => 'country_shipping', + 'shipping_iso_code_2' => 'iso_code_shipping', + + 'payment_firstname' => 'firstname_payment', + 'payment_lastname' => 'lastname_payment', + 'payment_company' => 'company_payment', + 'payment_address_1' => 'address_1_payment', + 'payment_city' => 'city_payment', + 'payment_zone' => 'state_payment', + 'payment_postcode' => 'postcode_payment', + 'payment_country' => 'country_payment', + 'payment_iso_code_2' => 'iso_code_payment', + + 'telephone' => 'telephone', + 'email' => 'email', + 'currency_code' => 'USD', + 'total' => 100, + 'payment_method' => ['name' => 'Payment'], + 'shipping_method' => ['name' => 'Shipping', 'cost' => 5, 'code' => 'shipping_code'], + 'products' => [ + ['order_product_id' => 'product_id', 'name' => 'name', 'model' => 'model', 'quantity' => 2, 'total' => 40, 'tax' => 4.8], + ], + 'tracking' => 'tracking', + 'comment' => 'comment', + 'date_added' => '2025-03-10 13:00:00' + ]); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('price', 100.0, 'USD')->andReturn('price_locale'); + $formatter->shouldReceive('format')->with('date', '2025-03-10 13:00:00')->andReturn('date_locale'); + $formatter->shouldReceive('format')->with('datetime', '2025-03-10 13:00:00')->andReturn('datetime_locale'); + $formatter->shouldReceive('format')->with('time', '2025-03-10 13:00:00')->andReturn('time_locale'); + $formatter->shouldReceive('format')->with('price', 5.0, 'USD')->andReturn('carrier_price_locale'); + $formatter->shouldReceive('format')->with('price', 44.8, 'USD')->andReturn('product_price_locale'); + + $order_loader = new \BulkGate\CartSms\Event\Loader\Order($order_model, $formatter); + $order_loader->load($variables = new Plugin\Event\Variables(['order_id' => 1])); + $order_loader->load(new Plugin\Event\Variables(['order_id' => '1'])); + + Assert::equal([ + 'order_id' => 1, + 'customer_firstname' => 'firstname_shipping', + 'customer_lastname' => 'lastname_shipping', + 'customer_company' => 'company_shipping', + 'customer_address' => 'address_1_shipping', + 'customer_city' => 'city_shipping', + 'customer_state' => 'state_shipping', + 'customer_postcode' => 'postcode_shipping', + 'customer_country' => 'country_shipping', + 'customer_country_id' => 'iso_code_shipping', + 'customer_invoice_firstname' => 'firstname_payment', + 'customer_invoice_lastname' => 'lastname_payment', + 'customer_invoice_company' => 'company_payment', + 'customer_invoice_address' => 'address_1_payment', + 'customer_invoice_city' => 'city_payment', + 'customer_invoice_state' => 'state_payment', + 'customer_invoice_postcode' => 'postcode_payment', + 'customer_invoice_country' => 'country_payment', + 'customer_invoice_country_id' => 'iso_code_payment', + 'customer_mobile' => 'telephone', + 'customer_email' => 'email', + 'shop_id' => 1, + 'lang_id' => 1, + 'customer_id' => 1, + 'id_address_delivery' => 1, + 'id_address_invoice' => 1, + 'order_status_id' => 1, + 'order_currency' => 'USD', + 'long_order_id' => '000001', + 'order_total_locale' => 'price_locale', + 'order_total_paid' => 100, + 'order_payment' => 'Payment', + 'order_tracking' => 'tracking', + 'order_message' => 'comment', + 'order_date' => 'date_locale', + 'order_date1' => '10.03.2025', + 'order_date2' => '10/03/2025', + 'order_date3' => '10-03-2025', + 'order_date4' => '2025-03-10', + 'order_date5' => '03.10.2025', + 'order_date6' => '03/10/2025', + 'order_date7' => '03-10-2025', + 'order_datetime' => 'datetime_locale', + 'order_time' => 'time_locale', + 'order_time1' => '13:00:00', + 'order_carrier_name' => 'Shipping', + 'order_carrier_price' => 5, + 'order_carrier_price_locale' => 'carrier_price_locale', + 'order_carrier_code' => 'shipping_code', + 'order_products1' => '2x name model product_price_locale', + 'order_products2' => '2x name product_price_locale', + 'order_products3' => '2x (product_id) name model product_price_locale', + 'order_products4' => '2x model product_price_locale', + 'order_products5' => '2x name model product_price_locale', + 'order_products6' => '2x name product_price_locale', + 'order_products7' => '2x (product_id) name model product_price_locale', + 'order_products8' => '2x model product_price_locale', + 'order_smsprinter1' => '2,name,44.8', + 'order_smsprinter2' => '2;name;44.8', + ], $variables->toArray()); + } + + public function testOverwrite() + { + $order_model = Mockery::mock(\Opencart\Catalog\Model\Checkout\Order::class); + $order_model->shouldReceive('getOrder')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'customer_id' => 1, + 'shipping_address_id' => 1, + 'payment_address_id' => 1, + 'order_status_id' => 1, + 'shipping_firstname' => 'firstname_shipping', + 'shipping_lastname' => 'lastname_shipping', + 'shipping_company' => 'company_shipping', + 'shipping_address_1' => 'address_1_shipping', + 'shipping_city' => 'city_shipping', + 'shipping_zone' => 'state_shipping', + 'shipping_postcode' => 'postcode_shipping', + 'shipping_country' => 'country_shipping', + 'shipping_iso_code_2' => 'iso_code_shipping', + + 'payment_firstname' => '', + 'payment_lastname' => '', + 'payment_company' => '', + 'payment_address_1' => '', + 'payment_city' => '', + 'payment_zone' => '', + 'payment_postcode' => '', + 'payment_country' => '', + 'payment_iso_code_2' => '', + + 'telephone' => 'telephone', + 'email' => 'email', + 'currency_code' => 'USD', + 'total' => 100, + 'payment_method' => ['name' => 'Payment'], + 'shipping_method' => ['name' => 'Shipping', 'cost' => 5, 'code' => 'shipping_code'], + 'products' => [ + ['order_product_id' => 'product_id', 'name' => 'name', 'model' => 'model', 'quantity' => 2, 'total' => 40, 'tax' => 4.8], + ], + 'tracking' => 'tracking', + 'comment' => 'comment', + 'date_added' => '2025-03-10 13:00:00' + ]); + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('price', 100.0, 'USD')->andReturn('price_locale'); + $formatter->shouldReceive('format')->with('date', '2025-03-10 13:00:00')->andReturn('date_locale'); + $formatter->shouldReceive('format')->with('datetime', '2025-03-10 13:00:00')->andReturn('datetime_locale'); + $formatter->shouldReceive('format')->with('time', '2025-03-10 13:00:00')->andReturn('time_locale'); + $formatter->shouldReceive('format')->with('price', 5.0, 'USD')->andReturn('carrier_price_locale'); + $formatter->shouldReceive('format')->with('price', 44.8, 'USD')->andReturn('product_price_locale'); + + $order_loader = new \BulkGate\CartSms\Event\Loader\Order($order_model, $formatter); + $order_loader->load($variables = new Plugin\Event\Variables([ + 'order_id' => 1, + 'shop_id' => 2, + 'lang_id' => 3, + 'customer_id' => 4, + 'id_address_delivery' => 5, + 'id_address_invoice' => 6, + 'order_status_id' => 7 + ])); + + Assert::same(1, $variables['order_id']); + Assert::same(2, $variables['shop_id']); + Assert::same(3, $variables['lang_id']); + Assert::same(4, $variables['customer_id']); + Assert::same(5, $variables['id_address_delivery']); + Assert::same(6, $variables['id_address_invoice']); + Assert::same(7, $variables['order_status_id']); + + Assert::same('firstname_shipping', $variables['customer_invoice_firstname']); + Assert::same('lastname_shipping', $variables['customer_invoice_lastname']); + Assert::same('company_shipping', $variables['customer_invoice_company']); + Assert::same('address_1_shipping', $variables['customer_invoice_address']); + Assert::same('city_shipping', $variables['customer_invoice_city']); + Assert::same('state_shipping', $variables['customer_invoice_state']); + Assert::same('postcode_shipping', $variables['customer_invoice_postcode']); + Assert::same('country_shipping', $variables['customer_invoice_country']); + Assert::same('iso_code_shipping', $variables['customer_invoice_country_id']); + } + + + public function testNoLoad(): void + { + $order_model = Mockery::mock(\Opencart\Catalog\Model\Checkout\Order::class); + $order_model->shouldNotReceive('getOrder'); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + + $order_loader = new \BulkGate\CartSms\Event\Loader\Order($order_model, $formatter); + $order_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new OrderTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/OrderReturn.phpt b/tests/Event/Loader/OrderReturn.phpt new file mode 100644 index 0000000..85f6aea --- /dev/null +++ b/tests/Event/Loader/OrderReturn.phpt @@ -0,0 +1,122 @@ +shouldReceive('getReturn')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'order_id' => 1, + 'customer_id' => 1, + 'return_status_id' => 1, + 'quantity' => 1, + 'product' => 'Product', + 'product_id' => 1, + 'return_status' => 'status', + 'comment' => 'test', + 'date_added' => '2025-03-10 11:00:00', + 'action' => 'action', + 'return_action_id' => 1, + 'return_reason_id' => 1, + 'reason' => 'reason' + ]); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('date', '2025-03-10 11:00:00')->andReturn('date'); + + $order_return_loader = new \BulkGate\CartSms\Event\Loader\OrderReturn($return_model, $formatter); + $order_return_loader->load($variables = new Plugin\Event\Variables(['return_id' => 1])); + $order_return_loader->load(new Plugin\Event\Variables(['return_id' => "1"])); + + Assert::same([ + 'return_id' => 1, + 'lang_id' => 1, + 'order_id' => 1, + 'customer_id' => 1, + 'return_status_id' => 1, + 'return_customer_message' => 'test', + 'return_date' => 'date', + 'return_action' => 'action', + 'return_action_id' => 1, + 'return_reason_id' => 1, + 'return_reason' => 'reason', + 'return_status' => 'status', + 'return_products1' => '1x Product 1', + 'return_products2' => '1x Product', + 'return_products3' => '1x (1) Product', + 'return_products4' => '1x 1' + ], $variables->toArray()); + } + + public function testOverwrite() + { + $return_model = Mockery::mock(\Opencart\Catalog\Model\Account\Returns::class); + $return_model->shouldReceive('getReturn')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'order_id' => 1, + 'customer_id' => 1, + 'return_status_id' => 1, + 'quantity' => 1, + 'product' => 'Product', + 'product_id' => 1, + 'return_status' => 'status', + 'comment' => 'test', + 'date_added' => '2025-03-10 11:00:00', + 'action' => 'action', + 'return_action_id' => 1, + 'return_reason_id' => 1, + 'reason' => 'reason' + ]); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('date', '2025-03-10 11:00:00')->andReturn('date'); + + $order_return_loader = new \BulkGate\CartSms\Event\Loader\OrderReturn($return_model, $formatter); + $order_return_loader->load($variables = new Plugin\Event\Variables([ + 'return_id' => 1, + 'lang_id' => 2, + 'order_id' => 3, + 'customer_id' => 4, + 'return_status_id' => 5 + ])); + + Assert::same(1, $variables['return_id']); + Assert::same(2, $variables['lang_id']); + Assert::same(3, $variables['order_id']); + Assert::same(4, $variables['customer_id']); + Assert::same(5, $variables['return_status_id']); + } + + public function testNoLoad(): void + { + $return_model = Mockery::mock(\Opencart\Catalog\Model\Account\Returns::class); + $return_model->shouldNotReceive('getReturn'); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + + $order_return_loader = new \BulkGate\CartSms\Event\Loader\OrderReturn($return_model, $formatter); + $order_return_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new OrderReturnTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/OrderReturnStatus.phpt b/tests/Event/Loader/OrderReturnStatus.phpt new file mode 100644 index 0000000..09b304c --- /dev/null +++ b/tests/Event/Loader/OrderReturnStatus.phpt @@ -0,0 +1,48 @@ +shouldReceive('getReturnStatus')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn(['name' => 'Pending']); + + $return_status_loader = new \BulkGate\CartSms\Event\Loader\OrderReturnStatus($order_return_status_model); + $return_status_loader->load($variables = new Plugin\Event\Variables(['return_status_id' => 1])); + $return_status_loader->load(new Plugin\Event\Variables(['return_status_id' => "1"])); + + Assert::same([ + 'return_status_id' => 1, + 'return_status' => 'Pending' + ], $variables->toArray()); + } + + public function testNoLoad(): void + { + $order_return_status_model = Mockery::mock(\Opencart\Admin\Model\Localisation\ReturnStatus::class); + $order_return_status_model->shouldNotReceive('getReturnStatus'); + + $return_status_loader = new \BulkGate\CartSms\Event\Loader\OrderReturnStatus($order_return_status_model); + $return_status_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + $return_status_loader->load(new Plugin\Event\Variables(['return_status_id' => 0])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new OrderReturnStatusTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/OrderStatus.phpt b/tests/Event/Loader/OrderStatus.phpt new file mode 100644 index 0000000..c154c26 --- /dev/null +++ b/tests/Event/Loader/OrderStatus.phpt @@ -0,0 +1,48 @@ +shouldReceive('getOrderStatus')->with(Mockery::on(fn($arg) => $arg === 1))->andReturn(['name' => 'Pending']); + + $order_status_loader = new \BulkGate\CartSms\Event\Loader\OrderStatus($order_status_model); + $order_status_loader->load($variables = new Plugin\Event\Variables(['order_status_id' => 1])); + $order_status_loader->load(new Plugin\Event\Variables(['order_status_id' => "1"])); + + Assert::same([ + 'order_status_id' => 1, + 'order_status' => 'Pending' + ], $variables->toArray()); + } + + public function testNoLoad(): void + { + $order_status_model = Mockery::mock(\Opencart\Catalog\Model\Localisation\OrderStatus::class); + $order_status_model->shouldNotReceive('getOrderStatus'); + + $order_status_loader = new \BulkGate\CartSms\Event\Loader\OrderStatus($order_status_model); + $order_status_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + $order_status_loader->load(new Plugin\Event\Variables(['order_status_id' => 0])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new OrderStatusTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Product.phpt b/tests/Event/Loader/Product.phpt new file mode 100644 index 0000000..989ae88 --- /dev/null +++ b/tests/Event/Loader/Product.phpt @@ -0,0 +1,123 @@ +shouldReceive('getProduct')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'manufacturer_id' => 1, + 'quantity' => 'quantity', + 'minimum' => 'min_quantity', + 'name' => 'Product', + 'model' => 'Model', + 'description' => 'description', + 'price' => 20, + 'ean' => 'ean', + 'upc' => 'upc', + 'isbn' => 'isbn', + 'jan' => 'jan', + 'mpn' => 'mpn', + 'sku' => 'sku' + ]); + + $manufacturer_model = Mockery::mock(\Opencart\Catalog\Model\Catalog\Manufacturer::class); + $manufacturer_model->shouldReceive('getManufacturer')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn(['name' => 'Manufacturer']); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('price', 20.0, 'USD')->andReturn('price_locale'); + + $product_loader = new \BulkGate\CartSms\Event\Loader\Product($product_model, $manufacturer_model, $formatter); + $product_loader->load($variables = new Plugin\Event\Variables(['product_id' => 1, 'shop_currency' => 'USD'])); + $product_loader->load(new Plugin\Event\Variables(['product_id' => '1', 'shop_currency' => 'USD'])); + + Assert::same([ + 'product_id' => 1, + 'shop_currency' => 'USD', + 'shop_id' => 1, + 'lang_id' => 1, + 'product_quantity' => 'quantity', + 'product_minimal_quantity' => 'min_quantity', + 'product_name' => 'Product', + 'product_model' => 'Model', + 'product_description' => 'description', + 'product_manufacturer' => 'Manufacturer', + 'product_price' => 20, + 'product_price_locale' => 'price_locale', + 'product_ean' => 'ean', + 'product_upc' => 'upc', + 'product_isbn' => 'isbn', + 'product_jan' => 'jan', + 'product_mpn' => 'mpn', + 'product_sku' => 'sku', + ], $variables->toArray()); + } + + public function testOverwrite() + { + $product_model = Mockery::mock(\Opencart\Catalog\Model\Catalog\Product::class); + $product_model->shouldReceive('getProduct')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn([ + 'store_id' => 1, + 'language_id' => 1, + 'manufacturer_id' => 1, + 'quantity' => 'quantity', + 'minimum' => 'min_quantity', + 'name' => 'Product', + 'model' => 'Model', + 'description' => 'description', + 'price' => 20, + 'ean' => 'ean', + 'upc' => 'upc', + 'isbn' => 'isbn', + 'jan' => 'jan', + 'mpn' => 'mpn', + 'sku' => 'sku' + ]); + + $manufacturer_model = Mockery::mock(\Opencart\Catalog\Model\Catalog\Manufacturer::class); + $manufacturer_model->shouldReceive('getManufacturer')->with(Mockery::on(fn ($arg) => $arg === 1))->andReturn(['name' => 'Manufacturer']); + + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + $formatter->shouldReceive('format')->with('price', 20.0, 'USD')->andReturn('price_locale'); + + $product_loader = new \BulkGate\CartSms\Event\Loader\Product($product_model, $manufacturer_model, $formatter); + $product_loader->load($variables = new Plugin\Event\Variables(['product_id' => 1, 'shop_id' => 2, 'lang_id' => 3, 'shop_currency' => 'USD'])); + + Assert::same(1, $variables['product_id']); + Assert::same(2, $variables['shop_id']); + Assert::same(3, $variables['lang_id']); + } + + public function testNoLoad(): void + { + $product_model = Mockery::mock(\Opencart\Catalog\Model\Catalog\Product::class); + $product_model->shouldNotReceive('getProduct'); + + $manufacturer_model = Mockery::mock(\Opencart\Catalog\Model\Catalog\Manufacturer::class); + $formatter = Mockery::mock(\BulkGate\Plugin\Localization\Formatter::class); + + $product_loader = new \BulkGate\CartSms\Event\Loader\Product($product_model, $manufacturer_model, $formatter); + $product_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 2, 'lang_id' => 3])); + + Assert::same(['shop_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new ProductTest())->run(); \ No newline at end of file diff --git a/tests/Event/Loader/Shop.phpt b/tests/Event/Loader/Shop.phpt new file mode 100644 index 0000000..2c9e29e --- /dev/null +++ b/tests/Event/Loader/Shop.phpt @@ -0,0 +1,135 @@ +shouldReceive('getSetting')->with('config', Mockery::on(fn($arg) => $arg === 1))->andReturn([ + 'config_name' => 'Shop', + 'config_url' => 'http://www.example.com', + 'config_currency' => 'USD', + 'config_language_catalog' => 'en-gb', + 'config_telephone' => '777888999', + 'config_email' => 'store@example.com' + ]); + + $language_model = Mockery::mock(\Opencart\Catalog\Model\Localisation\Language::class); + $language_model->shouldReceive('getLanguageByCode')->with('en-gb')->times(2)->andReturn(['language_id' => 1, 'code' => 'en-gb']); + $language_model->shouldReceive('getLanguage')->with(Mockery::on(fn($arg) => $arg === 1))->times(2)->andReturn(['language_id' => 1, 'code' => 'en-gb']); + + $request = Mockery::mock(\Opencart\System\Library\Request::class); + + $shop_loader = new \BulkGate\CartSms\Event\Loader\Shop($store_model, $language_model, $request); + $shop_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 1])); + $shop_loader->load(new Plugin\Event\Variables(['shop_id' => 1])); + + Assert::same([ + 'shop_id' => 1, + 'shop_email' => 'store@example.com', + 'shop_name' => 'Shop', + 'shop_domain' => 'http://www.example.com', + 'shop_currency' => 'USD', + 'shop_phone' => '777888999', + 'lang_id' => 1, + 'lang_iso' => 'en-gb' + ], $variables->toArray()); + } + + public function testOverwrite() + { + $store_model = Mockery::mock(\Opencart\Catalog\Model\Setting\Setting::class); + $store_model->shouldReceive('getSetting')->with('config', Mockery::on(fn($arg) => $arg === 1))->once()->ordered()->andReturn([ + 'config_name' => 'Shop', + 'config_url' => 'http://www.example.com', + 'config_currency' => 'USD', + 'config_language_catalog' => 'en-gb', + 'config_telephone' => '777888999', + 'config_email' => 'store@example.com' + ]); + + $language_model = Mockery::mock(\Opencart\Catalog\Model\Localisation\Language::class); + $language_model->shouldReceive('getLanguage')->with(2)->once()->andReturn(['language_id' => 2, 'code' => 'cs-cz']); + + $request = Mockery::mock(\Opencart\System\Library\Request::class); + + $shop_loader = new \BulkGate\CartSms\Event\Loader\Shop($store_model, $language_model, $request); + $shop_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 1, 'lang_id' => 2, 'shop_domain' => 'https://www.alza.cz'])); + + Assert::same(1, $variables['shop_id']); + Assert::same(2, $variables['lang_id']); + Assert::same('https://www.alza.cz', $variables['shop_domain']); + } + + public function testLanguagePriority(): void + { + $store_model = Mockery::mock(\Opencart\Catalog\Model\Setting\Setting::class); + $store_model->shouldReceive('getSetting')->with('config', Mockery::on(fn($arg) => $arg === 1))->times(2)->ordered()->andReturn([ + 'config_name' => 'Shop', + 'config_url' => 'http://www.example.com', + 'config_currency' => 'USD', + 'config_language_catalog' => 'en-gb', + 'config_telephone' => '777888999', + 'config_email' => 'store@example.com' + ]); + + $language_model = Mockery::mock(\Opencart\Catalog\Model\Localisation\Language::class); + $language_model->shouldReceive('getLanguageByCode')->with('cs-cz')->once()->andReturn(['language_id' => 2, 'code' => 'cs-cz']); + $language_model->shouldReceive('getLanguageByCode')->with('fr-fr')->once()->andReturn(['language_id' => 3, 'code' => 'fr-fr']); + $language_model->shouldReceive('getLanguage')->with(Mockery::on(fn($arg) => $arg === 2))->once()->andReturn(['language_id' => 2, 'code' => 'cs-cz']); + $language_model->shouldReceive('getLanguage')->with(Mockery::on(fn($arg) => $arg === 3))->once()->andReturn(['language_id' => 3, 'code' => 'fr-fr']); + + $request = Mockery::mock(\Opencart\System\Library\Request::class); + $request->get = ['language' => 'fr-fr']; + $request->cookie = ['language' => 'cs-cz']; + + //from GET eg: ?route=product/product&language=fr-fr + $shop_loader = new \BulkGate\CartSms\Event\Loader\Shop($store_model, $language_model, $request); + $shop_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 1])); + + Assert::same(3, $variables['lang_id']); + Assert::same('fr-fr', $variables['lang_iso']); + + //from Cookie eg: ?route=product/product + $request->get = []; + + $shop_loader = new \BulkGate\CartSms\Event\Loader\Shop($store_model, $language_model, $request); + $shop_loader->load($variables = new Plugin\Event\Variables(['shop_id' => 1])); + + Assert::same(2, $variables['lang_id']); + Assert::same('cs-cz', $variables['lang_iso']); + } + + public function testNoLoad(): void + { + $store_model = Mockery::mock(\Opencart\Catalog\Model\Setting\Setting::class); + $store_model->shouldNotReceive('getSetting'); + + $language_model = Mockery::mock(\Opencart\Catalog\Model\Localisation\Language::class); + $request = Mockery::mock(\Opencart\System\Library\Request::class); + + $shop_loader = new \BulkGate\CartSms\Event\Loader\Shop($store_model, $language_model, $request); + $shop_loader->load($variables = new Plugin\Event\Variables(['customer_id' => 2, 'lang_id' => 3])); + + Assert::same(['customer_id' => 2, 'lang_id' => 3], $variables->toArray()); + } + + public function tearDown(): void + { + Mockery::close(); + } +} + +(new ShopTest())->run(); \ No newline at end of file diff --git a/tests/Event/State.phpt b/tests/Event/State.phpt new file mode 100644 index 0000000..e14d6c6 --- /dev/null +++ b/tests/Event/State.phpt @@ -0,0 +1,44 @@ +getInitial()); + Assert::equal(null, $state->getActual()); + Assert::equal(null, $state->getExpected()); + Assert::false($state->isChanged()); + Assert::false($state->shouldRunHook()); + Assert::true($state->isExpected()); + + $state + ->captureInitial() + ->captureActual() + ->setExpected(2); + + Assert::equal(1, $state->getInitial()); + Assert::equal(2, $state->getActual()); + Assert::equal(2, $state->getExpected()); + Assert::true($state->isChanged()); + Assert::true($state->shouldRunHook()); + Assert::true($state->isExpected()); + + } +} + +(new StateTest())->run(); \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..d3d1b95 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,16 @@ +register('Opencart\\' . APPLICATION, DIR_APPLICATION); +$autoloader->register('Opencart\Extension', DIR_EXTENSION); +$autoloader->register('Opencart\System', DIR_SYSTEM); + +require_once DIR_SYSTEM . 'vendor.php'; + +// Registry +$registry = new \Opencart\System\Engine\Registry(); +$registry->set('autoloader', $autoloader); + +// Config +$config = new \Opencart\System\Engine\Config(); +$config->addPath(DIR_CONFIG); +// Load the default config +$config->load('default'); +$config->load(strtolower(APPLICATION)); +$registry->set('config', $config); + +// Set the default application +$config->set('application', APPLICATION); + +// Factory +$registry->set('factory', new \Opencart\System\Engine\Factory($registry)); + +// Loader +$loader = new \Opencart\System\Engine\Loader($registry); +$registry->set('load', $loader); + +// Event +$event = new \Opencart\System\Engine\Event($registry); +$registry->set('event', $event); + +// Cache +//$registry->set('cache', new \Opencart\System\Library\Cache($config->get('cache_engine'), $config->get('cache_expire'))); + +//$db = new \Opencart\System\Library\DB($config->get('db_engine'), $config->get('db_hostname'), $config->get('db_username'), $config->get('db_password'), $config->get('db_database'), $config->get('db_port'), $config->get('db_ssl_key'), $config->get('db_ssl_cert'), $config->get('db_ssl_ca')); +//$registry->set('db', $db); + +return $registry; diff --git a/upload/admin/controller/cartsms/black_list.php b/upload/admin/controller/cartsms/black_list.php deleted file mode 100644 index e0fec38..0000000 --- a/upload/admin/controller/cartsms/black_list.php +++ /dev/null @@ -1,20 +0,0 @@ -view('Black list', 'BlackList', 'default', true); - } - - public function actionImport() - { - $this->view('Black list', 'BlackList', 'import', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/dashboard.php b/upload/admin/controller/cartsms/dashboard.php deleted file mode 100644 index cb42288..0000000 --- a/upload/admin/controller/cartsms/dashboard.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Dashboard', 'Dashboard', 'default', false); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/events.php b/upload/admin/controller/cartsms/events.php deleted file mode 100644 index afb6022..0000000 --- a/upload/admin/controller/cartsms/events.php +++ /dev/null @@ -1,75 +0,0 @@ -runHook('return_status_change_'.$return_status_id, new Extensions\Hook\Variables([ - 'return_id' => (int) $return_id, - 'return_status_id' => (int) $return_status_id, - 'return_customer_message' => $comment - ])); - } - } - - - /** - * admin/model/sale/return/addReturn/after - * @param string $hook - * @param array $input - * @param int $return_id - */ - public function returnGoods($hook, $input, $return_id) - { - $this->runHook('product_return', new Extensions\Hook\Variables(array( - 'return_id' => (int) $return_id, - ))); - } - - - /** - * admin/model/catalog/product/deleteProduct/before - * @param string $hook - * @param array $input - */ - public function productDeleteHook($hook, $input) - { - list($product_id) = array_pad($input, 1, null); - - if($product_id) - { - $this->runHook('product_delete', new Extensions\Hook\Variables([ - 'product_id' => (int) $product_id - ])); - } - } - - /** - * admin/model/customer/customer/addCustomer/after - * @param string $hook - * @param array $input - * @param int $customer_id - */ - public function customerAddHook($hook, $input, $customer_id) - { - $this->runHook('customer_account_new', new Extensions\Hook\Variables(array( - 'customer_id' => (int) $customer_id, - ))); - } -} diff --git a/upload/admin/controller/cartsms/history.php b/upload/admin/controller/cartsms/history.php deleted file mode 100644 index 7f27383..0000000 --- a/upload/admin/controller/cartsms/history.php +++ /dev/null @@ -1,15 +0,0 @@ -view('History', 'History', 'list', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/inbox.php b/upload/admin/controller/cartsms/inbox.php deleted file mode 100644 index de3eef1..0000000 --- a/upload/admin/controller/cartsms/inbox.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Inbox', 'Inbox', 'list', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/module_about.php b/upload/admin/controller/cartsms/module_about.php deleted file mode 100644 index 4d9f03f..0000000 --- a/upload/admin/controller/cartsms/module_about.php +++ /dev/null @@ -1,17 +0,0 @@ -view('About module', 'ModuleAbout', 'default', false); - } - - -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/module_notifications.php b/upload/admin/controller/cartsms/module_notifications.php deleted file mode 100644 index 5a51b11..0000000 --- a/upload/admin/controller/cartsms/module_notifications.php +++ /dev/null @@ -1,44 +0,0 @@ -oc_proxy->add('save', $this->link('cartsms/module_notifications/ajaxSaveAdmin')); - $this->view('Admin SMS', 'ModuleNotifications', 'admin', true); - } - - public function ajaxSaveAdmin() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - $post['template'] = htmlspecialchars_decode($post['template']); - Extensions\JsonResponse::send( - $controller->oc_di->getProxy()->saveAdminNotifications($post) - ); - }); - } - - public function actionCustomer() - { - $this->oc_proxy->add('save', $this->link('cartsms/module_notifications/ajaxSaveCustomer')); - $this->view('Customer SMS', 'ModuleNotifications', 'customer', true); - } - - public function ajaxSaveCustomer() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - $post['template'] = htmlspecialchars_decode($post['template']); - Extensions\JsonResponse::send( - $controller->oc_di->getProxy()->saveCustomerNotifications($post) - ); - }); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/module_settings.php b/upload/admin/controller/cartsms/module_settings.php deleted file mode 100644 index f7ef17b..0000000 --- a/upload/admin/controller/cartsms/module_settings.php +++ /dev/null @@ -1,39 +0,0 @@ -response->redirect($this->link('cartsms/dashboard/actionDefault')); - } - $this->oc_proxy->add('save', $this->link('cartsms/module_settings/ajaxSave')); - $this->oc_proxy->add('logout', $this->link('cartsms/module_settings/ajaxLogout')); - - $this->view('About module', 'ModuleSettings', 'default', false); - } - - public function ajaxSave() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - $controller->oc_di->getProxy()->saveSettings($post); - Extensions\JsonResponse::send(array('redirect' => $controller->link('cartsms/module_settings/actionDefault'))); - - }, 'cartsms/module_settings/actionDefault'); - } - - - public function ajaxLogout() - { - $this->oc_di->getProxy()->logout(); - Extensions\JsonResponse::send(array('token' => 'guest', 'redirect' => $this->link('cartsms/sign/actionIn'))); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/payment.php b/upload/admin/controller/cartsms/payment.php deleted file mode 100644 index bff1e01..0000000 --- a/upload/admin/controller/cartsms/payment.php +++ /dev/null @@ -1,20 +0,0 @@ -view('Payment Data', 'Payment', 'data', true); - } - - public function actionList() - { - $this->view('Payment list', 'Payment', 'list', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/sign.php b/upload/admin/controller/cartsms/sign.php deleted file mode 100644 index bf3d0c4..0000000 --- a/upload/admin/controller/cartsms/sign.php +++ /dev/null @@ -1,50 +0,0 @@ -oc_proxy->add('login', $this->link('cartsms/sign/ajaxIn')); - $this->view('Sign in', 'ModuleSign', 'in', false); - } - - public function ajaxIn() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - $response = $controller->oc_di->getProxy()->login(array_merge(array('name' => $controller->config->get('config_meta_title')), $controller->request->post['__bulkgate'])); - - if($response instanceof Extensions\IO\Response) - { - Extensions\JsonResponse::send($response); - } - Extensions\JsonResponse::send(array( - 'token' => $response, - 'redirect' => $controller->link('cartsms/dashboard/actionDefault') - )); - }); - } - - public function actionUp() - { - $this->view('Sign up', 'Sign', 'up', false); - } - - public function authenticate() - { - try - { - Extensions\JsonResponse::send($this->oc_di->getProxy()->authenticate()); - } - catch (Extensions\IO\AuthenticateException $e) - { - Extensions\JsonResponse::send(array('redirect' => $this->link('cartsms/sign/actionIn'))); - } - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/sms_campaign.php b/upload/admin/controller/cartsms/sms_campaign.php deleted file mode 100644 index 4b53ace..0000000 --- a/upload/admin/controller/cartsms/sms_campaign.php +++ /dev/null @@ -1,82 +0,0 @@ -view('Campaigns', 'SmsCampaign', 'default', true); - } - - public function actionNew() - { - $this->view('Create new Campaign', 'SmsCampaign', 'new', true); - } - - public function actionCampaign() - { - $this->oc_proxy->add('loadModuleData', $this->link('cartsms/sms_campaign/ajaxLoadModuleData'), 'campaign'); - $this->oc_proxy->add('saveModuleCustomers', $this->link('cartsms/sms_campaign/ajaxSaveModuleCustomers'), 'campaign'); - $this->oc_proxy->add('addModuleFilter', $this->link('cartsms/sms_campaign/ajaxAddModuleFilter'), 'campaign'); - $this->oc_proxy->add('removeModuleFilter', $this->link('cartsms/sms_campaign/ajaxRemoveModuleFilter'), 'campaign'); - $this->view('Campaign', 'SmsCampaign', 'campaign', true); - } - - public function ajaxLoadModuleData() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - Extensions\JsonResponse::send($controller->oc_di->getProxy()->loadCustomersCount( - isset($post['application_id']) ? $post['application_id'] : null, - isset($post['campaign_id']) ? $post['campaign_id'] : null - )); - }); - } - - public function ajaxAddModuleFilter() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - Extensions\JsonResponse::send($controller->oc_di->getProxy()->loadCustomersCount( - isset($post['application_id']) ? $post['application_id'] : null, - isset($post['campaign_id']) ? $post['campaign_id'] : null, - 'addFilter', - $post - )); - }); - } - - public function ajaxRemoveModuleFilter() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - Extensions\JsonResponse::send($controller->oc_di->getProxy()->loadCustomersCount( - isset($post['application_id']) ? $post['application_id'] : null, - isset($post['campaign_id']) ? $post['campaign_id'] : null, - 'removeFilter', - $post - )); - }); - } - - public function ajaxSaveModuleCustomers() - { - $this->runAjax(function (CartSms\Controller $controller, array $post) - { - Extensions\JsonResponse::send($controller->oc_di->getProxy()->saveModuleCustomers( - isset($post['application_id']) ? $post['application_id'] : null, - isset($post['campaign_id']) ? $post['campaign_id'] : null - )); - }); - } - - public function actionActive() - { - $this->view('Campaign', 'SmsCampaign', 'active', false); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/sms_price.php b/upload/admin/controller/cartsms/sms_price.php deleted file mode 100644 index daa833e..0000000 --- a/upload/admin/controller/cartsms/sms_price.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Price list', 'SmsPrice', 'list', false); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/sms_settings.php b/upload/admin/controller/cartsms/sms_settings.php deleted file mode 100644 index 36464ae..0000000 --- a/upload/admin/controller/cartsms/sms_settings.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Sender ID Settings', 'SmsSettings', 'default', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/statistics.php b/upload/admin/controller/cartsms/statistics.php deleted file mode 100644 index 558cf4e..0000000 --- a/upload/admin/controller/cartsms/statistics.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Statistics', 'Statistics', 'default', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/top.php b/upload/admin/controller/cartsms/top.php deleted file mode 100644 index 90981f3..0000000 --- a/upload/admin/controller/cartsms/top.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Campaign', 'Top', 'up', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/user.php b/upload/admin/controller/cartsms/user.php deleted file mode 100644 index 7952ef0..0000000 --- a/upload/admin/controller/cartsms/user.php +++ /dev/null @@ -1,15 +0,0 @@ -view('User profile', 'User', 'profile', false); - } -} \ No newline at end of file diff --git a/upload/admin/controller/cartsms/wallet.php b/upload/admin/controller/cartsms/wallet.php deleted file mode 100644 index da58eb9..0000000 --- a/upload/admin/controller/cartsms/wallet.php +++ /dev/null @@ -1,15 +0,0 @@ -view('Payment Data', 'Wallet', 'detail', true); - } -} \ No newline at end of file diff --git a/upload/admin/controller/extension/module/cartsms.php b/upload/admin/controller/extension/module/cartsms.php deleted file mode 100644 index ab192f8..0000000 --- a/upload/admin/controller/extension/module/cartsms.php +++ /dev/null @@ -1,117 +0,0 @@ -response->redirect($this->url->link('cartsms/module_settings/actionDefault', 'user_token=' . $this->session->data['user_token'], true)); - } - - public function install() - { - $this->load->model('setting/event'); - $this->load->model('user/user_group'); - - $this->model_setting_event->deleteEvent('cartsms'); - - $this->oc_settings->install(); - - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/black_list'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/dashboard'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/history'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/inbox'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/module_about'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/module_notifications'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/module_settings'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/payment'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/sign'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/sms_campaign'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/sms_price'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/sms_settings'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/statistics'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/top'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/user'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'cartsms/wallet'); - $this->model_user_user_group->addPermission($this->user->getGroupId(), 'access', 'extension/module/cartsms'); - - $this->model_setting_event->addEvent('cartsms', 'admin/model/sale/return/addReturnHistory/after', 'cartsms/events/returnGoodsStatus'); - $this->model_setting_event->addEvent('cartsms', 'admin/model/customer/customer/addCustomer/after', 'cartsms/events/customerAddHook'); - $this->model_setting_event->addEvent('cartsms', 'admin/model/catalog/product/deleteProduct/before', 'cartsms/events/productDeleteHook'); - $this->model_setting_event->addEvent('cartsms', 'admin/model/sale/return/addReturn/after', 'cartsms/events/returnGoods'); - $this->model_setting_event->addEvent('cartsms', 'catalog/model/checkout/order/addOrderHistory/after', 'cartsms/events/changeOrderStatusHook'); - $this->model_setting_event->addEvent('cartsms', 'catalog/model/account/customer/addCustomer/after', 'cartsms/events/customerAddHook'); - $this->model_setting_event->addEvent('cartsms', 'catalog/model/account/return/addReturn/after', 'cartsms/events/returnGoods'); - $this->model_setting_event->addEvent('cartsms', 'catalog/bulkgate/cartsms/new/order/hook', 'cartsms/events/orderAddHook'); - $this->model_setting_event->addEvent('cartsms', 'catalog/bulkgate/cartsms/contact/form/hook', 'cartsms/events/contactFormHook'); - - $this->installOcMod(); - } - - public function uninstall() - { - $this->load->model('setting/event'); - $this->load->model('user/user_group'); - - $this->oc_settings->uninstall(); - - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/black_list'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/dashboard'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/history'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/inbox'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/module_about'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/module_notifications'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/module_settings'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/payment'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/sign'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/sms_campaign'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/sms_price'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/sms_settings'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/statistics'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/top'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/user'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'cartsms/wallet'); - $this->model_user_user_group->removePermission($this->user->getGroupId(), 'access', 'extension/module/cartsms'); - - $this->model_setting_event->deleteEventByCode('cartsms'); - - $this->uninstallOcMod(); - } - - private function installOcMod() - { - $this->uninstallOcMod(); - - $db = $this->oc_di->getDatabase(); - - $db->execute($db->prepare(" - INSERT INTO `{$db->table('modification')}` (`name`, `author`, `version`, `link`, `xml`, `status`, `date_added`, `code`) - VALUES (%s, %s, %s, %s, \"".$db->escape(file_get_contents(_BG_CARTSMS_DIR_ . DIRECTORY_SEPARATOR . CartSms\Init::MODULE_CODE . '.ocmod.xml'))."\", 1, NOW(), %s) - ", array( - CartSms\Init::NAME, - CartSms\Init::AUTHOR, - CartSms\Init::VERSION, - CartSms\Init::URL, - CartSms\Init::MODULE_CODE - ))); - - $refresh = new ControllerMarketplaceModification($this->registry); - $refresh->refresh(); - } - - private function uninstallOcMod() - { - $db = $this->oc_di->getDatabase(); - $db->execute($db->prepare("DELETE FROM `{$db->table('modification')}` WHERE `code` = %s", array(CartSms\Init::MODULE_CODE))); - } -} - diff --git a/upload/admin/language/en-gb/extension/module/cartsms.php b/upload/admin/language/en-gb/extension/module/cartsms.php deleted file mode 100644 index 34269ac..0000000 --- a/upload/admin/language/en-gb/extension/module/cartsms.php +++ /dev/null @@ -1,3 +0,0 @@ -CartSMS - SMS module for OpenCart 🌍'; diff --git a/upload/admin/view/template/cartsms/base.twig b/upload/admin/view/template/cartsms/base.twig deleted file mode 100644 index bf3cecb..0000000 --- a/upload/admin/view/template/cartsms/base.twig +++ /dev/null @@ -1,72 +0,0 @@ -{{ header }}{{ column_left }} -
-
- -
- -
-
-
-
-

Loading content

-
-
- - - - - -
-
- -{{ footer }} diff --git a/upload/catalog/controller/cartsms/events.php b/upload/catalog/controller/cartsms/events.php deleted file mode 100644 index fe9a891..0000000 --- a/upload/catalog/controller/cartsms/events.php +++ /dev/null @@ -1,103 +0,0 @@ -runHook('order_status_change_'.$order_status_id, new Extensions\Hook\Variables(array( - 'order_status_id' => (int) $order_status_id, - 'order_id' => (int) $order_id, - 'order_status_message' => $comment - ))); - } - } - - /** - * catalog/model/account/customer/addCustomer/after - * @param string $hook - * @param array $input - * @param int $customer_id - */ - public function customerAddHook($hook, $input, $customer_id) - { - $this->runHook('customer_account_new', new Extensions\Hook\Variables(array( - 'customer_id' => (int) $customer_id, - ))); - } - - /** - * bulkgate/cartsms/new/order/hook - * @param string $hook - * @param array $input - */ - public function orderAddHook($hook, $input) - { - list($order_id) = array_pad($input, 1, null); - - $this->runHook('order_new', new Extensions\Hook\Variables([ - 'order_id' => (int) $order_id - ])); - - foreach(Helpers::productsOutOfStock($this->oc_di->getDatabase()) as $product_id) - { - if(Extensions\Helpers::outOfStockCheck($this->oc_settings, $product_id)) - { - $this->runHook('product_out_of_stock', new Extensions\Hook\Variables([ - 'product_id' => (int) $product_id - ])); - } - } - } - - /** - * bulkgate/cartsms/contact/form/hook - * @param $hook - * @param $input - */ - public function contactFormHook($hook, $input) - { - list($email, $name, $text) = array_pad($input, 3, null); - - if($text !== null) - { - $this->runHook('contact_form', new Extensions\Hook\Variables(array( - 'customer_email' => $email, - 'customer_name' => $name, - 'customer_message' => $text, - 'customer_message_short_50' => Helpers::subStr($text, 0, 50), - 'customer_message_short_80' => Helpers::subStr($text, 0, 80), - 'customer_message_short_100' => Helpers::subStr($text, 0, 100), - 'customer_message_short_120' => Helpers::subStr($text, 0, 120), - ))); - } - } - - /** - * catalog/model/account/return/addReturn/after - * @param string $hook - * @param array $input - * @param int $return_id - */ - public function returnGoods($hook, $input, $return_id) - { - $this->runHook('product_return', new Extensions\Hook\Variables(array( - 'return_id' => (int) $return_id, - ))); - } -} diff --git a/upload/system/library/cartsms/Controller.php b/upload/system/library/cartsms/Controller.php deleted file mode 100644 index c7f0a9a..0000000 --- a/upload/system/library/cartsms/Controller.php +++ /dev/null @@ -1,147 +0,0 @@ -registry); - $this->oc_di = $init->di(); - $this->oc_module = $this->oc_di->getModule(); - $this->oc_settings = $this->oc_di->getSettings(); - $this->oc_proxy = new BulkGate\CartSms\ProxyGenerator(); - - $this->load->model('setting/event'); - } - - protected function view($title, $presenter, $action, $box = false) - { - $this->synchronize(); - $this->document->addStyle($this->oc_module->getUrl('/dist/css/devices.min.css')); - $this->document->addStyle($this->oc_module->getUrl('/'.(defined('BULKGATE_DEV_MODE') ? 'dev' : 'dist').'/css/bulkgate-cartsms.css')); - $this->document->addStyle('https://fonts.googleapis.com/icon?family=Material+Icons|Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i'); - - $this->response->setOutput($this->load->view('cartsms/base', array( - 'application_id' => $this->oc_settings->load('static:application_id', ''), - 'language' => $this->oc_settings->load('main:language', 'en'), - 'presenter' => $presenter, - 'action' => $action, - 'title' => $title, - 'mode' => defined('BULKGATE_DEV_MODE') ? 'dev' : 'dist', - 'box' => $box, - 'widget_api_url' => $this->oc_module->getUrl('/'.(defined('BULKGATE_DEV_MODE') ? 'dev' : 'dist').'/widget-api/widget-api.js'), - 'logo' => $this->oc_module->getUrl('/images/products/oc.svg'), - 'proxy' => $this->oc_proxy->get(), - 'authenticate' => $this->link('cartsms/sign/authenticate'), - 'homepage' => $this->link('cartsms/dashboard'), - 'info' => $this->oc_module->info(), - 'header' => $this->load->controller('common/header'), - 'column_left' => $this->load->controller('common/column_left'), - 'footer' => $this->load->controller('common/footer'), - 'loading' => $this->oc_di->getTranslator()->translate('loading_content', 'Loading Content') - ))); - } - - protected function link($route, array $params = array()) - { - return BulkGate\CartSms\Helpers::fixUrl( - $this->url->link($route, array_merge(array('user_token' => $this->session->data['user_token']), $params), true) - ); - } - - protected function runAjax($callback, $fail_redirect = 'common/dashboard') - { - if(isset($this->request->post['__bulkgate'])) - { - $post = $this->request->post['__bulkgate']; - - if(is_array($post)) - { - call_user_func_array($callback, array($this, $post)); - } - else - { - $this->response->redirect($this->url->link($fail_redirect, 'user_token=' . $this->session->data['user_token'], true)); - } - } - else - { - $this->response->redirect($this->url->link($fail_redirect, 'user_token=' . $this->session->data['user_token'], true)); - } - } - - protected function synchronize($now = false) - { - if($this->oc_settings->load('static:application_token')) - { - $status = $this->oc_module->statusLoad(); $language = $this->oc_module->languageLoad(); $store = $this->oc_module->storeLoad(); $return = $this->oc_module->returnStatusLoad(); - - $now = $now || $status || $language || $store || $return; - try - { - $this->oc_di->getSynchronize()->run($this->oc_module->getUrl('/module/settings/synchronize'), $now); - return true; - } - catch (Extensions\IO\InvalidResultException $e) - { - } - } - return false; - } - - protected function runHook($name, Extensions\Hook\Variables $variables) - { - if(!$variables->get('language_id')) - { - $language_iso = isset($this->session->data['language']) ? $this->session->data['language'] : null; - $variables->set('language_id', (int) BulkGate\CartSms\Helpers::getLanguageId($language_iso, $this->oc_di->getDatabase())); - } - - $hook = new Extensions\Hook\Hook( - $this->oc_di->getModule()->getUrl('/module/hook'), - $variables->get('language_id', 0), - $variables->get('store_id', (int) ($this->config->get('config_store_id') ?: 0)), - $this->oc_di->getConnection(), - $this->oc_settings, - new BulkGate\CartSms\HookLoad($this->oc_di->getDatabase()) - ); - - try - { - $hook->run((string) $name, $variables); - return true; - } - catch (Extensions\IO\InvalidResultException $e) - { - return false; - } - } - -} diff --git a/upload/system/library/cartsms/cartsms.ocmod.xml b/upload/system/library/cartsms/cartsms.ocmod.xml deleted file mode 100644 index 3f4d24a..0000000 --- a/upload/system/library/cartsms/cartsms.ocmod.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - CartSMS - SMS module for OpenCart - 6.00 - BulkGate - http://www.cartsms.com/ - - - - - user->hasPermission('access', 'cartsms/sign')) - { - // CartSMS - $cartsms = new CartSms\Init($this->registry); - $data['menus'][] = $cartsms->menu(); - } - ]]> - - - - - - response->setOutput($this->load->view('sale/order_info', $data)); - ]]> - - registry); - - if($init->di()->getSettings()->load("static:application_token", false)) - { - $data['cartsms_css'] = $init->di()->getModule()->getUrl('/'.(defined('BULKGATE_DEV_MODE') ? 'dev' : 'dist').'/css/bulkgate-cartsms.css'); - $data['cartsms_application_id'] = $init->di()->getSettings()->load('static:application_id', ''); - $data['cartsms_language'] = $init->di()->getSettings()->load('main:language', 'en'); - $data['cartsms_widget_api_url'] = $init->di()->getModule()->getUrl('/'.(defined('BULKGATE_DEV_MODE') ? 'dev' : 'dist').'/widget-api/widget-api.js'); - $data['cartsms_authenticate'] = \BulkGate\CartSms\Helpers::fixUrl($this->url->link('cartsms/sign/authenticate', 'user_token=' . $this->session->data['user_token'], true)); - $data['cartsms_customer_iso'] = \BulkGate\CartSms\Helpers::getCountryCode($init->di()->getDatabase(), isset($order_info['payment_country_id']) ? $order_info['payment_country_id'] : -1); - } - } - - ]]> - - - - - -