├── Block ├── Head.php ├── Success.php ├── System │ └── Config │ │ ├── Admin.php │ │ └── Message.php ├── Trustbox.php └── Trustpilot.php ├── Controller └── Adminhtml │ ├── Index │ └── Index.php │ └── Trustpilot │ └── Index.php ├── Helper ├── Data.php ├── HttpClient.php ├── Notifications.php ├── OrderData.php ├── PastOrders.php ├── Products.php ├── TrustpilotHttpClient.php ├── TrustpilotLog.php └── TrustpilotPluginStatus.php ├── LICENSE ├── Model └── Config.php ├── Observer └── OrderSaveObserver.php ├── README.md ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── menu.xml │ ├── routes.xml │ └── system.xml ├── csp_whitelist.xml ├── events.xml └── module.xml ├── registration.php └── view ├── adminhtml ├── layout │ ├── default.xml │ └── trustpilot_reviews_trustpilot_index.xml ├── templates │ └── system │ │ └── config │ │ ├── admin.phtml │ │ └── message.phtml └── web │ ├── css │ ├── trustpilot-message.css │ ├── trustpilot-message.min.css │ ├── trustpilot.css │ └── trustpilot.min.css │ ├── fonts │ ├── trustpilot.eot │ ├── trustpilot.svg │ ├── trustpilot.ttf │ └── trustpilot.woff │ └── js │ ├── admin.js │ ├── admin.min.js │ └── admin.min.js.map └── frontend ├── layout ├── checkout_onepage_success.xml └── default.xml ├── templates ├── head │ └── head.phtml ├── order │ └── success.phtml └── trustbox.phtml └── web ├── css ├── trustpilot.css └── trustpilot.min.css └── js ├── wgxpath.install.js ├── wgxpath.install.min.js └── wgxpath.install.min.js.map /Block/Head.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 22 | $this->_scriptUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_SCRIPT_URL; 23 | $this->_tbWidgetScriptUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_WIDGET_SCRIPT_URL; 24 | $this->_previewScriptUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PREVIEW_SCRIPT_URL; 25 | $this->_previewCssUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PREVIEW_CSS_URL; 26 | 27 | parent::__construct($context, $data); 28 | } 29 | 30 | public function getScriptUrl() 31 | { 32 | return $this->_scriptUrl; 33 | } 34 | 35 | public function getWidgetScriptUrl() 36 | { 37 | return $this->_tbWidgetScriptUrl; 38 | } 39 | 40 | public function getPreviewScriptUrl() 41 | { 42 | return $this->_previewScriptUrl; 43 | } 44 | 45 | public function getPreviewCssUrl() 46 | { 47 | return $this->_previewCssUrl; 48 | } 49 | 50 | public function getInstallationKey() 51 | { 52 | $scope = $this->_helper->getScope(); 53 | $storeId = $this->_helper->getWebsiteOrStoreId(); 54 | return $this->_helper->getKey($scope, $storeId); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Block/Success.php: -------------------------------------------------------------------------------- 1 | _salesFactory = $salesOrderFactory; 36 | $this->_checkoutSession = $checkoutSession; 37 | $this->_helper = $helper; 38 | $this->_orderData = $orderData; 39 | $this->_trustpilotLog = $trustpilotLog; 40 | $this->_storeManager = $context->getStoreManager(); 41 | $this->_pluginStatus = $pluginStatus; 42 | 43 | parent::__construct($context, $data); 44 | } 45 | 46 | public function getOrder() 47 | { 48 | try { 49 | $orderId = $this->_checkoutSession->getLastOrderId(); 50 | $order = $this->_salesFactory->load($orderId); 51 | $storeId = $order->getStoreId(); 52 | 53 | $origin = $this->_storeManager->getStore($storeId)->getBaseUrl(UrlInterface::URL_TYPE_WEB); 54 | $code = $this->_pluginStatus->checkPluginStatus($origin, $storeId); 55 | if ($code > 250 && $code < 254) { 56 | return 'undefined'; 57 | } 58 | 59 | $general_settings = json_decode($this->_helper->getConfig('master_settings_field', $storeId, StoreScopeInterface::SCOPE_STORES))->general; 60 | $data = $this->_orderData->getInvitation($order, 'magento2_success', \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA); 61 | 62 | try { 63 | $data['totalCost'] = $order->getGrandTotal(); 64 | $data['currency'] = $order->getOrderCurrencyCode(); 65 | } catch (\Throwable $e) { 66 | $description = 'Unable to get order total cost'; 67 | $this->_trustpilotLog->error($e, $description, array( 68 | 'orderId' => $orderId, 69 | 'storeId' => $storeId 70 | )); 71 | } catch (\Exception $e) { 72 | $description = 'Unable to get order total cost'; 73 | $this->_trustpilotLog->error($e, $description, array( 74 | 'orderId' => $orderId, 75 | 'storeId' => $storeId 76 | )); 77 | } 78 | 79 | if (!in_array('trustpilotOrderConfirmed', $general_settings->mappedInvitationTrigger)) { 80 | $data['payloadType'] = 'OrderStatusUpdate'; 81 | } 82 | 83 | return json_encode($data, JSON_HEX_APOS); 84 | } catch (\Throwable $e) { 85 | $error = array('message' => $e->getMessage()); 86 | $data = array('error' => $error); 87 | $vars = array( 88 | 'orderId' => isset($orderId) ? $orderId : null, 89 | 'storeId' => isset($storeId) ? $storeId : null, 90 | ); 91 | $description = 'Unable to get order data'; 92 | $this->_trustpilotLog->error($e, $description, $vars); 93 | return json_encode($data, JSON_HEX_APOS); 94 | } catch (\Exception $e) { 95 | $error = array('message' => $e->getMessage()); 96 | $data = array('error' => $error); 97 | $vars = array( 98 | 'orderId' => isset($orderId) ? $orderId : null, 99 | 'storeId' => isset($storeId) ? $storeId : null, 100 | ); 101 | $description = 'Unable to get order data'; 102 | $this->_trustpilotLog->error($e, $description, $vars); 103 | return json_encode($data, JSON_HEX_APOS); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Block/System/Config/Admin.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 26 | $this->_pastOrders = $pastOrders; 27 | $this->_trustpilotLog = $trustpilotLog; 28 | parent::__construct($context, $data); 29 | } 30 | 31 | public function getIntegrationAppUrl() 32 | { 33 | return $this->_helper->getIntegrationAppUrl(); 34 | } 35 | 36 | public function getSettings($scope, $storeId) { 37 | return base64_encode($this->_helper->getConfig('master_settings_field', $storeId, $scope)); 38 | } 39 | 40 | public function getPageUrls($scope, $storeId) { 41 | return base64_encode(json_encode($this->_helper->getPageUrls($scope, $storeId))); 42 | } 43 | 44 | public function getCustomTrustBoxes($scope, $storeId) 45 | { 46 | $customTrustboxes = $this->_helper->getConfig('custom_trustboxes', $storeId, $scope); 47 | if ($customTrustboxes) { 48 | return $customTrustboxes; 49 | } 50 | return "{}"; 51 | } 52 | 53 | public function getProductIdentificationOptions() { 54 | return $this->_helper->getProductIdentificationOptions(); 55 | } 56 | 57 | public function getStoreInformation() { 58 | return $this->_helper->getStoreInformation(); 59 | } 60 | 61 | public function getPluginStatus($scope, $storeId) { 62 | return base64_encode($this->_helper->getConfig('plugin_status', $storeId, $scope)); 63 | } 64 | 65 | public function getPastOrdersInfo($scope, $storeId) { 66 | $info = $this->_pastOrders->getPastOrdersInfo($scope, $storeId); 67 | $info['basis'] = 'plugin'; 68 | return json_encode($info); 69 | } 70 | 71 | public function getSku($scope, $storeId) 72 | { 73 | try { 74 | $product = $this->_helper->getFirstProduct($scope, $storeId); 75 | if ($product) { 76 | $skuSelector = json_decode($this->_helper->getConfig('master_settings_field', $storeId, $scope))->skuSelector; 77 | $productId = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PRODUCT_ID_PREFIX . $this->_helper->loadSelector($product, 'id'); 78 | if ($skuSelector == 'none') $skuSelector = 'sku'; 79 | return $this->_helper->loadSelector($product, $skuSelector) . ',' . $productId; 80 | } 81 | } catch (\Throwable $throwable) { 82 | $description = 'Unable to get sku in Admin.php'; 83 | $this->_trustpilotLog->error($throwable, $description, array( 84 | 'scope' => $scope, 85 | 'storeId' => $storeId 86 | )); 87 | return ''; 88 | } catch (\Exception $exception) { 89 | $description = 'Unable to get sku in Admin.php'; 90 | $this->_trustpilotLog->error($exception, $description, array( 91 | 'scope' => $scope, 92 | 'storeId' => $storeId 93 | )); 94 | return ''; 95 | } 96 | } 97 | 98 | public function getProductName($scope, $storeId) 99 | { 100 | return $this->_helper->getFirstProduct($scope, $storeId)->getName(); 101 | } 102 | 103 | protected function _getElementHtml(AbstractElement $element) 104 | { 105 | return $this->_toHtml(); 106 | } 107 | 108 | public function getVersion() 109 | { 110 | return $this->_helper->getVersion(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Block/System/Config/Message.php: -------------------------------------------------------------------------------- 1 | _storeManager->isSingleStoreMode()) { 13 | return $this->_toHtml(); 14 | } else { 15 | return null; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Block/Trustbox.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 31 | $this->_registry = $registry; 32 | $this->_request = $request; 33 | $this->_storeManager = $context->getStoreManager(); 34 | $this->_tbWidgetScriptUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_WIDGET_SCRIPT_URL; 35 | $this->_urlInterface = ObjectManager::getInstance()->get('Magento\Framework\UrlInterface'); 36 | $this->_linkManagement = $linkManagement; 37 | parent::__construct($context, $data); 38 | } 39 | 40 | private function getCurrentUrl() { 41 | return $this->_urlInterface->getCurrentUrl(); 42 | } 43 | 44 | public function getWidgetScriptUrl() 45 | { 46 | return $this->_tbWidgetScriptUrl; 47 | } 48 | 49 | public function loadTrustboxes() 50 | { 51 | $scope = $this->_helper->getScope(); 52 | $storeId = $this->_helper->getWebsiteOrStoreId(); 53 | $settings = json_decode($this->_helper->getConfig('master_settings_field', $storeId, $scope)); 54 | $trustboxSettings = $settings->trustbox; 55 | if (isset($trustboxSettings->trustboxes)) { 56 | $currentUrl = $this->getCurrentUrl(); 57 | $currentCategory = $this->_registry->registry('current_category'); 58 | $loadedTrustboxes = $this->loadPageTrustboxes($settings, $currentUrl); 59 | 60 | if ($this->_registry->registry('current_product')) { 61 | $loadedTrustboxes = array_merge((array)$this->loadPageTrustboxes($settings, 'product'), (array)$loadedTrustboxes); 62 | } else if ($currentCategory) { 63 | $loadedTrustboxes = array_merge((array)$this->loadPageTrustboxes($settings, 'category'), (array)$loadedTrustboxes); 64 | if ($this->repeatData($loadedTrustboxes)) { 65 | $trustboxSettings->categoryProductsData = $this->loadCategoryProductInfo($scope, $storeId, $currentCategory); 66 | } 67 | } 68 | if ($this->_request->getFullActionName() == 'cms_index_index') { 69 | $loadedTrustboxes = array_merge((array)$this->loadPageTrustboxes($settings, 'landing'), (array)$loadedTrustboxes); 70 | } 71 | 72 | if (count($loadedTrustboxes) > 0) { 73 | $trustboxSettings->trustboxes = $loadedTrustboxes; 74 | return json_encode($trustboxSettings, JSON_HEX_APOS); 75 | } 76 | } 77 | 78 | return '{"trustboxes":[]}'; 79 | } 80 | 81 | private function repeatData($trustBoxes) { 82 | foreach ($trustBoxes as $trustbox) { 83 | if (isset($trustbox->repeat) && $trustbox->repeat || true) { 84 | return true; 85 | } 86 | } 87 | return false; 88 | } 89 | 90 | private function loadSkus($current_product, $skuSelector, $includeIds) 91 | { 92 | $skus = array(); 93 | if ($includeIds) { 94 | array_push($skus, \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PRODUCT_ID_PREFIX . $current_product->getId()); 95 | } 96 | $productSku = $this->_helper->loadSelector($current_product, $skuSelector); 97 | if ($productSku) { 98 | array_push($skus, $productSku); 99 | } 100 | 101 | if ($current_product->getTypeId() == 'configurable') { 102 | $collection = $this->_linkManagement->getChildren($current_product->getSku()); 103 | foreach ($collection as $product) { 104 | if ($includeIds) { 105 | array_push($skus, \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PRODUCT_ID_PREFIX . $product->getId()); 106 | } 107 | $productSku = $this->_helper->loadSelector($product, $skuSelector); 108 | if ($productSku) { 109 | array_push($skus, $productSku); 110 | } 111 | } 112 | } 113 | return implode(',', $skus); 114 | } 115 | 116 | private function loadPageTrustboxes($settings, $page) 117 | { 118 | $data = []; 119 | $skuSelector = empty($settings->skuSelector) || $settings->skuSelector == 'none' ? 'sku' : $settings->skuSelector; 120 | foreach ($settings->trustbox->trustboxes as $trustbox) { 121 | if ((rtrim($trustbox->page, '/') == rtrim($page, '/') || $this->checkCustomPage($trustbox->page, $page)) && $trustbox->enabled == 'enabled') { 122 | $current_product = $this->_registry->registry('current_product'); 123 | if ($current_product) { 124 | $sku = $this->loadSkus($current_product, $skuSelector, true); 125 | if (strlen($sku) > \Trustpilot\Reviews\Model\Config::MAX_SKU_LENGTH) { 126 | $sku = $this->loadSkus($current_product, $skuSelector, false); 127 | } 128 | $trustbox->sku = $sku; 129 | $trustbox->name = $current_product->getName(); 130 | } 131 | array_push($data, $trustbox); 132 | } 133 | } 134 | return $data; 135 | } 136 | 137 | private function checkCustomPage($tbPage, $page) { 138 | return ( 139 | $tbPage == strtolower(base64_encode($page . '/')) || 140 | $tbPage == strtolower(base64_encode($page)) || 141 | $tbPage == strtolower(base64_encode(rtrim($page, '/'))) 142 | ); 143 | } 144 | 145 | public function loadCategoryProductInfo($scope, $storeId, $category = null) { 146 | try { 147 | if ($category == null) { 148 | $block = $this->getLayout()->getBlock('category.products.list'); 149 | $products = $block->getLoadedProductCollection(); 150 | } else { 151 | $products = $category->getProductCollection(); 152 | } 153 | return $this->_helper->loadCategoryProductInfo($products, $scope, $storeId); 154 | } catch(\Throwable $e) { 155 | return array(); 156 | } catch(\Exception $e) { 157 | return array(); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Block/Trustpilot.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 30 | $this->_pastOrders = $pastOrders; 31 | $this->_trustpilotLog = $trustpilotLog; 32 | parent::__construct($context, $data); 33 | } 34 | 35 | public function getIntegrationAppUrl() 36 | { 37 | return $this->_helper->getIntegrationAppUrl(); 38 | } 39 | 40 | public function getSettings($scope, $storeId) { 41 | return base64_encode($this->_helper->getConfig('master_settings_field', $storeId, $scope)); 42 | } 43 | 44 | public function getPageUrls($scope, $storeId) { 45 | return base64_encode(json_encode($this->_helper->getPageUrls($scope, $storeId))); 46 | } 47 | 48 | public function getCustomTrustBoxes($scope, $storeId) 49 | { 50 | $customTrustboxes = $this->_helper->getConfig('custom_trustboxes', $storeId, $scope); 51 | if ($customTrustboxes) { 52 | return $customTrustboxes; 53 | } 54 | return "{}"; 55 | } 56 | 57 | public function getProductIdentificationOptions() { 58 | return $this->_helper->getProductIdentificationOptions(); 59 | } 60 | 61 | public function getStoreInformation() { 62 | return $this->_helper->getStoreInformation(); 63 | } 64 | 65 | public function getPluginStatus($scope, $storeId) { 66 | return base64_encode($this->_helper->getConfig('plugin_status', $storeId, $scope)); 67 | } 68 | 69 | public function getPastOrdersInfo($scope, $storeId) { 70 | $info = $this->_pastOrders->getPastOrdersInfo($scope, $storeId); 71 | $info['basis'] = 'plugin'; 72 | return json_encode($info); 73 | } 74 | 75 | public function getSku($scope, $storeId) 76 | { 77 | try { 78 | $product = $this->_helper->getFirstProduct($scope, $storeId); 79 | if ($product) { 80 | $skuSelector = json_decode($this->_helper->getConfig('master_settings_field', $storeId, $scope))->skuSelector; 81 | $productId = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PRODUCT_ID_PREFIX . $this->_helper->loadSelector($product, 'id'); 82 | if ($skuSelector == 'none') $skuSelector = 'sku'; 83 | return $this->_helper->loadSelector($product, $skuSelector) . ',' . $productId; 84 | } 85 | } catch (\Throwable $exception) { 86 | $description = 'Unable to get sku in Trustpilot.php'; 87 | $this->_trustpilotLog->error($exception, $description, array('scope' => $scope, 'storeId' => $storeId)); 88 | return ''; 89 | } catch (\Exception $exception) { 90 | $description = 'Unable to get sku in Trustpilot.php'; 91 | $this->_trustpilotLog->error($exception, $description, array('scope' => $scope, 'storeId' => $storeId)); 92 | return ''; 93 | } 94 | } 95 | 96 | public function getProductName($scope, $storeId) 97 | { 98 | return $this->_helper->getFirstProduct($scope, $storeId)->getName(); 99 | } 100 | 101 | public function _prepareLayout() 102 | { 103 | return parent::_prepareLayout(); 104 | } 105 | 106 | public function getVersion() 107 | { 108 | return $this->_helper->getVersion(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Index/Index.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 24 | $this->_pastOrders = $pastOrders; 25 | $this->_products = $products; 26 | } 27 | 28 | public function execute() 29 | { 30 | session_write_close(); 31 | if ($this->getRequest()->isAjax()) { 32 | $post = $this->getRequest()->getPostValue(); 33 | $scope = $post['scope']; 34 | $scopeId = (int) $post['scopeId']; 35 | switch ($post["action"]) { 36 | case 'handle_save_changes': 37 | if (array_key_exists('settings', $post)) { 38 | $this->setConfig('master_settings_field', $post['settings'], $scope, $scopeId); 39 | break; 40 | } else if (array_key_exists('pageUrls', $post)) { 41 | $this->setConfig('page_urls', $post['pageUrls'], $scope, $scopeId); 42 | break; 43 | } else if (array_key_exists('customTrustBoxes', $post)) { 44 | $this->setConfig('custom_trustboxes', $post['customTrustBoxes'], $scope, $scopeId); 45 | break; 46 | } 47 | break; 48 | case 'handle_past_orders': 49 | if (array_key_exists('sync', $post)) { 50 | $this->_pastOrders->sync($post["sync"], $scope, $scopeId); 51 | $output = $this->_pastOrders->getPastOrdersInfo($scope, $scopeId); 52 | $output['basis'] = 'plugin'; 53 | $output['pastOrders']['showInitial'] = false; 54 | $this->getResponse()->setBody(json_encode($output)); 55 | break; 56 | } else if (array_key_exists('resync', $post)) { 57 | $this->_pastOrders->resync($scope, $scopeId); 58 | $output = $this->_pastOrders->getPastOrdersInfo($scope, $scopeId); 59 | $output['basis'] = 'plugin'; 60 | $this->getResponse()->setBody(json_encode($output)); 61 | break; 62 | } else if (array_key_exists('issynced', $post)) { 63 | $output = $this->_pastOrders->getPastOrdersInfo($scope, $scopeId); 64 | $output['basis'] = 'plugin'; 65 | $this->getResponse()->setBody(json_encode($output)); 66 | break; 67 | } else if (array_key_exists('showPastOrdersInitial', $post)) { 68 | $this->_helper->setConfig("show_past_orders_initial", $post["showPastOrdersInitial"], $scope, $scopeId); 69 | $this->getResponse()->setBody('true'); 70 | break; 71 | } 72 | break; 73 | case 'check_product_skus': 74 | $result = array( 75 | 'skuScannerResults' => $this->_products->checkSkus($post['skuSelector']) 76 | ); 77 | $this->getResponse()->setBody(json_encode($result)); 78 | break; 79 | case 'get_signup_data': 80 | $result = array( 81 | 'trustpilot_signup_data' => base64_encode(json_encode($this->_helper->getBusinessInformation($scope, $scopeId))) 82 | ); 83 | $this->getResponse()->setBody(json_encode($result)); 84 | break; 85 | case 'get_category_product_info': 86 | $result = array( 87 | 'categoryProductsData' => $this->_helper->loadDefaultCategoryProductInfo($scope, $scopeId) 88 | ); 89 | $this->getResponse()->setBody(json_encode($result)); 90 | break; 91 | } 92 | } 93 | 94 | return false; 95 | } 96 | 97 | private function setConfig($key, $value, $scope, $scopeId) 98 | { 99 | $this->_helper->setConfig($key, $value, $scope, $scopeId); 100 | $this->getResponse()->setBody($value); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Trustpilot/Index.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 28 | } 29 | 30 | /** 31 | * Load the page defined in view/adminhtml/layout/exampleadminnewpage_helloworld_index.xml 32 | * 33 | * @return \Magento\Framework\View\Result\Page 34 | */ 35 | public function execute() 36 | { 37 | $resultPage = $this->resultPageFactory->create(); 38 | $resultPage->setActiveMenu('Trustpilot_Reviews::Trustpilot'); 39 | $resultPage->getConfig()->getTitle()->prepend(__('Trustpilot')); 40 | return $resultPage; 41 | } 42 | } -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | _storeManager = $storeManager; 60 | $this->_categoryCollectionFactory = $categoryCollectionFactory; 61 | $this->_productCollectionFactory = $productCollectionFactory; 62 | $this->_websiteCollectionFactory = $websiteCollectionFactory; 63 | $this->_searchCriteriaBuilder = $searchCriteriaBuilder; 64 | $this->_attributeRepository = $attributeRepository; 65 | $this->_configWriter = $configWriter; 66 | parent::__construct($context); 67 | $this->_request = $context->getRequest(); 68 | $this->_storeRepository = $storeRepository; 69 | $this->_integrationAppUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_INTEGRATION_APP_URL; 70 | $this->_reinitableConfig = $reinitableConfig; 71 | $this->_registry = $registry; 72 | $this->_linkManagement = $linkManagement; 73 | $this->_trustpilotLog = $trustpilotLog; 74 | $this->_url = $url; 75 | } 76 | 77 | public function getIntegrationAppUrl() 78 | { 79 | $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') 80 | || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) 81 | || isset($_SERVER['HTTP_USESSL']) 82 | || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') 83 | || (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') 84 | ? "https:" : "http:"; 85 | $domainName = $protocol . $this->_integrationAppUrl; 86 | return $domainName; 87 | } 88 | 89 | public function getKey($scope, $storeId) 90 | { 91 | return trim(json_decode(self::getConfig('master_settings_field', $storeId, $scope))->general->key); 92 | } 93 | 94 | private function getDefaultConfigValues($key) 95 | { 96 | $config = array(); 97 | $config['master_settings_field'] = json_encode( 98 | array( 99 | 'general' => array( 100 | 'key' => '', 101 | 'invitationTrigger' => 'orderConfirmed', 102 | 'mappedInvitationTrigger' => array(), 103 | ), 104 | 'trustbox' => array( 105 | 'trustboxes' => array(), 106 | ), 107 | 'skuSelector' => 'none', 108 | 'mpnSelector' => 'none', 109 | 'gtinSelector' => 'none', 110 | 'pastOrderStatuses' => array('processing', 'complete'), 111 | ) 112 | ); 113 | $config['sync_in_progress'] = 'false'; 114 | $config['show_past_orders_initial'] = 'true'; 115 | $config['past_orders'] = '0'; 116 | $config['failed_orders'] = '{}'; 117 | $config['custom_trustboxes'] = '{}'; 118 | $config['plugin_status'] = json_encode( 119 | array( 120 | 'pluginStatus' => 200, 121 | 'blockedDomains' => array(), 122 | ) 123 | ); 124 | 125 | if (isset($config[$key])) { 126 | return $config[$key]; 127 | } 128 | return false; 129 | } 130 | 131 | public function getWebsiteOrStoreId() 132 | { 133 | if ($this->_request->getParam('store') !== null && strlen($this->_request->getParam('store'))) { 134 | return (int) $this->_request->getParam('store', 0); 135 | } 136 | if ($this->_request->getParam('website') !== null && strlen($this->_request->getParam('website'))) { 137 | return (int) $this->_request->getParam('website', 0); 138 | } 139 | if ($this->isAdminPage() && $this->_storeManager->getStore()->getWebsiteId()) { 140 | return (int) $this->_storeManager->getStore()->getWebsiteId(); 141 | } 142 | if ($this->_storeManager->getStore()->getStoreId()) { 143 | return (int) $this->_storeManager->getStore()->getStoreId(); 144 | } 145 | return 0; 146 | } 147 | 148 | public function getScope() 149 | { 150 | // user is on the admin store level 151 | if ($this->_request->getParam('store') !== null && strlen($this->_request->getParam('store'))) { 152 | return StoreScopeInterface::SCOPE_STORES; 153 | } 154 | // user is on the admin website level 155 | if ($this->_request->getParam('website') !== null && strlen($this->_request->getParam('website'))) { 156 | return StoreScopeInterface::SCOPE_WEBSITES; 157 | } 158 | // is user is on admin page, try to automatically detect his website scope 159 | if ($this->isAdminPage() && $this->_storeManager->getStore()->getWebsiteId()) { 160 | return StoreScopeInterface::SCOPE_WEBSITES; 161 | } 162 | // user is on the storefront 163 | if ($this->_storeManager->getStore()->getStoreId()) { 164 | return StoreScopeInterface::SCOPE_STORES; 165 | } 166 | // user at admin default level 167 | return 'default'; 168 | } 169 | 170 | public function getConfig($config, $storeId, $scope = null) 171 | { 172 | $path = self::TRUSTPILOT_SETTINGS . $config; 173 | 174 | if ($scope === null) { 175 | $scope = $this->getScope(); 176 | } elseif ($scope === 'store') { 177 | $scope = 'stores'; 178 | } elseif ($scope === 'website') { 179 | $scope = 'websites'; 180 | } 181 | 182 | $setting = $this->scopeConfig->getValue($path, $scope, $storeId); 183 | 184 | if ($config === 'master_settings_field') { 185 | return ($setting && json_decode($setting) != null) ? $setting : $this->getDefaultConfigValues($config); 186 | } else { 187 | return $setting ? $setting : $this->getDefaultConfigValues($config); 188 | } 189 | } 190 | 191 | public function setConfig($config, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0) 192 | { 193 | if ($scope === 'store') { 194 | $scope = 'stores'; 195 | } elseif ($scope === 'website') { 196 | $scope = 'websites'; 197 | } 198 | $this->_configWriter->save(self::TRUSTPILOT_SETTINGS . $config, $value, $scope, $scopeId); 199 | 200 | $this->_reinitableConfig->reinit(); 201 | } 202 | 203 | public function getVersion() { 204 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); 205 | $productMetadata = $objectManager->get('Magento\Framework\App\ProductMetadataInterface'); 206 | if (method_exists($productMetadata, 'getVersion')) { 207 | return $productMetadata->getVersion(); 208 | } else { 209 | return \Magento\Framework\AppInterface::VERSION; 210 | } 211 | } 212 | 213 | public function getPageUrls($scope, $storeId) 214 | { 215 | $pageUrls = new \stdClass(); 216 | $pageUrls->landing = $this->getPageUrl('trustpilot_trustbox_homepage', $scope, $storeId); 217 | $pageUrls->category = $this->getPageUrl('trustpilot_trustbox_category', $scope, $storeId); 218 | $pageUrls->product = $this->getPageUrl('trustpilot_trustbox_product', $scope, $storeId); 219 | $customPageUrls = json_decode($this->getConfig('page_urls', $storeId, $scope)); 220 | $urls = (object) array_merge((array) $customPageUrls, (array) $pageUrls); 221 | return $urls; 222 | } 223 | 224 | public function getDefaultStoreIdByWebsiteId($websiteId) { 225 | foreach ($this->_storeManager->getWebsites() as $website) { 226 | if ($website->getId() === $websiteId) { 227 | $storeIds = $website->getStoreIds(); 228 | return isset($storeIds[0]) ? $storeIds[0] : 0; 229 | } 230 | } 231 | } 232 | 233 | public function getFirstProduct($scope, $storeId) 234 | { 235 | if ($scope === 'website' || $scope === 'websites') { 236 | $storeId = $this->getDefaultStoreIdByWebsiteId($storeId); 237 | } 238 | $collection = $this->_productCollectionFactory->create(); 239 | $collection->addAttributeToSelect('*'); 240 | $collection->setStore($storeId); 241 | $collection->addStoreFilter($storeId); 242 | $collection->addAttributeToFilter('status', 1); 243 | $collection->addAttributeToFilter('visibility', array(2, 3, 4)); 244 | $collection->addUrlRewrite(); 245 | $collection->setPageSize(1); 246 | return $collection->getFirstItem(); 247 | } 248 | 249 | public function getPageUrl($page, $scope, $storeId) 250 | { 251 | try { 252 | if ($scope === 'website' || $scope === 'websites') { 253 | $storeId = $this->getDefaultStoreIdByWebsiteId($storeId); 254 | } 255 | $storeCode = $this->_storeManager->getStore($storeId)->getCode(); 256 | switch ($page) { 257 | case 'trustpilot_trustbox_homepage': 258 | return $this->_storeManager->getStore($storeId)->getBaseUrl().'?___store='.$storeCode; 259 | case 'trustpilot_trustbox_category': 260 | $category = $this->getFirstCategory($storeId); 261 | $categoryUrl = $this->_url->getUrl('catalog/category/view', [ 262 | '_scope' => $storeId, 263 | 'id' => $category->getId(), 264 | '_nosid' => true, 265 | '_query' => ['___store' => $storeCode] 266 | ]); 267 | return $categoryUrl; 268 | case 'trustpilot_trustbox_product': 269 | $product = $this->getFirstProduct('store', $storeId); 270 | $productUrl = $this->_url->getUrl('catalog/product/view', [ 271 | '_scope' => $storeId, 272 | 'id' => $product->getId(), 273 | '_nosid' => true, 274 | '_query' => ['___store' => $storeCode] 275 | ]); 276 | return $productUrl; 277 | } 278 | } catch (\Throwable $e) { 279 | $description = 'Unable to find URL for a page ' . $page; 280 | $this->_trustpilotLog->error($e, $description, array( 281 | 'page' => $page, 282 | 'storeId' => $storeId 283 | )); 284 | return $this->_storeManager->getStore()->getBaseUrl(); 285 | } catch (\Exception $e) { 286 | $description = 'Unable to find URL for a page ' . $page; 287 | $this->_trustpilotLog->error($e, $description, array( 288 | 'page' => $page, 289 | 'storeId' => $storeId 290 | )); 291 | return $this->_storeManager->getStore()->getBaseUrl(); 292 | } 293 | } 294 | 295 | public function getFirstCategory($storeId) { 296 | $collection = $this->_categoryCollectionFactory->create(); 297 | $collection->addAttributeToSelect('*'); 298 | $collection->setStore($storeId); 299 | $collection->addAttributeToFilter('is_active', 1); 300 | $collection->addAttributeToFilter('children_count', 0); 301 | $collection->addUrlRewriteToResult(); 302 | $collection->setPageSize(1); 303 | return $collection->getFirstItem(); 304 | } 305 | 306 | public function getProductIdentificationOptions() 307 | { 308 | $fields = array('none', 'sku', 'id'); 309 | $optionalFields = array('upc', 'isbn', 'brand', 'manufacturer', 'ean'); 310 | $dynamicFields = array('mpn', 'gtin'); 311 | $attrs = array_map(function ($t) { return $t; }, $this->getAttributes()); 312 | 313 | foreach ($attrs as $attr) { 314 | foreach ($optionalFields as $field) { 315 | if ($attr == $field) { 316 | array_push($fields, $field); 317 | } 318 | } 319 | foreach ($dynamicFields as $field) { 320 | if (stripos($attr, $field) !== false) { 321 | array_push($fields, $attr); 322 | } 323 | } 324 | } 325 | 326 | return json_encode($fields); 327 | } 328 | 329 | private function getAttributes() 330 | { 331 | $attr = array(); 332 | 333 | $searchCriteria = $this->_searchCriteriaBuilder->create(); 334 | $attributeRepository = $this->_attributeRepository->getList( 335 | 'catalog_product', 336 | $searchCriteria 337 | ); 338 | foreach ($attributeRepository->getItems() as $items) { 339 | array_push($attr, $items->getAttributeCode()); 340 | } 341 | return $attr; 342 | } 343 | 344 | public function loadSelector($product, $selector, $childProducts = null) 345 | { 346 | $values = array(); 347 | if (!empty($childProducts)) { 348 | foreach ($childProducts as $childProduct) { 349 | $value = $this->loadAttributeValue($childProduct, $selector); 350 | if (!empty($value)) { 351 | array_push($values, $value); 352 | } 353 | } 354 | } 355 | if (!empty($values)) { 356 | return implode(',', $values); 357 | } else { 358 | return $this->loadAttributeValue($product, $selector); 359 | } 360 | } 361 | 362 | private function loadAttributeValue($product, $selector) 363 | { 364 | try { 365 | if ($selector == 'id') { 366 | return (string) $product->getId(); 367 | } 368 | if ($attribute = $product->getResource()->getAttribute($selector)) { 369 | $data = $product->getData($selector); 370 | $label = $attribute->getSource()->getOptionText($data); 371 | if (is_array($label)) { 372 | $label = implode(', ', $label); 373 | } 374 | return $label ? $label : (string) $data; 375 | } else { 376 | return $label = ''; 377 | } 378 | } catch(\Throwable $e) { 379 | $description = 'Unable get attribute value for selector ' . $selector; 380 | $this->_trustpilotLog->error($e, $description, array( 381 | 'product' => $product, 382 | 'selector' => $selector 383 | )); 384 | return ''; 385 | } catch(\Exception $e) { 386 | $description = 'Unable get attribute value for selector ' . $selector; 387 | $this->_trustpilotLog->error($e, $description, array( 388 | 'product' => $product, 389 | 'selector' => $selector 390 | )); 391 | return ''; 392 | } 393 | } 394 | 395 | public function getStoreInformation() { 396 | $stores = $this->_storeRepository->getList(); 397 | $result = array(); 398 | //Each store view is unique 399 | foreach ($stores as $store) { 400 | if ($store->isActive() && $store->getId() != 0) { 401 | $names = array( 402 | 'site' => $store->getWebsite()->getName(), 403 | 'store' => $store->getGroup()->getName(), 404 | 'view' => $store->getName(), 405 | ); 406 | $item = array( 407 | 'ids' => array((string) $store->getWebsite()->getId(), (string) $store->getGroupId(), (string) $store->getStoreId()), 408 | 'names' => $names, 409 | 'domain' => parse_url($store->getBaseUrl(UrlInterface::URL_TYPE_WEB), PHP_URL_HOST), 410 | ); 411 | array_push($result, $item); 412 | } 413 | } 414 | return base64_encode(json_encode($result)); 415 | } 416 | 417 | public function isAdminPage() { 418 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); 419 | $state = $objectManager->get('Magento\Framework\App\State'); 420 | return 'adminhtml' === $state->getAreaCode(); 421 | } 422 | 423 | public function getBusinessInformation($scope, $scopeId) { 424 | $config = $this->scopeConfig; 425 | $useSecure = $config->getValue('web/secure/use_in_frontend', $scope, $scopeId); 426 | return array( 427 | 'website' => $config->getValue('web/'. ($useSecure ? 'secure' : 'unsecure') .'/base_url', $scope, $scopeId), 428 | 'company' => $config->getValue('general/store_information/name', $scope, $scopeId), 429 | 'name' => $config->getValue('trans_email/ident_general/name', $scope, $scopeId), 430 | 'email' => $config->getValue('trans_email/ident_general/email', $scope, $scopeId), 431 | 'country' => $config->getValue('general/store_information/country_id', $scope, $scopeId), 432 | 'phone' => $config->getValue('general/store_information/phone', $scope, $scopeId) 433 | ); 434 | } 435 | 436 | public function loadCategoryProductInfo($products, $scope, $scopeId) { 437 | try { 438 | $settings = json_decode(self::getConfig('master_settings_field', $scopeId, $scope)); 439 | $skuSelector = empty($settings->skuSelector) || $settings->skuSelector == 'none' ? 'sku' : $settings->skuSelector; 440 | $productList = $variationSkus = $variationIds = array(); 441 | 442 | foreach ($products->getItems() as $product) { 443 | if ($product->getTypeId() == 'configurable') { 444 | $childProducts = $this->_linkManagement->getChildren($product->getSku()); 445 | $variationSkus = $skuSelector != 'id' ? $this->loadSelector($product, $skuSelector, $childProducts) : array(); 446 | $variationIds = $this->loadSelector($product, 'id', $childProducts); 447 | } 448 | $sku = $skuSelector != 'id' ? $this->loadSelector($product, $skuSelector) : ''; 449 | $id = $this->loadSelector($product, 'id'); 450 | array_push($productList, array( 451 | "sku" => $sku, 452 | "id" => $id, 453 | "variationIds" => $variationIds, 454 | "variationSkus" => $variationSkus, 455 | "productUrl" => $product->getProductUrl() ?: '', 456 | "name" => $product->getName(), 457 | )); 458 | } 459 | return $productList; 460 | } catch(\Throwable $e) { 461 | $description = 'Unable to load category product info '; 462 | $this->_trustpilotLog->error($e, $description, array( 463 | 'scope' => $scope, 464 | 'scopeId' => $scopeId 465 | )); 466 | return array(); 467 | } catch(\Exception $e) { 468 | $description = 'Unable to load category product info '; 469 | $this->_trustpilotLog->error($e, $description, array( 470 | 'scope' => $scope, 471 | 'scopeId' => $scopeId 472 | )); 473 | return array(); 474 | } 475 | } 476 | 477 | public function loadDefaultCategoryProductInfo($scope, $scopeId) { 478 | try { 479 | $category = $this->getFirstCategory($scopeId); 480 | $limit = $this->scopeConfig->getValue('catalog/frontend/grid_per_page'); 481 | $page = 1; 482 | 483 | $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); 484 | $layerResolver = $objectManager->get(\Magento\Catalog\Model\Layer\Resolver::class); 485 | $layer = $layerResolver->get(); 486 | $layer->setCurrentCategory($category); 487 | $products = $layer->getProductCollection()->setPage($page, $limit); 488 | return $this->loadCategoryProductInfo($products, $scope, $scopeId); 489 | } catch(\Throwable $e) { 490 | $description = 'Unable to load category product info '; 491 | $this->_trustpilotLog->error($e, $description, array( 492 | 'scope' => $scope, 493 | 'scopeId' => $scopeId 494 | )); 495 | return array(); 496 | } catch(\Exception $e) { 497 | $description = 'Unable to load category product info '; 498 | $this->_trustpilotLog->error($e, $description, array( 499 | 'scope' => $scope, 500 | 'scopeId' => $scopeId 501 | )); 502 | return array(); 503 | } 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /Helper/HttpClient.php: -------------------------------------------------------------------------------- 1 | _logger = $logger; 16 | } 17 | 18 | public function request($url, $httpRequest, $origin = null, $data = null, $params = array(), $timeout = self::HTTP_REQUEST_TIMEOUT) 19 | { 20 | try{ 21 | $ch = curl_init(); 22 | $this->setCurlOptions($ch, $httpRequest, $data, $origin, $timeout); 23 | $url = $this->buildParams($url, $params); 24 | curl_setopt($ch, CURLOPT_URL, $url); 25 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 26 | $content = curl_exec($ch); 27 | $responseData = json_decode($content); 28 | $responseInfo = curl_getinfo($ch); 29 | $responseCode = $responseInfo['http_code']; 30 | curl_close($ch); 31 | $response = array(); 32 | $response['code'] = $responseCode; 33 | if (is_object($responseData) || is_array($responseData)) { 34 | $response['data'] = $responseData; 35 | } 36 | return $response; 37 | } catch (\Exception $e){ 38 | //intentionally empty 39 | } 40 | } 41 | 42 | private function jsonEncoder($data) 43 | { 44 | if (function_exists('json_encode')) 45 | return json_encode($data); 46 | elseif (method_exists('Tools', 'jsonEncode')) 47 | return Tools::jsonEncode($data); 48 | } 49 | 50 | private function setCurlOptions($ch, $httpRequest, $data, $origin, $timeout) 51 | { 52 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); 53 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 54 | if ($httpRequest == 'POST') { 55 | $encoded_data = $this->jsonEncoder($data); 56 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); 57 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('content-type: application/json', 'Content-Length: ' . strlen($encoded_data), 'Origin: ' . $origin)); 58 | curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data); 59 | return; 60 | } elseif ($httpRequest == 'GET') { 61 | curl_setopt($ch, CURLOPT_POST, false); 62 | return; 63 | } 64 | return; 65 | } 66 | 67 | private function buildParams($url, $params = array()) 68 | { 69 | if (!empty($params) && is_array($params)) { 70 | $url .= '?'.http_build_query($params); 71 | } 72 | return $url; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Helper/Notifications.php: -------------------------------------------------------------------------------- 1 | _inbox = $inbox; 15 | } 16 | 17 | public function createAdminNotification($title, $desc, $url) 18 | { 19 | $this->_inbox->addCritical($title, $desc, $url); 20 | 21 | return $this; 22 | } 23 | 24 | public function getNotificationsCollection($title, $desc) 25 | { 26 | $collection = $this->_inbox->getCollection(); 27 | 28 | $collection->getSelect() 29 | ->where('title like "%'.$title.'%" and description like "%'.$desc.'%"') 30 | ->where('is_read != 1') 31 | ->where('is_remove != 1'); 32 | 33 | return $collection; 34 | } 35 | 36 | public function getLatestMissingKeyNotification() 37 | { 38 | $collection = $this->getNotificationsCollection('Trustpilot', 'installation key'); 39 | 40 | $collection->getSelect() 41 | ->order('notification_id DESC') 42 | ->limit(1); 43 | 44 | $items = array_values($collection->getItems()); 45 | return count($items) > 0 ? $items[0] : null; 46 | } 47 | 48 | public function createMissingKeyNotification() 49 | { 50 | $this->createAdminNotification( 51 | 'An invitation to leave a review on Trustpilot has failed due to a missing installation key.', 52 | 'Please enter your installation key in the Trustpilot extension configuration page to complete the integration. You can find your installation key in the Trustpilot Business > Integrations > Apps > Magento integration guide.', 53 | 'https://support.trustpilot.com/hc/en-us/articles/203934298-How-to-Guide-Trustpilot-s-Magento-Application' 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Helper/OrderData.php: -------------------------------------------------------------------------------- 1 | _storeManager = $storeManager; 28 | $this->_helper = $helper; 29 | $this->_categoryCollectionFactory = $categoryCollectionFactory; 30 | $this->_trustpilotLog = $trustpilotLog; 31 | $this->_productFactory = $_productFactory; 32 | } 33 | 34 | public function getInvitation($order, $hook, $collect_product_data = \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA) 35 | { 36 | $invitation = null; 37 | if (!is_null($order)) { 38 | $invitation = array(); 39 | $invitation['recipientEmail'] = trim($this->getEmail($order)); 40 | $invitation['recipientName'] = trim($this->getName($order)); 41 | $invitation['referenceId'] = $order->getRealOrderId(); 42 | $invitation['source'] = 'Magento-' . $this->_helper->getVersion(); 43 | $invitation['pluginVersion'] = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_PLUGIN_VERSION; 44 | $invitation['hook'] = $hook; 45 | $invitation['orderStatusId'] = $order->getState(); 46 | $invitation['orderStatusName'] = $order->getStatus() ? $order->getStatusLabel() : ''; 47 | try { 48 | $invitation['templateParams'] = array((string)$this->getWebsiteId($order), (string)$this->getGroupId($order), (string)$order->getStoreId()); 49 | } catch (\Throwable $e) { 50 | $description = 'Unable to get invitation data'; 51 | $this->_trustpilotLog->error($e, $description, array( 52 | 'hook' => $hook, 53 | 'collect_product_data' => $collect_product_data 54 | )); 55 | } catch (\Exception $e) { 56 | $description = 'Unable to get invitation data'; 57 | $this->_trustpilotLog->error($e, $description, array( 58 | 'hook' => $hook, 59 | 'collect_product_data' => $collect_product_data 60 | )); 61 | } 62 | 63 | if ($collect_product_data == \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA) { 64 | $products = $this->getProducts($order); 65 | $invitation['products'] = $products; 66 | $invitation['productSkus'] = $this->getSkus($products); 67 | } 68 | } 69 | return $invitation; 70 | } 71 | 72 | public function getWebsiteId($order) { 73 | return $this->_storeManager->getStore($order->getStoreId())->getWebsite()->getId(); 74 | } 75 | 76 | public function getGroupId($order) { 77 | return $this->_storeManager->getStore($order->getStoreId())->getGroupId(); 78 | } 79 | 80 | public function getName($order) 81 | { 82 | if ($order->getCustomerIsGuest() == 1) { 83 | return $order->getBillingAddress()->getFirstName() . ' ' . $order->getBillingAddress()->getLastName(); 84 | } else { 85 | return $order->getCustomerName(); 86 | } 87 | } 88 | 89 | public function getEmail($order) 90 | { 91 | if ($this->is_empty($order)) 92 | return ''; 93 | 94 | try { 95 | if (!($this->is_empty($order->getCustomerEmail()))) 96 | return $order->getCustomerEmail(); 97 | } catch(\Throwable $e) { 98 | $description = 'Unable to get customer email from an order'; 99 | $this->_trustpilotLog->error($e, $description); 100 | } catch(\Exception $e) { 101 | $description = 'Unable to get customer email from an order'; 102 | $this->_trustpilotLog->error($e, $description); 103 | } 104 | 105 | try { 106 | if (!($this->is_empty($order->getShippingAddress())) && !($this->is_empty($order->getShippingAddress()->getEmail()))) 107 | return $order->getShippingAddress()->getEmail(); 108 | } catch (\Throwable $e) { 109 | $description = 'Unable to get customer email from a shipping address'; 110 | $this->_trustpilotLog->error($e, $description); 111 | } catch (\Exception $e) { 112 | $description = 'Unable to get customer email from a shipping address'; 113 | $this->_trustpilotLog->error($e, $description); 114 | } 115 | 116 | try { 117 | if (!($this->is_empty($order->getBillingAddress())) && !($this->is_empty($order->getBillingAddress()->getEmail()))) 118 | return $order->getBillingAddress()->getEmail(); 119 | } catch (\Throwable $e) { 120 | $description = 'Unable to get customer email from a billing address'; 121 | $this->_trustpilotLog->error($e, $description); 122 | } catch (\Exception $e) { 123 | $description = 'Unable to get customer email from a billing address'; 124 | $this->_trustpilotLog->error($e, $description); 125 | } 126 | 127 | try { 128 | if (!($this->is_empty($order->getCustomerId())) && !($this->is_empty($order->getCustomerId()))) 129 | return $this->_customer->load($order->getCustomerId())->getEmail(); 130 | } catch (\Throwable $e) { 131 | $description = 'Unable to get customer email from customer data'; 132 | $this->_trustpilotLog->error($e, $description); 133 | } catch (\Exception $e) { 134 | $description = 'Unable to get customer email from customer data'; 135 | $this->_trustpilotLog->error($e, $description); 136 | } 137 | 138 | return ''; 139 | } 140 | 141 | public function getSkus($products) 142 | { 143 | $skus = array(); 144 | foreach ($products as $product) { 145 | array_push($skus, $product['sku']); 146 | } 147 | return $skus; 148 | } 149 | 150 | public function is_empty($var) 151 | { 152 | return empty($var); 153 | } 154 | 155 | public function getProducts($order) 156 | { 157 | $products = array(); 158 | try { 159 | $storeId = $order->getStoreId(); 160 | $settings = json_decode($this->_helper->getConfig('master_settings_field', $storeId, StoreScopeInterface::SCOPE_STORES)); 161 | $skuSelector = $settings->skuSelector; 162 | $gtinSelector = $settings->gtinSelector; 163 | $mpnSelector = $settings->mpnSelector; 164 | 165 | $items = $order->getAllVisibleItems(); 166 | foreach ($items as $item) { 167 | $product = $this->_productFactory->create()->setStoreId($storeId)->load($item->getProductId()); 168 | 169 | $childProducts = array(); 170 | if ($item->getHasChildren() && !($product->getTypeId() == 'bundle')) { 171 | $orderChildItems = $item->getChildrenItems(); 172 | foreach ($orderChildItems as $cpItem) { 173 | array_push($childProducts, $cpItem->getProduct()); 174 | } 175 | } 176 | 177 | $sku = $this->_helper->loadSelector($product, $skuSelector, $childProducts); 178 | $mpn = $this->_helper->loadSelector($product, $mpnSelector, $childProducts); 179 | $gtin = $this->_helper->loadSelector($product, $gtinSelector, $childProducts); 180 | $productId = $this->_helper->loadSelector($product, 'id', $childProducts); 181 | 182 | $productData = array( 183 | 'productId' => $productId, 184 | 'productUrl' => $product->getProductUrl(), 185 | 'name' => $product->getName(), 186 | 'sku' => $sku ? $sku : '', 187 | 'mpn' => $mpn ? $mpn : '', 188 | 'gtin' => $gtin ? $gtin : '', 189 | 'imageUrl' => $this->_storeManager->getStore($storeId)->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA) 190 | . 'catalog/product/' . ltrim($product->getImage(), '/') 191 | ); 192 | 193 | $productData = $this->getProductExtraFields($productData, $product, $childProducts, $order); 194 | 195 | array_push($products, $productData); 196 | } 197 | } catch (\Throwable $e) { 198 | // Just skipping products data if we are not able to collect it 199 | $description = 'Unable to get product data'; 200 | $this->_trustpilotLog->error($e, $description); 201 | } catch (\Exception $e) { 202 | // Just skipping products data if we are not able to collect it 203 | $description = 'Unable to get product data'; 204 | $this->_trustpilotLog->error($e, $description); 205 | } 206 | 207 | return $products; 208 | } 209 | function getOptionText($product, $fieldName, $optionId) { 210 | try { 211 | return $product->getAttributeText($fieldName); 212 | } catch (\Throwable $e) { 213 | return $optionId; 214 | } catch (\Exception $e) { 215 | return $optionId; 216 | } 217 | } 218 | 219 | function getProductExtraFields($productData, $product, $childProducts, $order) { 220 | try { 221 | $manufacturer = $this->_helper->loadSelector($product, 'manufacturer', $childProducts); 222 | $manufacturerValue = $this->getOptionText($product, 'manufacturer', $manufacturer); 223 | 224 | $brandField = $product->getBrand(); 225 | $brandValue = $this->getOptionText($product, 'brand', $brandField); 226 | 227 | return array_merge($productData, array( 228 | 'price' => $product->getFinalPrice(), 229 | 'currency' => $order->getOrderCurrencyCode(), 230 | 'description' => $this->stripAllTags($product->getDescription(), true), 231 | 'meta' => array( 232 | 'title' => $product->getMetaTitle() ? $product->getMetaTitle() : $product->getName(), 233 | 'keywords' => $product->getMetaKeyword() ? $product->getMetaKeyword() : $product->getName(), 234 | 'description' => $product->getMetaDescription() ? 235 | $product->getMetaDescription() : substr($this->stripAllTags($product->getDescription(), true), 0, 255), 236 | ), 237 | 'manufacturer' => $manufacturerValue ? $manufacturerValue : '', 238 | 'categories' => $this->getProductCategories($product, $childProducts), 239 | 'images' => $this->getAllImages($product, $childProducts), 240 | 'videos' => $this->getAllVideos($product, $childProducts), 241 | 'tags' => null, 242 | 'brand' => $brandValue ? $brandValue : ($manufacturerValue ? $manufacturerValue : ''), 243 | )); 244 | } catch (\Throwable $e) { 245 | $description = 'Unable to get product extra fields'; 246 | $this->_trustpilotLog->error($e, $description); 247 | return $productData; 248 | } catch (\Exception $e) { 249 | $description = 'Unable to get product extra fields'; 250 | $this->_trustpilotLog->error($e, $description); 251 | return $productData; 252 | } 253 | } 254 | 255 | function getProductCategories($product, $childProducts = null) { 256 | $categories = array(); 257 | $categoryIds = array(); 258 | 259 | if (!empty($childProducts)) { 260 | foreach ($childProducts as $childProduct) { 261 | $childCategoryIds = $childProduct->getCategoryIds(); 262 | if (!empty($childCategoryIds)) { 263 | $categoryIds = array_merge($categoryIds, $childCategoryIds); 264 | } 265 | } 266 | } else { 267 | $categoryIds = $product->getCategoryIds(); 268 | } 269 | 270 | if (!empty($categoryIds)) { 271 | $catCollection = $this->_categoryCollectionFactory->create(); 272 | $catCollection 273 | ->addAttributeToSelect('*') 274 | ->addAttributeToFilter('entity_id', $categoryIds); 275 | 276 | foreach ($catCollection as $category) { 277 | array_push($categories, $category->getName()); 278 | } 279 | } 280 | return $categories; 281 | } 282 | 283 | function getAllImages($product, $childProducts = null) { 284 | $images = array(); 285 | 286 | if (!empty($childProducts)) { 287 | foreach ($childProducts as $childProduct) { 288 | foreach ($childProduct->getMediaGalleryImages() as $image) { 289 | array_push($images, $image->getUrl()); 290 | } 291 | } 292 | } 293 | 294 | foreach ($product->getMediaGalleryImages() as $image) { 295 | array_push($images, $image->getUrl()); 296 | } 297 | 298 | return $images; 299 | } 300 | 301 | function getAllVideos($product, $childProducts = null) { 302 | $videos = array(); 303 | 304 | if (!empty($childProducts)) { 305 | foreach ($childProducts as $childProduct) { 306 | foreach ($childProduct->getMediaGalleryImages() as $image) { 307 | $imageData = $image->getData(); 308 | if (isset($imageData['media_type']) && $imageData['media_type'] == 'external-video') { 309 | array_push($videos, $imageData['video_url']); 310 | } 311 | 312 | } 313 | } 314 | } 315 | 316 | foreach ($product->getMediaGalleryImages() as $image) { 317 | $imageData = $image->getData(); 318 | if (isset($imageData['media_type']) && $imageData['media_type'] == 'external-video') { 319 | array_push($videos, $imageData['video_url']); 320 | } 321 | } 322 | 323 | return $videos; 324 | } 325 | 326 | function stripAllTags($string, $remove_breaks = false) { 327 | if (gettype($string) != 'string') { 328 | return ''; 329 | } 330 | $string = preg_replace('@<(script|style)[^>]*?>.*?@si', '', $string); 331 | $string = strip_tags($string); 332 | if ($remove_breaks) { 333 | $string = preg_replace('/[\r\n\t ]+/', ' ', $string); 334 | } 335 | return trim($string); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /Helper/PastOrders.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 24 | $this->_trustpilotHttpClient = $trustpilotHttpClient; 25 | $this->_orderData = $orderData; 26 | $this->_orders = $orders; 27 | $this->_trustpilotLog = $trustpilotLog; 28 | } 29 | 30 | public function sync($period_in_days, $scope, $storeId) 31 | { 32 | $this->_helper->setConfig('sync_in_progress', 'true', $scope, $storeId); 33 | $this->_helper->setConfig("show_past_orders_initial", 'false', $scope, $storeId); 34 | try { 35 | $key = $this->_helper->getKey($scope, $storeId); 36 | $collect_product_data = \Trustpilot\Reviews\Model\Config::WITHOUT_PRODUCT_DATA; 37 | if (!is_null($key)) { 38 | $this->_helper->setConfig('past_orders', 0, $scope, $storeId); 39 | $pageId = 1; 40 | $sales_collection = $this->getSalesCollection($period_in_days, $scope, $storeId); 41 | $post_batch = $this->getInvitationsForPeriod($sales_collection, $collect_product_data, $pageId); 42 | while ($post_batch) { 43 | set_time_limit(30); 44 | $batch = null; 45 | if (!is_null($post_batch)) { 46 | $batch['invitations'] = $post_batch; 47 | $batch['type'] = $collect_product_data; 48 | $response = $this->_trustpilotHttpClient->postBatchInvitations($key, $storeId, $batch); 49 | $code = $this->handleTrustpilotResponse($response, $batch, $scope, $storeId); 50 | if ($code == 202) { 51 | $collect_product_data = \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA; 52 | $batch['invitations'] = $this->getInvitationsForPeriod($sales_collection, $collect_product_data, $pageId); 53 | $batch['type'] = $collect_product_data; 54 | $response = $this->_trustpilotHttpClient->postBatchInvitations($key, $storeId, $batch); 55 | $code = $this->handleTrustpilotResponse($response, $batch, $scope, $storeId); 56 | } 57 | if ($code < 200 || $code > 202) { 58 | $this->_helper->setConfig('show_past_orders_initial', 'true', $scope, $storeId); 59 | $this->_helper->setConfig('sync_in_progress', 'false', $scope, $storeId); 60 | $this->_helper->setConfig('past_orders', 0, $scope, $storeId); 61 | $this->_helper->setConfig('failed_orders', '{}', $scope, $storeId); 62 | return; 63 | } 64 | } 65 | $pageId = $pageId + 1; 66 | $post_batch = $this->getInvitationsForPeriod($sales_collection, $collect_product_data, $pageId); 67 | } 68 | } 69 | } catch (\Throwable $e) { 70 | $description = 'Unable to sync past orders'; 71 | $this->_trustpilotLog->error($e, $description); 72 | } catch (\Exception $e) { 73 | $description = 'Unable to sync past orders'; 74 | $this->_trustpilotLog->error($e, $description); 75 | } 76 | $this->_helper->setConfig('sync_in_progress', 'false', $scope, $storeId); 77 | } 78 | 79 | public function resync($scope, $storeId) 80 | { 81 | $this->_helper->setConfig('sync_in_progress', 'true', $scope, $storeId); 82 | try { 83 | $key = $this->_helper->getKey($scope, $storeId); 84 | $failed_orders_object = json_decode($this->_helper->getConfig('failed_orders', $storeId, $scope)); 85 | $collect_product_data = \Trustpilot\Reviews\Model\Config::WITHOUT_PRODUCT_DATA; 86 | if (!is_null($key)) { 87 | $failed_orders_array = array(); 88 | foreach ($failed_orders_object as $id => $value) { 89 | array_push($failed_orders_array, $id); 90 | } 91 | 92 | $chunked_failed_orders = array_chunk($failed_orders_array, 10, true); 93 | foreach ($chunked_failed_orders as $failed_orders_chunk) { 94 | set_time_limit(30); 95 | $post_batch = $this->trustpilotGetOrdersByIds($collect_product_data, $failed_orders_chunk); 96 | $batch = null; 97 | $batch['invitations'] = $post_batch; 98 | $batch['type'] = $collect_product_data; 99 | $response = $this->_trustpilotHttpClient->postBatchInvitations($key, $storeId, $batch); 100 | $code = $this->handleTrustpilotResponse($response, $batch, $scope, $storeId); 101 | 102 | if ($code == 202) { 103 | $collect_product_data = \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA; 104 | $batch['invitations'] = $this->trustpilotGetOrdersByIds($collect_product_data, $failed_orders_chunk); 105 | $batch['type'] = $collect_product_data; 106 | $response = $this->_trustpilotHttpClient->postBatchInvitations($key, $storeId, $batch); 107 | $code = $this->handleTrustpilotResponse($response, $batch, $scope, $storeId); 108 | } 109 | if ($code < 200 || $code > 202) { 110 | $this->_helper->setConfig('sync_in_progress', 'false', $scope, $storeId); 111 | return; 112 | } 113 | } 114 | } 115 | } catch (\Throwable $e) { 116 | $description = 'Unable to resync past orders'; 117 | $this->_trustpilotLog->error($e, $description); 118 | } catch (\Exception $e) { 119 | $description = 'Unable to resync past orders'; 120 | $this->_trustpilotLog->error($e, $description); 121 | } 122 | $this->_helper->setConfig('sync_in_progress', 'false', $scope, $storeId); 123 | } 124 | 125 | private function trustpilotGetOrdersByIds($collect_product_data, $order_ids) { 126 | $invitations = array(); 127 | foreach ($order_ids as $id) { 128 | $order = $this->_orders->loadByIncrementId($id); 129 | $invitation = $this->_orderData->getInvitation($order, 'past-orders', $collect_product_data); 130 | if (!is_null($invitation)) { 131 | array_push($invitations, $invitation); 132 | } 133 | } 134 | 135 | return $invitations; 136 | } 137 | 138 | public function getPastOrdersInfo($scope, $storeId) 139 | { 140 | $syncInProgress = $this->_helper->getConfig('sync_in_progress', $storeId, $scope); 141 | $showInitial = $this->_helper->getConfig('show_past_orders_initial', $storeId, $scope); 142 | if ($syncInProgress === 'false') { 143 | $synced_orders = (int) $this->_helper->getConfig('past_orders', $storeId, $scope); 144 | $failed_orders = json_decode($this->_helper->getConfig('failed_orders', $storeId, $scope)); 145 | 146 | $failed_orders_result = array(); 147 | foreach ($failed_orders as $key => $value) { 148 | $item = array( 149 | 'referenceId' => $key, 150 | 'error' => $value 151 | ); 152 | array_push($failed_orders_result, $item); 153 | } 154 | 155 | return array( 156 | 'pastOrders' => array( 157 | 'synced' => $synced_orders, 158 | 'unsynced' => count($failed_orders_result), 159 | 'failed' => $failed_orders_result, 160 | 'syncInProgress' => $syncInProgress === 'true', 161 | 'showInitial' => $showInitial === 'true', 162 | ) 163 | ); 164 | } else { 165 | return array( 166 | 'pastOrders' => array( 167 | 'syncInProgress' => $syncInProgress === 'true', 168 | 'showInitial' => $showInitial === 'true', 169 | ) 170 | ); 171 | } 172 | } 173 | 174 | private function getSalesCollection($period_in_days, $scope, $storeId) { 175 | $date = new \DateTime(); 176 | $args = array( 177 | 'date_created' => $date->setTimestamp(time() - (86400 * $period_in_days))->format('Y-m-d'), 178 | 'limit' => 20, 179 | 'past_order_statuses' => json_decode($this->_helper->getConfig('master_settings_field', $storeId, $scope))->pastOrderStatuses 180 | ); 181 | 182 | $collection = $this->_orders->getCollection() 183 | ->addAttributeToFilter('state', array('in' => $args['past_order_statuses'])) 184 | ->addAttributeToFilter('created_at', array('gteq' => $args['date_created'])) 185 | ->setPageSize($args['limit']); 186 | 187 | return $collection; 188 | } 189 | 190 | private function getInvitationsForPeriod($sales_collection, $collect_product_data, $page_id) 191 | { 192 | if ($page_id <= $sales_collection->getLastPageNumber()) { 193 | $sales_collection->setCurPage($page_id)->load(); 194 | $orders = array(); 195 | foreach($sales_collection as $order) { 196 | array_push($orders, $this->_orderData->getInvitation($order, 'past-orders', $collect_product_data)); 197 | } 198 | $sales_collection->clear(); 199 | return $orders; 200 | } else { 201 | return null; 202 | } 203 | } 204 | 205 | private function handleTrustpilotResponse($response, $post_batch, $scope, $storeId) 206 | { 207 | $synced_orders = (int) $this->_helper->getConfig('past_orders', $storeId, $scope); 208 | $failed_orders = json_decode($this->_helper->getConfig('failed_orders', $storeId, $scope)); 209 | 210 | $data = array(); 211 | if (isset($response['data'])) 212 | { 213 | $data = $response['data']; 214 | } 215 | 216 | // all succeeded 217 | if ($response['code'] == 201 && count($data) == 0) { 218 | $this->saveSyncedOrders($synced_orders, $post_batch['invitations'], $scope, $storeId); 219 | $this->saveFailedOrders($failed_orders, $post_batch['invitations'], $scope, $storeId); 220 | } 221 | // all/some failed 222 | if ($response['code'] == 201 && count($data) > 0) { 223 | $failed_order_ids = $this->selectColumn($data, 'referenceId'); 224 | $succeeded_orders = array_filter($post_batch['invitations'], function ($invitation) use ($failed_order_ids) { 225 | return !(in_array($invitation['referenceId'], $failed_order_ids)); 226 | }); 227 | 228 | $this->saveSyncedOrders($synced_orders, $succeeded_orders, $scope, $storeId); 229 | $this->saveFailedOrders($failed_orders, $succeeded_orders, $scope, $storeId, $data); 230 | } 231 | return $response['code']; 232 | } 233 | 234 | private function selectColumn($array, $column) 235 | { 236 | if (version_compare(phpversion(), '7.2.10', '<')) { 237 | $newarr = array(); 238 | foreach ($array as $row) { 239 | array_push($newarr, $row->{$column}); 240 | } 241 | return $newarr; 242 | } else { 243 | return array_column($array, $column); 244 | } 245 | } 246 | 247 | private function saveSyncedOrders($synced_orders, $new_orders, $scope, $storeId) 248 | { 249 | if (count($new_orders) > 0) { 250 | $synced_orders = (int)($synced_orders + count($new_orders)); 251 | $this->_helper->setConfig('past_orders', $synced_orders, $scope, $storeId); 252 | } 253 | } 254 | 255 | private function saveFailedOrders($failed_orders, $succeeded_orders, $scope, $storeId, $new_failed_orders = array()) 256 | { 257 | $update_needed = false; 258 | if (count($succeeded_orders) > 0) { 259 | $update_needed = true; 260 | foreach ($succeeded_orders as $order) { 261 | if (isset($failed_orders->{$order['referenceId']})) { 262 | unset($failed_orders->{$order['referenceId']}); 263 | } 264 | } 265 | } 266 | 267 | if (count($new_failed_orders) > 0) { 268 | $update_needed = true; 269 | foreach ($new_failed_orders as $failed_order) { 270 | $failed_orders->{$failed_order->referenceId} = base64_encode($failed_order->error); 271 | } 272 | } 273 | 274 | if ($update_needed) { 275 | $this->_helper->setConfig('failed_orders', json_encode($failed_orders), $scope, $storeId); 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /Helper/Products.php: -------------------------------------------------------------------------------- 1 | _product = $product; 24 | $this->_helper = $helper; 25 | $this->_linkManagement = $linkManagement; 26 | $this->_backendHelper = $backendHelper; 27 | } 28 | 29 | public function checkSkus($skuSelector) { 30 | $data = array(); 31 | $page_id = 1; 32 | $productCollection = $this->_product 33 | ->getCollection() 34 | ->addAttributeToSelect(array('name', $skuSelector)) 35 | ->setPageSize(20); 36 | $lastPage = $productCollection->getLastPageNumber(); 37 | while ($page_id <= $lastPage) { 38 | set_time_limit(30); 39 | $collection = $productCollection->setCurPage($page_id)->load(); 40 | if (isset($collection)) { 41 | foreach ($collection as $product) { 42 | $sku = $this->_helper->loadSelector($product, $skuSelector); 43 | 44 | if (empty($sku)) { 45 | $item = array(); 46 | $item['id'] = $product->getId(); 47 | $item['name'] = $product->getName(); 48 | $item['productAdminUrl'] = $this->_backendHelper->getUrl('catalog/product/edit', array('id' => $product->getId())); 49 | $item['productFrontendUrl'] = $product->getProductUrl(); 50 | array_push($data, $item); 51 | } 52 | } 53 | } 54 | $collection->clear(); 55 | $page_id = $page_id + 1; 56 | } 57 | return $data; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Helper/TrustpilotHttpClient.php: -------------------------------------------------------------------------------- 1 | _pluginStatus = $pluginStatus; 24 | $this->_httpClient = $httpClient; 25 | $this->_storeManager = $storeManager; 26 | $this->_apiUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_API_URL; 27 | } 28 | 29 | public function post($url, $origin, $data, $storeId) 30 | { 31 | $httpRequest = 'POST'; 32 | $response = $this->_httpClient->request( 33 | $url, 34 | $httpRequest, 35 | $origin, 36 | $data 37 | ); 38 | if ($response['code'] > 250 && $response['code'] < 254) { 39 | $this->_pluginStatus->setPluginStatus($response, $storeId); 40 | } 41 | return $response; 42 | } 43 | 44 | public function buildUrl($key, $endpoint) 45 | { 46 | return $this->_apiUrl . $key . $endpoint; 47 | } 48 | 49 | public function checkStatusAndPost($url, $origin, $data, $storeId) 50 | { 51 | $code = $this->_pluginStatus->checkPluginStatus($origin, $storeId); 52 | if ($code > 250 && $code < 254) { 53 | return array( 54 | 'code' => $code, 55 | ); 56 | } 57 | return $this->post($url, $origin, $data, $storeId); 58 | } 59 | 60 | public function postInvitation($key, $storeId, $data = array()) 61 | { 62 | $origin = $this->_storeManager->getStore($storeId)->getBaseUrl(UrlInterface::URL_TYPE_WEB); 63 | return $this->checkStatusAndPost($this->buildUrl($key, '/invitation'), $origin, $data, $storeId); 64 | } 65 | 66 | public function postSettings($key, $data) 67 | { 68 | $origin = $this->_storeManager->getStore()->getBaseUrl(UrlInterface::URL_TYPE_WEB); 69 | return $this->post($this->buildUrl($key, '/settings'), $origin, $data, $storeId); 70 | } 71 | 72 | public function postBatchInvitations($key, $storeId, $data = array()) 73 | { 74 | $origin = $this->_storeManager->getStore($storeId)->getBaseUrl(UrlInterface::URL_TYPE_WEB); 75 | return $this->checkStatusAndPost($this->buildUrl($key, '/batchinvitations'), $origin, $data, $storeId); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Helper/TrustpilotLog.php: -------------------------------------------------------------------------------- 1 | _logger = $logger; 23 | $this->_httpClient = $httpClient; 24 | $this->_storeManager = $storeManager; 25 | $this->_apiUrl = \Trustpilot\Reviews\Model\Config::TRUSTPILOT_API_URL; 26 | } 27 | 28 | public function error($e, $description, $optional = array()) { 29 | $errorObject = array( 30 | 'error' => $e->getMessage(), 31 | 'description' => $description, 32 | 'platform' => 'Magento2', 33 | 'version' => Config::TRUSTPILOT_PLUGIN_VERSION, 34 | 'method' => $this->getMethodName($e), 35 | 'trace' => $e->getTraceAsString(), 36 | 'variables' => $optional 37 | ); 38 | 39 | $storeId = in_array('storeId', $optional) ? $optional['storeId'] : false; 40 | $this->postLog($errorObject, $storeId); 41 | 42 | // Don't log stack trace locally 43 | unset($errorObject['trace']); 44 | // Logs to var/log/system.log 45 | $this->_logger->error(json_encode($errorObject)); 46 | } 47 | 48 | private function getMethodName($e) { 49 | $trace = $e->getTrace(); 50 | if (array_key_exists(0, $trace)) { 51 | $firstNode = $trace[0]; 52 | if (array_key_exists('function', $firstNode)) { 53 | return $firstNode['function']; 54 | } 55 | } 56 | return ''; 57 | } 58 | 59 | private function postLog($data, $storeId = null) 60 | { 61 | try { 62 | $origin = $storeId ? $this->_storeManager->getStore($storeId)->getBaseUrl(UrlInterface::URL_TYPE_WEB) : ''; 63 | return $this->_httpClient->request( 64 | $this->_apiUrl . 'log', 65 | 'POST', 66 | $origin, 67 | $data 68 | ); 69 | } catch (\Exception $e) { 70 | return false; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Helper/TrustpilotPluginStatus.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 16 | } 17 | 18 | public function setPluginStatus($response, $storeId) 19 | { 20 | $data = json_encode( 21 | array( 22 | 'pluginStatus' => $response['code'], 23 | 'blockedDomains' => isset($response['data']) ? $response['data'] : array(), 24 | ) 25 | ); 26 | $this->_helper->setConfig('plugin_status', $data, 'stores', $storeId); 27 | } 28 | public function checkPluginStatus($origin, $storeId) 29 | { 30 | $data = json_decode($this->_helper->getConfig('plugin_status', $storeId, 'stores')); 31 | if (in_array(parse_url($origin, PHP_URL_HOST), $data->blockedDomains)) { 32 | return $data->pluginStatus; 33 | } 34 | return self::TRUSTPILOT_SUCCESSFUL_STATUS; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Trustpilot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Model/Config.php: -------------------------------------------------------------------------------- 1 | _helper = $helper; 32 | $this->_trustpilotHttpClient = $trustpilotHttpClient; 33 | $this->_orderData = $orderData; 34 | $this->_config = $config; 35 | $this->_trustpilotLog = $trustpilotLog; 36 | } 37 | 38 | public function execute(EventObserver $observer) 39 | { 40 | $event = $observer->getEvent(); 41 | $order = $event->getOrder(); 42 | $orderStatus = $order->getState(); 43 | $storeId = $order->getStoreId(); 44 | 45 | $settings = json_decode($this->_helper->getConfig('master_settings_field', $storeId, StoreScopeInterface::SCOPE_STORES)); 46 | $key = $settings->general->key; 47 | 48 | try { 49 | if (isset($key) && $order->getState() != $order->getOrigData('state')) { 50 | $data = $this->_orderData->getInvitation($order, 'sales_order_save_after', \Trustpilot\Reviews\Model\Config::WITHOUT_PRODUCT_DATA); 51 | 52 | if (in_array($orderStatus, $settings->general->mappedInvitationTrigger)) { 53 | $response = $this->_trustpilotHttpClient->postInvitation($key, $storeId, $data); 54 | 55 | if ($response['code'] == __ACCEPTED__) { 56 | $data = $this->_orderData->getInvitation($order, 'sales_order_save_after', \Trustpilot\Reviews\Model\Config::WITH_PRODUCT_DATA); 57 | $response = $this->_trustpilotHttpClient->postInvitation($key, $storeId, $data); 58 | } 59 | $this->handleSingleResponse($response, $data, $storeId); 60 | } else { 61 | $data['payloadType'] = 'OrderStatusUpdate'; 62 | $this->_trustpilotHttpClient->postInvitation($key, $storeId, $data); 63 | } 64 | } 65 | } catch (\Throwable $e) { 66 | $description = 'Unable to get invitation data in OrderSaveObserver'; 67 | $vars = array( 68 | 'storeId' => isset($storeId) ? $storeId : null, 69 | 'orderStatus' => isset($orderStatus) ? $orderStatus : null, 70 | 'key' => isset($key) ? $key : null, 71 | ); 72 | $this->_trustpilotLog->error($e, $description, $vars); 73 | } catch (\Exception $e) { 74 | $description = 'Unable to get invitation data in OrderSaveObserver'; 75 | $vars = array( 76 | 'storeId' => isset($storeId) ? $storeId : null, 77 | 'orderStatus' => isset($orderStatus) ? $orderStatus : null, 78 | 'key' => isset($key) ? $key : null, 79 | ); 80 | $this->_trustpilotLog->error($e, $description, $vars); 81 | } 82 | } 83 | 84 | public function handleSingleResponse($response, $order, $storeId) 85 | { 86 | try { 87 | $scope = StoreScopeInterface::SCOPE_STORES; 88 | $synced_orders = (int) $this->_helper->getConfig('past_orders', $storeId, $scope); 89 | $failed_orders = json_decode($this->_helper->getConfig('failed_orders', $storeId, $scope)); 90 | 91 | if ($response['code'] == 201) { 92 | if (isset($failed_orders->{$order['referenceId']})) { 93 | unset($failed_orders->{$order['referenceId']}); 94 | $this->saveConfig('failed_orders', json_encode($failed_orders), $scope, $storeId); 95 | } 96 | } else { 97 | $failed_orders->{$order['referenceId']} = base64_encode('Automatic invitation sending failed'); 98 | $this->saveConfig('failed_orders', json_encode($failed_orders), $scope, $storeId); 99 | } 100 | } catch (\Throwable $e) { 101 | $description = 'Unable to handle response from invitations API'; 102 | $this->_trustpilotLog->error($e, $description, array('storeId' => $storeId)); 103 | } catch (\Exception $e) { 104 | $description = 'Unable to handle response from invitations API'; 105 | $this->_trustpilotLog->error($e, $description, array('storeId' => $storeId)); 106 | } 107 | } 108 | 109 | private function saveConfig($config, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0) 110 | { 111 | $path = 'trustpilot/trustpilot_general_group/'; 112 | 113 | if ($scope === 'store') { 114 | $scope = 'stores'; 115 | } elseif ($scope === 'website') { 116 | $scope = 'websites'; 117 | } 118 | 119 | $this->_config->saveConfig($path . $config, $value, $scope, $scopeId); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The official Trustpilot extension for Magento 2 2 | 3 | 4 | Trustpilot is an open review platform that helps consumers make better choices while helping companies showcase and improve their customer service. 5 | 6 | To install the Trustpilot plugin on your website, please follow the steps provided in this package. 7 | 8 | ## How to install the Trustpilot extension 9 | 10 | 1. Log in to your Magento server using SSH (Secure Shell) and run the commands that follow. 11 | 2. Create a system and database backup by navigating to the root directory of your Magento installation and execute this command:
php bin/magento setup:backup --code --db --media
(Please note that your website will be inaccessible during the backup process.) 12 | 3. Enable maintenance mode.
php bin/magento maintenance:enable
13 | 4. Download and install the Trustpilot plugin using Composer.
composer require “trustpilot/module-reviews”
14 | 5. If this is the first time you install a plugin using Composer, Magento will ask you to provide your Magento Marketplace account credentials. To find your account information go to __https://marketplace.magento.com > My profile > Access Keys > Create A New Access Key.__ Note: Your __public key__ is your username, while your __private key__ is your password. 15 | 6. Enable the Trustpilot plugin.
php bin/magento module:enable Trustpilot_Reviews --clear-static-content
16 | 7. Update the database schema. (Please proceed cautiously: This command is global and will enable all Magento plugins that you’ ve installed.)
php bin/magento setup:upgrade
17 | 8. Compile (This command is only required in production mode.)
php bin/magento setup:di:compile
18 | 9. Deploy static content (This command is only required in production mode.)
php bin/magento setup:static-content:deploy
19 | 10. Disable maintenance mode.
php bin/magento maintenance:disable
20 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "trustpilot/module-reviews", 3 | "description": "The Trustpilot Review extension makes it simple and easy for merchants to collect reviews from their customers to power their marketing efforts, increase sales conversion, build their online reputation and draw business insights.", 4 | "type": "magento2-module", 5 | "version": "2.6.580", 6 | "license": [ 7 | "OSL-3.0" 8 | ], 9 | "require": { 10 | "magento/framework": ">=100.0.20", 11 | "magento/module-sales": ">=100.0.16", 12 | "magento/module-store": ">=100.0.9", 13 | "magento/module-checkout": ">=100.0.16", 14 | "magento/module-configurable-product": ">=100.0.12", 15 | "magento/module-catalog": ">=100.0.17", 16 | "magento/module-eav": ">=100.0.12", 17 | "magento/module-admin-notification": ">=100.0.7", 18 | "magento/module-backend": ">=100.0.12", 19 | "psr/log": ">=1.0.0" 20 | }, 21 | "autoload": { 22 | "files": [ "registration.php" ], 23 | "psr-4": { 24 | "Trustpilot\\Reviews\\": "" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | separator-top 10 | 11 | trustpilot 12 | Trustpilot_Reviews::configuration 13 | 14 | 1 15 | 16 | Trustpilot\Reviews\Block\System\Config\Message 17 | 18 | 19 | Trustpilot\Reviews\Block\System\Config\Admin 20 | 21 | 22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /etc/csp_whitelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | *.trustpilot.com 7 | 8 | 9 | 10 | 11 | *.trustpilot.com 12 | 13 | 14 | 15 | 16 | *.trustpilot.com 17 | 18 | 19 | 20 | 21 | *.gstatic.com 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /etc/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 27 |
28 | 49 | 59 |
60 | 61 | 64 | -------------------------------------------------------------------------------- /view/adminhtml/templates/system/config/message.phtml: -------------------------------------------------------------------------------- 1 |
2 | Important:

To set up Trustpilot integration change the current configuration scope to Website or Store View.

3 |
-------------------------------------------------------------------------------- /view/adminhtml/web/css/trustpilot-message.css: -------------------------------------------------------------------------------- 1 | .shake-and-hide-element { 2 | -moz-animation: hide-element 0s ease-in 10s forwards, shake-element 1s; 3 | /* Firefox */ 4 | -webkit-animation: hide-element 0s ease-in 10s forwards, shake-element 1s; 5 | /* Safari and Chrome */ 6 | -o-animation: hide-element 0s ease-in 10s forwards, shake-element 1s; 7 | /* Opera */ 8 | animation: hide-element 0s ease-in 10s forwards, shake-element 1s; 9 | -webkit-animation-fill-mode: forwards; 10 | animation-fill-mode: forwards; 11 | } 12 | 13 | .shake-element { 14 | -moz-animation: shake-element 1s; 15 | /* Firefox */ 16 | -webkit-animation: shake-element 1s; 17 | /* Safari and Chrome */ 18 | -o-animation: shake-element 1s; 19 | /* Opera */ 20 | animation: shake-element 1s; 21 | -webkit-animation-fill-mode: forwards; 22 | animation-fill-mode: forwards; 23 | } 24 | 25 | @keyframes shake-element { 26 | 0%, 100% {transform: translateX(0);} 27 | 10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 28 | 20%, 40%, 60%, 80% {transform: translateX(5px);} 29 | } 30 | 31 | @-webkit-keyframes shake-element { 32 | 0%, 100% {transform: translateX(0);} 33 | 10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 34 | 20%, 40%, 60%, 80% {transform: translateX(5px);} 35 | } 36 | 37 | @keyframes hide-element { 38 | to { 39 | width: 0; 40 | height: 0; 41 | padding: 0px; 42 | margin: 0px; 43 | overflow: hidden; 44 | } 45 | } 46 | 47 | @-webkit-keyframes hide-element { 48 | to { 49 | width: 0; 50 | height: 0; 51 | padding: 0px; 52 | margin: 0px; 53 | visibility: hidden; 54 | } 55 | } 56 | 57 | .warning-icon{ 58 | width: 20%; 59 | max-width: 30px; 60 | font-size: 21px; 61 | display: inline-block; 62 | vertical-align: middle; 63 | } 64 | 65 | .trustbox-message-text { 66 | width: 80%; 67 | display: inline-block; 68 | vertical-align: middle; 69 | } 70 | 71 | .trustbox-message-box { 72 | background-color: #fcf8e3; 73 | color: #8a6d3b; 74 | padding: 15px; 75 | margin-bottom: 20px; 76 | border-radius: 4px; 77 | border: 1px solid; 78 | border-color: #faebcc; 79 | display: inline-block; 80 | } 81 | -------------------------------------------------------------------------------- /view/adminhtml/web/css/trustpilot-message.min.css: -------------------------------------------------------------------------------- 1 | .shake-and-hide-element{-moz-animation:hide-element 0s ease-in 10s forwards,shake-element 1s;-webkit-animation:hide-element 0s ease-in 10s forwards,shake-element 1s;-o-animation:hide-element 0s ease-in 10s forwards,shake-element 1s;animation:hide-element 0s ease-in 10s forwards,shake-element 1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.shake-element{-moz-animation:shake-element 1s;-webkit-animation:shake-element 1s;-o-animation:shake-element 1s;animation:shake-element 1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards} 2 | @keyframes shake-element{0%,100%{transform:translateX(0)}10%,30%,50%,70%,90%{transform:translateX(-5px)}20%,40%,60%,80%{transform:translateX(5px)}}@-webkit-keyframes shake-element{0%,100%{transform:translateX(0)}10%,30%,50%,70%,90%{transform:translateX(-5px)}20%,40%,60%,80%{transform:translateX(5px)}}@keyframes hide-element{to{width:0;height:0;padding:0;margin:0;overflow:hidden}}@-webkit-keyframes hide-element{to{width:0;height:0;padding:0;margin:0;visibility:hidden} 3 | }.warning-icon{width:20%;max-width:30px;font-size:21px;display:inline-block;vertical-align:middle}.trustbox-message-text{width:80%;display:inline-block;vertical-align:middle}.trustbox-message-box{background-color:#fcf8e3;color:#8a6d3b;padding:15px;margin-bottom:20px;border-radius:4px;border:1px solid;border-color:#faebcc;display:inline-block} -------------------------------------------------------------------------------- /view/adminhtml/web/css/trustpilot.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family:'Trustpilot'; 3 | src:url('../fonts/trustpilot.eot'); 4 | src:url('../fonts/trustpilot.eot?#iefix') format('embedded-opentype'),url('../fonts/trustpilot.woff') format('woff'),url('../fonts/trustpilot.ttf') format('truetype'),url('../fonts/trustpilot.svg') format('svg');font-weight:normal;font-style:normal 5 | } 6 | 7 | .admin__menu .level-0.item-trustpilot > a::before { 8 | content: '\e900'; 9 | font-size: 2.6rem; 10 | position: relative; 11 | top: -0.7rem; 12 | font-family: 'Trustpilot'; 13 | } 14 | 15 | .trustpilot_reviews-trustpilot-index .page-title { 16 | display:none; 17 | } 18 | 19 | #row_trustpilotGeneral_general_trustpilot_admin .label { 20 | display: none; 21 | } 22 | 23 | #row_trustpilotGeneral_general_trustpilot_admin .value { 24 | width: 100%; 25 | } 26 | 27 | #trustpilotGeneral_general-head { 28 | display:none; 29 | } 30 | 31 | #preview-container { 32 | position: relative; 33 | } 34 | 35 | .load-spinner { 36 | height:20px; 37 | width:110px; 38 | position: absolute; 39 | top: 0; 40 | right: 0; 41 | bottom: 0; 42 | left: 0; 43 | margin: auto; 44 | z-index: 9999; 45 | } 46 | 47 | .one { animation: spinner-one-animation .9s infinite; } 48 | .two { animation: spinner-two-animation .9s infinite; } 49 | .three { animation: spinner-three-animation .9s infinite; } 50 | .four { animation: spinner-four-animation .9s infinite; } 51 | .five { animation: spinner-five-animation .9s infinite; } 52 | 53 | .one, .two, .three, .four, .five { 54 | will-change: opacity; 55 | position: relative; 56 | float: left; 57 | height: 100%; 58 | width: 18%; 59 | margin-left: 2%; 60 | background-image: url("//cdn.trustpilot.net/brand-assets/1.0.3/single-star-5.svg"); 61 | background-color: #13b57a; 62 | background-position: center center; 63 | background-size: 110% 110%; 64 | background-repeat: no-repeat; 65 | border-radius: 10%; 66 | transition: opacity, 0.3s ease-in-out; 67 | opacity: 0; 68 | } 69 | 70 | @keyframes spinner-one-animation { 71 | 0% { opacity: 1; } 72 | 12% { opacity: 1; } 73 | 25% { opacity: 1; } 74 | 36% { opacity: 1; } 75 | 49% { opacity: 0; } 76 | 61% { opacity: 0; } 77 | 74% { opacity: 0; } 78 | 86% { opacity: 0; } 79 | 99% { opacity: 0; } 80 | } 81 | 82 | @keyframes spinner-two-animation { 83 | 0% { opacity: 0; } 84 | 12% { opacity: 1; } 85 | 25% { opacity: 1; } 86 | 36% { opacity: 1; } 87 | 49% { opacity: 1; } 88 | 61% { opacity: 0; } 89 | 74% { opacity: 0; } 90 | 86% { opacity: 0; } 91 | 99% { opacity: 0; } 92 | } 93 | 94 | @keyframes spinner-three-animation { 95 | 0% { opacity: 0; } 96 | 12% { opacity: 0; } 97 | 25% { opacity: 1; } 98 | 36% { opacity: 1; } 99 | 49% { opacity: 1; } 100 | 61% { opacity: 1; } 101 | 74% { opacity: 0; } 102 | 86% { opacity: 0; } 103 | 99% { opacity: 0; } 104 | } 105 | 106 | @keyframes spinner-four-animation { 107 | 0% { opacity: 0; } 108 | 12% { opacity: 0; } 109 | 25% { opacity: 0; } 110 | 36% { opacity: 1; } 111 | 49% { opacity: 1; } 112 | 61% { opacity: 1; } 113 | 74% { opacity: 1; } 114 | 86% { opacity: 0; } 115 | 99% { opacity: 0; } 116 | } 117 | 118 | @keyframes spinner-five-animation { 119 | 0% { opacity: 0; } 120 | 12% { opacity: 0; } 121 | 25% { opacity: 0; } 122 | 36% { opacity: 0; } 123 | 49% { opacity: 1; } 124 | 61% { opacity: 1; } 125 | 74% { opacity: 1; } 126 | 86% { opacity: 1; } 127 | 99% { opacity: 0; } 128 | } 129 | 130 | .ls-backdrop { 131 | position: absolute; 132 | top: 0; 133 | right: 0; 134 | bottom: 0; 135 | left: 0; 136 | z-index: 9999; 137 | background-color: rgba(0,0,0,0.4); 138 | } -------------------------------------------------------------------------------- /view/adminhtml/web/css/trustpilot.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:'Trustpilot';src:url('../fonts/trustpilot.eot');src:url('../fonts/trustpilot.eot?#iefix') format('embedded-opentype'),url('../fonts/trustpilot.woff') format('woff'),url('../fonts/trustpilot.ttf') format('truetype'),url('../fonts/trustpilot.svg') format('svg');font-weight:normal;font-style:normal}.admin__menu .level-0.item-trustpilot>a::before{content:'\e900';font-size:2.6rem;position:relative;top:-0.7rem;font-family:'Trustpilot'} 2 | .trustpilot_reviews-trustpilot-index .page-title{display:none}#row_trustpilotGeneral_general_trustpilot_admin .label{display:none}#row_trustpilotGeneral_general_trustpilot_admin .value{width:100%}#trustpilotGeneral_general-head{display:none}#preview-container{position:relative}.load-spinner{height:20px;width:110px;position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;z-index:9999}.one{animation:spinner-one-animation .9s infinite}.two{animation:spinner-two-animation .9s infinite}.three{animation:spinner-three-animation .9s infinite} 3 | .four{animation:spinner-four-animation .9s infinite}.five{animation:spinner-five-animation .9s infinite}.one,.two,.three,.four,.five{will-change:opacity;position:relative;float:left;height:100%;width:18%;margin-left:2%;background-image:url("//cdn.trustpilot.net/brand-assets/1.0.3/single-star-5.svg");background-color:#13b57a;background-position:center center;background-size:110% 110%;background-repeat:no-repeat;border-radius:10%;transition:opacity,0.3s ease-in-out;opacity:0}@keyframes spinner-one-animation{0%{opacity:1} 4 | 12%{opacity:1}25%{opacity:1}36%{opacity:1}49%{opacity:0}61%{opacity:0}74%{opacity:0}86%{opacity:0}99%{opacity:0}}@keyframes spinner-two-animation{0%{opacity:0}12%{opacity:1}25%{opacity:1}36%{opacity:1}49%{opacity:1}61%{opacity:0}74%{opacity:0}86%{opacity:0}99%{opacity:0}}@keyframes spinner-three-animation{0%{opacity:0}12%{opacity:0}25%{opacity:1}36%{opacity:1}49%{opacity:1}61%{opacity:1}74%{opacity:0}86%{opacity:0}99%{opacity:0}}@keyframes spinner-four-animation{0%{opacity:0} 5 | 12%{opacity:0}25%{opacity:0}36%{opacity:1}49%{opacity:1}61%{opacity:1}74%{opacity:1}86%{opacity:0}99%{opacity:0}}@keyframes spinner-five-animation{0%{opacity:0}12%{opacity:0}25%{opacity:0}36%{opacity:0}49%{opacity:1}61%{opacity:1}74%{opacity:1}86%{opacity:1}99%{opacity:0}}.ls-backdrop{position:absolute;top:0;right:0;bottom:0;left:0;z-index:9999;background-color:rgba(0,0,0,0.4)} -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/trustpilot.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustpilot/plugin-magento2/2bd44fb95e4d6839425b62c0107ae7308d08355c/view/adminhtml/web/fonts/trustpilot.eot -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/trustpilot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/trustpilot.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustpilot/plugin-magento2/2bd44fb95e4d6839425b62c0107ae7308d08355c/view/adminhtml/web/fonts/trustpilot.ttf -------------------------------------------------------------------------------- /view/adminhtml/web/fonts/trustpilot.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trustpilot/plugin-magento2/2bd44fb95e4d6839425b62c0107ae7308d08355c/view/adminhtml/web/fonts/trustpilot.woff -------------------------------------------------------------------------------- /view/adminhtml/web/js/admin.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("message", this.receiveSettings); 2 | 3 | function receiveSettings(e) { 4 | if (e.origin === location.origin){ 5 | return receiveInternalData(e); 6 | } 7 | const iframe = document.getElementById('configuration_iframe'); 8 | const attrs = iframe.dataset; 9 | if (e.origin !== attrs.transfer) { 10 | return; 11 | } 12 | const data = e.data; 13 | 14 | if (typeof data !== 'string') { 15 | return; 16 | } 17 | 18 | if (data.startsWith('sync:') || data.startsWith('showPastOrdersInitial:')) { 19 | const split = data.split(':'); 20 | const action = {}; 21 | action['action'] = 'handle_past_orders'; 22 | action[split[0]] = split[1]; 23 | this.submitPastOrdersCommand(action); 24 | } else if (data.startsWith('resync')) { 25 | const action = {}; 26 | action['action'] = 'handle_past_orders'; 27 | action['resync'] = 'resync'; 28 | this.submitPastOrdersCommand(action); 29 | } else if (data.startsWith('issynced')) { 30 | const action = {}; 31 | action['action'] = 'handle_past_orders'; 32 | action['issynced'] = 'issynced'; 33 | this.submitPastOrdersCommand(action); 34 | } else if (data.startsWith('check_product_skus')) { 35 | const split = data.split(':'); 36 | const action = {}; 37 | action['action'] = 'check_product_skus'; 38 | action['skuSelector'] = split[1]; 39 | this.submitCheckProductSkusCommand(action); 40 | } else if (data === 'signup_data') { 41 | this.sendSignupData(); 42 | } else if (data === 'update') { 43 | this.updateplugin(); 44 | } else if (data === 'reload') { 45 | this.reloadSettings(); 46 | } else { 47 | this.handleJSONMessage(data); 48 | } 49 | } 50 | 51 | function handleJSONMessage(data) { 52 | const parsedData = {}; 53 | if (tryParseJson(data, parsedData)) { 54 | if (parsedData.TrustBoxPreviewMode) { 55 | this.trustBoxPreviewMode(parsedData); 56 | } else if (parsedData.window) { 57 | this.updateIframeSize(parsedData); 58 | } else if (parsedData.type === 'submit') { 59 | this.submitSettings(parsedData); 60 | } else if (parsedData.trustbox) { 61 | const iframe = document.getElementById('trustbox_preview_frame'); 62 | iframe.contentWindow.postMessage(JSON.stringify(parsedData.trustbox), "*"); 63 | } 64 | } 65 | } 66 | 67 | function trustBoxPreviewMode(settings) { 68 | const div = document.getElementById('trustpilot-trustbox-preview'); 69 | if (settings.TrustBoxPreviewMode.enable) { 70 | div.hidden = false; 71 | } else { 72 | div.hidden = true; 73 | } 74 | } 75 | 76 | function receiveInternalData(e) { 77 | const data = e.data; 78 | const parsedData = {}; 79 | if (data && typeof data === 'string' && tryParseJson(data, parsedData)) { 80 | if (parsedData && parsedData.type === 'loadCategoryProductInfo') { 81 | requestCategoryInfo(); 82 | } 83 | if (parsedData.type === 'updatePageUrls' || parsedData.type === 'newTrustBox') { 84 | this.submitSettings(parsedData); 85 | } 86 | } 87 | } 88 | 89 | function requestCategoryInfo() { 90 | // TODO: It brake's existing category list page filtering therefore commented until solution will be found 91 | // const data = { 92 | // action: 'get_category_product_info', 93 | // form_key: window.FORM_KEY, 94 | // scope, scopeId, 95 | // }; 96 | 97 | // if (typeof websiteId !== 'undefined') { 98 | // data.website_id = websiteId; 99 | // } 100 | // if (typeof storeId !== 'undefined') { 101 | // data.store_id = storeId; 102 | // } 103 | 104 | // const xhr = new XMLHttpRequest(); 105 | // xhr.onreadystatechange = function() { 106 | // if (xhr.readyState === 4) { 107 | // if (xhr.status >= 400) { 108 | // console.log(`callback error: ${xhr.response} ${xhr.status}`); 109 | // } else { 110 | // window.postMessage(JSON.stringify(xhr.response), window.origin); 111 | // } 112 | // } 113 | // } 114 | // xhr.open('POST', `${ajaxUrl}?isAjax=true`, true); 115 | // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 116 | // xhr.send(encodeSettings(data)); 117 | } 118 | 119 | function submitPastOrdersCommand(data) { 120 | data['form_key'] = window.FORM_KEY; 121 | data['scope'] = scope; 122 | data['scopeId'] = scopeId; 123 | const xhr = new XMLHttpRequest(); 124 | xhr.open('POST', `${ajaxUrl}?isAjax=true`, true); 125 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 126 | xhr.onreadystatechange = function () { 127 | if (xhr.readyState === 4) { 128 | if (xhr.status >= 400) { 129 | console.log(`callback error: ${xhr.response} ${xhr.status}`); 130 | } else { 131 | sendPastOrdersInfo(xhr.response); 132 | } 133 | } 134 | }; 135 | xhr.send(encodeSettings(data)); 136 | } 137 | 138 | function submitCheckProductSkusCommand(data) { 139 | data['form_key'] = window.FORM_KEY; 140 | data['scope'] = scope; 141 | data['scopeId'] = scopeId; 142 | const xhr = new XMLHttpRequest(); 143 | xhr.open('POST', `${ajaxUrl}?isAjax=true`, true); 144 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 145 | xhr.onreadystatechange = function () { 146 | if (xhr.readyState === 4) { 147 | if (xhr.status >= 400) { 148 | console.log(`callback error: ${xhr.response} ${xhr.status}`); 149 | } else { 150 | const iframe = document.getElementById('configuration_iframe'); 151 | iframe.contentWindow.postMessage(xhr.response, iframe.dataset.transfer); 152 | } 153 | } 154 | }; 155 | xhr.send(encodeSettings(data)); 156 | } 157 | 158 | function submitSettings(parsedData) { 159 | const data = { 160 | action: 'handle_save_changes', 161 | form_key: window.FORM_KEY, 162 | scope, scopeId 163 | }; 164 | 165 | if (parsedData.type === 'updatePageUrls') { 166 | data.pageUrls = encodeURIComponent(JSON.stringify(parsedData.pageUrls)); 167 | } else if (parsedData.type === 'newTrustBox') { 168 | data.customTrustBoxes = encodeURIComponent(JSON.stringify(parsedData)); 169 | } else { 170 | data.settings = encodeURIComponent(JSON.stringify(parsedData.settings)); 171 | const frame = document.getElementById('trustbox_preview_frame'); 172 | if (frame) { 173 | frame.dataset.settings = btoa(encodeURIComponent(JSON.stringify(parsedData.settings))); 174 | } else { 175 | console.log('trustbox_preview_frame is missing. Skipping...'); 176 | } 177 | } 178 | 179 | if (typeof websiteId !== 'undefined') { 180 | data.website_id = websiteId; 181 | } 182 | if (typeof storeId !== 'undefined') { 183 | data.store_id = storeId; 184 | } 185 | 186 | const xhr = new XMLHttpRequest(); 187 | xhr.open('POST', `${ajaxUrl}?isAjax=true`); 188 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 189 | xhr.send(encodeSettings(data)); 190 | } 191 | 192 | function encodeSettings(settings) { 193 | let encodedString = ''; 194 | for (const setting in settings) { 195 | encodedString += `${setting}=${settings[setting]}&` 196 | } 197 | return encodedString.substring(0, encodedString.length - 1); 198 | } 199 | 200 | function sendSettings() { 201 | const iframe = document.getElementById('configuration_iframe'); 202 | 203 | const attrs = iframe.dataset; 204 | const settings = JSON.parse(atob(attrs.settings)); 205 | 206 | if (!settings.trustbox) { 207 | settings.trustbox = {} 208 | } 209 | 210 | settings.trustbox.pageUrls = JSON.parse(atob(attrs.pageUrls)); 211 | settings.pluginVersion = attrs.pluginVersion; 212 | settings.source = attrs.source; 213 | settings.version = attrs.version; 214 | settings.basis = 'plugin'; 215 | settings.productIdentificationOptions = JSON.parse(attrs.productIdentificationOptions); 216 | settings.configurationScopeTree = JSON.parse(atob(attrs.configurationScopeTree)); 217 | settings.pluginStatus = JSON.parse(atob(attrs.pluginStatus)); 218 | settings.isFromMarketplace = attrs.isFromMarketplace; 219 | 220 | if (settings.trustbox.trustboxes && attrs.sku) { 221 | for (trustbox of settings.trustbox.trustboxes) { 222 | trustbox.sku = attrs.sku; 223 | } 224 | } 225 | 226 | if (settings.trustbox.trustboxes && attrs.name) { 227 | for (trustbox of settings.trustbox.trustboxes) { 228 | trustbox.name = attrs.name; 229 | } 230 | } 231 | 232 | iframe.contentWindow.postMessage(JSON.stringify(settings), attrs.transfer); 233 | } 234 | 235 | function sendPastOrdersInfo(data) { 236 | const iframe = document.getElementById('configuration_iframe'); 237 | const attrs = iframe.dataset; 238 | 239 | if (data === undefined) { 240 | data = attrs.pastOrders; 241 | } 242 | iframe.contentWindow.postMessage(data, attrs.transfer); 243 | } 244 | 245 | function updateIframeSize(settings) { 246 | const iframe = document.getElementById('configuration_iframe'); 247 | if (iframe) { 248 | iframe.height=(settings.window.height) + "px"; 249 | } 250 | } 251 | 252 | function sendSignupData() { 253 | const data = { 254 | action: 'get_signup_data', 255 | form_key: window.FORM_KEY, 256 | scope, scopeId, 257 | }; 258 | 259 | const xhr = new XMLHttpRequest(); 260 | xhr.open('POST', `${ajaxUrl}?isAjax=true`, true); 261 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 262 | xhr.onreadystatechange = function () { 263 | if (xhr.readyState === 4) { 264 | if (xhr.status >= 400) { 265 | console.log(`callback error: ${xhr.response} ${xhr.status}`); 266 | } else { 267 | const iframe = document.getElementById('configuration_iframe'); 268 | iframe.contentWindow.postMessage(xhr.response, iframe.dataset.transfer); 269 | } 270 | } 271 | }; 272 | xhr.send(encodeSettings(data)); 273 | } 274 | 275 | function tryParseJson(str, out) { 276 | try { 277 | out = Object.assign(out, JSON.parse(str)); 278 | } catch (e) { 279 | return false; 280 | } 281 | return true; 282 | } 283 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/admin.min.js: -------------------------------------------------------------------------------- 1 | function receiveSettings(e){if(e.origin===location.origin)return receiveInternalData(e);const t=document.getElementById("configuration_iframe").dataset;if(e.origin!==t.transfer)return;const s=e.data;if("string"==typeof s)if(s.startsWith("sync:")||s.startsWith("showPastOrdersInitial:")){const e=s.split(":"),t={action:"handle_past_orders"};t[e[0]]=e[1],this.submitPastOrdersCommand(t)}else if(s.startsWith("resync")){const e={action:"handle_past_orders",resync:"resync"};this.submitPastOrdersCommand(e)}else if(s.startsWith("issynced")){const e={action:"handle_past_orders",issynced:"issynced"};this.submitPastOrdersCommand(e)}else if(s.startsWith("check_product_skus")){const e=s.split(":"),t={action:"check_product_skus"};t.skuSelector=e[1],this.submitCheckProductSkusCommand(t)}else"signup_data"===s?this.sendSignupData():"update"===s?this.updateplugin():"reload"===s?this.reloadSettings():this.handleJSONMessage(s)}function handleJSONMessage(e){const t={};if(tryParseJson(e,t))if(t.TrustBoxPreviewMode)this.trustBoxPreviewMode(t);else if(t.window)this.updateIframeSize(t);else if("submit"===t.type)this.submitSettings(t);else if(t.trustbox){document.getElementById("trustbox_preview_frame").contentWindow.postMessage(JSON.stringify(t.trustbox),"*")}}function trustBoxPreviewMode(e){const t=document.getElementById("trustpilot-trustbox-preview");e.TrustBoxPreviewMode.enable?t.hidden=!1:t.hidden=!0}function receiveInternalData(e){const t=e.data,s={};t&&"string"==typeof t&&tryParseJson(t,s)&&(s&&"loadCategoryProductInfo"===s.type&&requestCategoryInfo(),"updatePageUrls"!==s.type&&"newTrustBox"!==s.type||this.submitSettings(s))}function requestCategoryInfo(){}function submitPastOrdersCommand(e){e.form_key=window.FORM_KEY,e.scope=scope,e.scopeId=scopeId;const t=new XMLHttpRequest;t.open("POST",`${ajaxUrl}?isAjax=true`,!0),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){4===t.readyState&&(t.status>=400?console.log(`callback error: ${t.response} ${t.status}`):sendPastOrdersInfo(t.response))},t.send(encodeSettings(e))}function submitCheckProductSkusCommand(e){e.form_key=window.FORM_KEY,e.scope=scope,e.scopeId=scopeId;const t=new XMLHttpRequest;t.open("POST",`${ajaxUrl}?isAjax=true`,!0),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){if(4===t.readyState)if(t.status>=400)console.log(`callback error: ${t.response} ${t.status}`);else{const e=document.getElementById("configuration_iframe");e.contentWindow.postMessage(t.response,e.dataset.transfer)}},t.send(encodeSettings(e))}function submitSettings(e){const t={action:"handle_save_changes",form_key:window.FORM_KEY,scope:scope,scopeId:scopeId};if("updatePageUrls"===e.type)t.pageUrls=encodeURIComponent(JSON.stringify(e.pageUrls));else if("newTrustBox"===e.type)t.customTrustBoxes=encodeURIComponent(JSON.stringify(e));else{t.settings=encodeURIComponent(JSON.stringify(e.settings));const s=document.getElementById("trustbox_preview_frame");s?s.dataset.settings=btoa(encodeURIComponent(JSON.stringify(e.settings))):console.log("trustbox_preview_frame is missing. Skipping...")}"undefined"!=typeof websiteId&&(t.website_id=websiteId),"undefined"!=typeof storeId&&(t.store_id=storeId);const s=new XMLHttpRequest;s.open("POST",`${ajaxUrl}?isAjax=true`),s.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),s.send(encodeSettings(t))}function encodeSettings(e){let t="";for(const s in e)t+=`${s}=${e[s]}&`;return t.substring(0,t.length-1)}function sendSettings(){const e=document.getElementById("configuration_iframe"),t=e.dataset,s=JSON.parse(atob(t.settings));if(s.trustbox||(s.trustbox={}),s.trustbox.pageUrls=JSON.parse(atob(t.pageUrls)),s.pluginVersion=t.pluginVersion,s.source=t.source,s.version=t.version,s.basis="plugin",s.productIdentificationOptions=JSON.parse(t.productIdentificationOptions),s.configurationScopeTree=JSON.parse(atob(t.configurationScopeTree)),s.pluginStatus=JSON.parse(atob(t.pluginStatus)),s.isFromMarketplace=t.isFromMarketplace,s.trustbox.trustboxes&&t.sku)for(trustbox of s.trustbox.trustboxes)trustbox.sku=t.sku;if(s.trustbox.trustboxes&&t.name)for(trustbox of s.trustbox.trustboxes)trustbox.name=t.name;e.contentWindow.postMessage(JSON.stringify(s),t.transfer)}function sendPastOrdersInfo(e){const t=document.getElementById("configuration_iframe"),s=t.dataset;void 0===e&&(e=s.pastOrders),t.contentWindow.postMessage(e,s.transfer)}function updateIframeSize(e){const t=document.getElementById("configuration_iframe");t&&(t.height=e.window.height+"px")}function sendSignupData(){const e={action:"get_signup_data",form_key:window.FORM_KEY,scope:scope,scopeId:scopeId},t=new XMLHttpRequest;t.open("POST",`${ajaxUrl}?isAjax=true`,!0),t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.onreadystatechange=function(){if(4===t.readyState)if(t.status>=400)console.log(`callback error: ${t.response} ${t.status}`);else{const e=document.getElementById("configuration_iframe");e.contentWindow.postMessage(t.response,e.dataset.transfer)}},t.send(encodeSettings(e))}function tryParseJson(e,t){try{t=Object.assign(t,JSON.parse(e))}catch(e){return!1}return!0}window.addEventListener("message",this.receiveSettings); 2 | //# sourceMappingURL=admin.min.js.map 3 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/admin.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["admin.js"],"names":["receiveSettings","e","origin","location","receiveInternalData","attrs","document","getElementById","dataset","transfer","data","startsWith","split","action","this","submitPastOrdersCommand","resync","issynced","submitCheckProductSkusCommand","sendSignupData","updateplugin","reloadSettings","handleJSONMessage","parsedData","tryParseJson","TrustBoxPreviewMode","trustBoxPreviewMode","window","updateIframeSize","type","submitSettings","trustbox","contentWindow","postMessage","JSON","stringify","settings","div","enable","hidden","requestCategoryInfo","FORM_KEY","scope","scopeId","xhr","XMLHttpRequest","open","ajaxUrl","setRequestHeader","onreadystatechange","readyState","status","console","log","response","sendPastOrdersInfo","send","encodeSettings","iframe","form_key","pageUrls","encodeURIComponent","customTrustBoxes","frame","btoa","websiteId","website_id","storeId","store_id","encodedString","setting","substring","length","sendSettings","parse","atob","pluginVersion","source","version","basis","productIdentificationOptions","configurationScopeTree","pluginStatus","isFromMarketplace","trustboxes","sku","name","undefined","pastOrders","height","str","out","Object","assign","addEventListener"],"mappings":"AAEA,SAASA,gBAAgBC,GACrB,GAAIA,EAAEC,SAAWC,SAASD,OACtB,OAAOE,oBAAoBH,GAE/B,MACMI,EADSC,SAASC,eAAe,wBAClBC,QACrB,GAAIP,EAAEC,SAAWG,EAAMI,SACnB,OAEJ,MAAMC,EAAOT,EAAES,KAEf,GAAoB,iBAATA,EAIX,GAAIA,EAAKC,WAAW,UAAYD,EAAKC,WAAW,0BAA2B,CACvE,MAAMC,EAAQF,EAAKE,MAAM,KACnBC,GACNA,OAAmB,sBACnBA,EAAOD,EAAM,IAAMA,EAAM,GACzBE,KAAKC,wBAAwBF,QAC1B,GAAIH,EAAKC,WAAW,UAAW,CAClC,MAAME,GACNA,OAAmB,qBACnBG,OAAmB,UACnBF,KAAKC,wBAAwBF,QAC1B,GAAIH,EAAKC,WAAW,YAAa,CACpC,MAAME,GACNA,OAAmB,qBACnBI,SAAqB,YACrBH,KAAKC,wBAAwBF,QAC1B,GAAIH,EAAKC,WAAW,sBAAuB,CAC9C,MAAMC,EAAQF,EAAKE,MAAM,KACnBC,GACNA,OAAmB,sBACnBA,EAAoB,YAAID,EAAM,GAC9BE,KAAKI,8BAA8BL,OACnB,gBAATH,EACPI,KAAKK,iBACW,WAATT,EACPI,KAAKM,eACW,WAATV,EACPI,KAAKO,iBAELP,KAAKQ,kBAAkBZ,GAI/B,SAASY,kBAAkBZ,GACvB,MAAMa,KACN,GAAIC,aAAad,EAAMa,GACnB,GAAIA,EAAWE,oBACXX,KAAKY,oBAAoBH,QACtB,GAAIA,EAAWI,OAClBb,KAAKc,iBAAiBL,QACnB,GAAwB,WAApBA,EAAWM,KAClBf,KAAKgB,eAAeP,QACjB,GAAIA,EAAWQ,SAAU,CACbzB,SAASC,eAAe,0BAChCyB,cAAcC,YAAYC,KAAKC,UAAUZ,EAAWQ,UAAW,MAKlF,SAASL,oBAAoBU,GACzB,MAAMC,EAAM/B,SAASC,eAAe,+BAChC6B,EAASX,oBAAoBa,OAC7BD,EAAIE,QAAS,EAEbF,EAAIE,QAAS,EAIrB,SAASnC,oBAAoBH,GACzB,MAAMS,EAAOT,EAAES,KACTa,KACFb,GAAwB,iBAATA,GAAqBc,aAAad,EAAMa,KACnDA,GAAkC,4BAApBA,EAAWM,MACzBW,sBAEoB,mBAApBjB,EAAWM,MAAiD,gBAApBN,EAAWM,MACnDf,KAAKgB,eAAeP,IAKhC,SAASiB,uBA8BT,SAASzB,wBAAwBL,GAC7BA,EAAe,SAAIiB,OAAOc,SAC1B/B,EAAY,MAAIgC,MAChBhC,EAAc,QAAIiC,QAClB,MAAMC,EAAM,IAAIC,eAChBD,EAAIE,KAAK,UAAWC,uBAAuB,GAC3CH,EAAII,iBAAiB,eAAgB,qCACrCJ,EAAIK,mBAAqB,WACE,IAAnBL,EAAIM,aACAN,EAAIO,QAAU,IACdC,QAAQC,uBAAuBT,EAAIU,YAAYV,EAAIO,UAEnDI,mBAAmBX,EAAIU,YAInCV,EAAIY,KAAKC,eAAe/C,IAG5B,SAASQ,8BAA8BR,GACnCA,EAAe,SAAIiB,OAAOc,SAC1B/B,EAAY,MAAIgC,MAChBhC,EAAc,QAAIiC,QAClB,MAAMC,EAAM,IAAIC,eAChBD,EAAIE,KAAK,UAAWC,uBAAuB,GAC3CH,EAAII,iBAAiB,eAAgB,qCACrCJ,EAAIK,mBAAqB,WACrB,GAAuB,IAAnBL,EAAIM,WACJ,GAAIN,EAAIO,QAAU,IACdC,QAAQC,uBAAuBT,EAAIU,YAAYV,EAAIO,cAChD,CACH,MAAMO,EAASpD,SAASC,eAAe,wBACvCmD,EAAO1B,cAAcC,YAAYW,EAAIU,SAAUI,EAAOlD,QAAQC,YAI1EmC,EAAIY,KAAKC,eAAe/C,IAG5B,SAASoB,eAAeP,GACpB,MAAMb,GACFG,OAAQ,sBACR8C,SAAUhC,OAAOc,SACjBC,MAAAA,MAAOC,QAAAA,SAGX,GAAwB,mBAApBpB,EAAWM,KACXnB,EAAKkD,SAAWC,mBAAmB3B,KAAKC,UAAUZ,EAAWqC,gBAC1D,GAAwB,gBAApBrC,EAAWM,KAClBnB,EAAKoD,iBAAmBD,mBAAmB3B,KAAKC,UAAUZ,QACvD,CACHb,EAAK0B,SAAWyB,mBAAmB3B,KAAKC,UAAUZ,EAAWa,WAC7D,MAAM2B,EAAQzD,SAASC,eAAe,0BAClCwD,EACAA,EAAMvD,QAAQ4B,SAAW4B,KAAKH,mBAAmB3B,KAAKC,UAAUZ,EAAWa,YAE3EgB,QAAQC,IAAI,kDAIK,oBAAdY,YACPvD,EAAKwD,WAAaD,WAEC,oBAAZE,UACPzD,EAAK0D,SAAWD,SAGpB,MAAMvB,EAAM,IAAIC,eAChBD,EAAIE,KAAK,UAAWC,uBACpBH,EAAII,iBAAiB,eAAgB,qCACrCJ,EAAIY,KAAKC,eAAe/C,IAG5B,SAAS+C,eAAerB,GACpB,IAAIiC,EAAgB,GACpB,IAAK,MAAMC,KAAWlC,EAClBiC,MAAoBC,KAAWlC,EAASkC,MAE5C,OAAOD,EAAcE,UAAU,EAAGF,EAAcG,OAAS,GAG7D,SAASC,eACL,MAAMf,EAASpD,SAASC,eAAe,wBAEjCF,EAAQqD,EAAOlD,QACf4B,EAAWF,KAAKwC,MAAMC,KAAKtE,EAAM+B,WAgBvC,GAdKA,EAASL,WACVK,EAASL,aAGbK,EAASL,SAAS6B,SAAW1B,KAAKwC,MAAMC,KAAKtE,EAAMuD,WACnDxB,EAASwC,cAAgBvE,EAAMuE,cAC/BxC,EAASyC,OAASxE,EAAMwE,OACxBzC,EAAS0C,QAAUzE,EAAMyE,QACzB1C,EAAS2C,MAAQ,SACjB3C,EAAS4C,6BAA+B9C,KAAKwC,MAAMrE,EAAM2E,8BACzD5C,EAAS6C,uBAAyB/C,KAAKwC,MAAMC,KAAKtE,EAAM4E,yBACxD7C,EAAS8C,aAAehD,KAAKwC,MAAMC,KAAKtE,EAAM6E,eAC9C9C,EAAS+C,kBAAoB9E,EAAM8E,kBAE/B/C,EAASL,SAASqD,YAAc/E,EAAMgF,IACtC,IAAKtD,YAAYK,EAASL,SAASqD,WAC/BrD,SAASsD,IAAMhF,EAAMgF,IAI7B,GAAIjD,EAASL,SAASqD,YAAc/E,EAAMiF,KACtC,IAAKvD,YAAYK,EAASL,SAASqD,WAC/BrD,SAASuD,KAAOjF,EAAMiF,KAI9B5B,EAAO1B,cAAcC,YAAYC,KAAKC,UAAUC,GAAW/B,EAAMI,UAGrE,SAAS8C,mBAAmB7C,GACxB,MAAMgD,EAASpD,SAASC,eAAe,wBACjCF,EAAQqD,EAAOlD,aAER+E,IAAT7E,IACAA,EAAOL,EAAMmF,YAEjB9B,EAAO1B,cAAcC,YAAYvB,EAAML,EAAMI,UAGjD,SAASmB,iBAAiBQ,GACtB,MAAMsB,EAASpD,SAASC,eAAe,wBACnCmD,IACAA,EAAO+B,OAAQrD,EAAST,OAAa,OAAI,MAIjD,SAASR,iBACL,MAAMT,GACFG,OAAQ,kBACR8C,SAAUhC,OAAOc,SACjBC,MAAAA,MAAOC,QAAAA,SAGLC,EAAM,IAAIC,eAChBD,EAAIE,KAAK,UAAWC,uBAAuB,GAC3CH,EAAII,iBAAiB,eAAgB,qCACrCJ,EAAIK,mBAAqB,WACrB,GAAuB,IAAnBL,EAAIM,WACJ,GAAIN,EAAIO,QAAU,IACdC,QAAQC,uBAAuBT,EAAIU,YAAYV,EAAIO,cAChD,CACH,MAAMO,EAASpD,SAASC,eAAe,wBACvCmD,EAAO1B,cAAcC,YAAYW,EAAIU,SAAUI,EAAOlD,QAAQC,YAI1EmC,EAAIY,KAAKC,eAAe/C,IAG5B,SAASc,aAAakE,EAAKC,GACvB,IACIA,EAAMC,OAAOC,OAAOF,EAAKzD,KAAKwC,MAAMgB,IACtC,MAAOzF,GACL,OAAO,EAEX,OAAO,EAxRX0B,OAAOmE,iBAAiB,UAAWhF,KAAKd","file":"admin.min.js"} -------------------------------------------------------------------------------- /view/frontend/layout/checkout_onepage_success.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /view/frontend/layout/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /view/frontend/templates/head/head.phtml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 15 | -------------------------------------------------------------------------------- /view/frontend/templates/order/success.phtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /view/frontend/templates/trustbox.phtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /view/frontend/web/css/trustpilot.css: -------------------------------------------------------------------------------- 1 | .shake-and-hide-element { 2 | -moz-animation: hide-element 0s ease-in 5s forwards, shake-element 1s; 3 | /* Firefox */ 4 | -webkit-animation: hide-element 0s ease-in 5s forwards, shake-element 1s; 5 | /* Safari and Chrome */ 6 | -o-animation: hide-element 0s ease-in 5s forwards, shake-element 1s; 7 | /* Opera */ 8 | animation: hide-element 0s ease-in 5s forwards, shake-element 1s; 9 | -webkit-animation-fill-mode: forwards; 10 | animation-fill-mode: forwards; 11 | } 12 | 13 | 14 | @keyframes shake-element { 15 | 0%, 100% {transform: translateX(0);} 16 | 10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 17 | 20%, 40%, 60%, 80% {transform: translateX(5px);} 18 | } 19 | 20 | @-webkit-keyframes shake-element { 21 | 0%, 100% {transform: translateX(0);} 22 | 10%, 30%, 50%, 70%, 90% {transform: translateX(-5px);} 23 | 20%, 40%, 60%, 80% {transform: translateX(5px);} 24 | } 25 | 26 | @keyframes hide-element { 27 | to { 28 | width: 0; 29 | height: 0; 30 | padding: 0px; 31 | margin: 0px; 32 | overflow: hidden; 33 | } 34 | } 35 | 36 | @-webkit-keyframes hide-element { 37 | to { 38 | width: 0; 39 | height: 0; 40 | padding: 0px; 41 | margin: 0px; 42 | visibility: hidden; 43 | } 44 | } 45 | .warning-icon{ 46 | width: 20%; 47 | max-width: 30px; 48 | font-size: 21px; 49 | display: inline-block; 50 | vertical-align: middle; 51 | } 52 | .trustbox-message-text { 53 | width: 80%; 54 | display: inline-block; 55 | vertical-align: middle; 56 | } 57 | 58 | .trustbox-message-box { 59 | background-color: #fcf8e3; 60 | color: #8a6d3b; 61 | padding: 15px; 62 | margin-bottom: 20px; 63 | border-radius: 4px; 64 | border: 1px solid; 65 | border-color: #faebcc; 66 | } -------------------------------------------------------------------------------- /view/frontend/web/css/trustpilot.min.css: -------------------------------------------------------------------------------- 1 | .shake-and-hide-element{-moz-animation:hide-element 0s ease-in 5s forwards,shake-element 1s;-webkit-animation:hide-element 0s ease-in 5s forwards,shake-element 1s;-o-animation:hide-element 0s ease-in 5s forwards,shake-element 1s;animation:hide-element 0s ease-in 5s forwards,shake-element 1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@keyframes shake-element{0%,100%{transform:translateX(0)} 2 | 10%,30%,50%,70%,90%{transform:translateX(-5px)}20%,40%,60%,80%{transform:translateX(5px)}}@-webkit-keyframes shake-element{0%,100%{transform:translateX(0)}10%,30%,50%,70%,90%{transform:translateX(-5px)}20%,40%,60%,80%{transform:translateX(5px)}}@keyframes hide-element{to{width:0;height:0;padding:0;margin:0;overflow:hidden}}@-webkit-keyframes hide-element{to{width:0;height:0;padding:0;margin:0;visibility:hidden}}.warning-icon{width:20%;max-width:30px;font-size:21px;display:inline-block;vertical-align:middle} 3 | .trustbox-message-text{width:80%;display:inline-block;vertical-align:middle}.trustbox-message-box{background-color:#fcf8e3;color:#8a6d3b;padding:15px;margin-bottom:20px;border-radius:4px;border:1px solid;border-color:#faebcc} -------------------------------------------------------------------------------- /view/frontend/web/js/wgxpath.install.js: -------------------------------------------------------------------------------- 1 | (function(){'use strict';var k=this; 2 | function aa(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"== 3 | b&&"undefined"==typeof a.call)return"object";return b}function l(a){return"string"==typeof a}function ba(a,b,c){return a.call.apply(a.bind,arguments)}function ca(a,b,c){if(!a)throw Error();if(2b?1:0};var ha=Array.prototype.indexOf?function(a,b,c){return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(l(a))return l(b)&&1==b.length?a.indexOf(b,c):-1;for(;cc?null:l(a)?a.charAt(c):a[c]}function la(a){return Array.prototype.concat.apply(Array.prototype,arguments)}function ma(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var u;a:{var na=k.navigator;if(na){var oa=na.userAgent;if(oa){u=oa;break a}}u=""};var pa=q(u,"Opera")||q(u,"OPR"),v=q(u,"Trident")||q(u,"MSIE"),qa=q(u,"Edge"),ra=q(u,"Gecko")&&!(q(u.toLowerCase(),"webkit")&&!q(u,"Edge"))&&!(q(u,"Trident")||q(u,"MSIE"))&&!q(u,"Edge"),sa=q(u.toLowerCase(),"webkit")&&!q(u,"Edge");function ta(){var a=k.document;return a?a.documentMode:void 0}var ua; 33 | a:{var va="",wa=function(){var a=u;if(ra)return/rv\:([^\);]+)(\)|;)/.exec(a);if(qa)return/Edge\/([\d\.]+)/.exec(a);if(v)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(sa)return/WebKit\/(\S+)/.exec(a);if(pa)return/(?:Version)[ \/]?(\S+)/.exec(a)}();wa&&(va=wa?wa[1]:"");if(v){var xa=ta();if(null!=xa&&xa>parseFloat(va)){ua=String(xa);break a}}ua=va}var ya={}; 34 | function za(a){if(!ya[a]){for(var b=0,c=fa(String(ua)).split("."),d=fa(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f",4,2,function(a,b,c){return O(function(a,b){return a>b},a,b,c)});P("<=",4,2,function(a,b,c){return O(function(a,b){return a<=b},a,b,c)});P(">=",4,2,function(a,b,c){return O(function(a,b){return a>=b},a,b,c)});var Wa=P("=",3,2,function(a,b,c){return O(function(a,b){return a==b},a,b,c,!0)});P("!=",3,2,function(a,b,c){return O(function(a,b){return a!=b},a,b,c,!0)});P("and",2,2,function(a,b,c){return M(a,c)&&M(b,c)});P("or",1,2,function(a,b,c){return M(a,c)||M(b,c)});function Q(a,b,c){this.a=a;this.b=b||1;this.f=c||1};function Za(a,b){if(b.a.length&&4!=a.i)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");n.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}m(Za);Za.prototype.a=function(a){a=this.c.a(a);return $a(this.h,a)};Za.prototype.toString=function(){var a;a="Filter:"+J(this.c);return a+=J(this.h)};function ab(a,b){if(b.lengtha.v)throw Error("Function "+a.j+" expects at most "+a.v+" arguments, "+b.length+" given");a.B&&r(b,function(b,d){if(4!=b.i)throw Error("Argument "+d+" to function "+a.j+" is not of type Nodeset: "+b);});n.call(this,a.i);this.h=a;this.c=b;Ua(this,a.g||ja(b,function(a){return a.g}));Va(this,a.D&&!b.length||a.C&&!!b.length||ja(b,function(a){return a.b}))}m(ab); 48 | ab.prototype.a=function(a){return this.h.m.apply(null,la(a,this.c))};ab.prototype.toString=function(){var a="Function: "+this.h;if(this.c.length)var b=t(this.c,function(a,b){return a+J(b)},"Arguments:"),a=a+J(b);return a};function bb(a,b,c,d,e,f,g,h,p){this.j=a;this.i=b;this.g=c;this.D=d;this.C=e;this.m=f;this.A=g;this.v=void 0!==h?h:g;this.B=!!p}bb.prototype.toString=function(){return this.j};var cb={}; 49 | function R(a,b,c,d,e,f,g,h){if(cb.hasOwnProperty(a))throw Error("Function already created: "+a+".");cb[a]=new bb(a,b,c,d,!1,e,f,g,h)}R("boolean",2,!1,!1,function(a,b){return M(b,a)},1);R("ceiling",1,!1,!1,function(a,b){return Math.ceil(K(b,a))},1);R("concat",3,!1,!1,function(a,b){return t(ma(arguments,1),function(b,d){return b+L(d,a)},"")},2,null);R("contains",2,!1,!1,function(a,b,c){return q(L(b,a),L(c,a))},2);R("count",1,!1,!1,function(a,b){return b.a(a).l},1,1,!0); 50 | R("false",2,!1,!1,function(){return!1},0);R("floor",1,!1,!1,function(a,b){return Math.floor(K(b,a))},1);R("id",4,!1,!1,function(a,b){function c(a){if(w){var b=e.all[a];if(b){if(b.nodeType&&a==b.id)return b;if(b.length)return ka(b,function(b){return a==b.id})}return null}return e.getElementById(a)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument,d=L(b,a).split(/\s+/),f=[];r(d,function(a){a=c(a);!a||0<=ha(f,a)||f.push(a)});f.sort(La);var g=new C;r(f,function(a){F(g,a)});return g},1); 51 | R("lang",2,!1,!1,function(){return!1},1);R("last",1,!0,!1,function(a){if(1!=arguments.length)throw Error("Function last expects ()");return a.f},0);R("local-name",3,!1,!0,function(a,b){var c=b?Ra(b.a(a)):a.a;return c?c.localName||c.nodeName.toLowerCase():""},0,1,!0);R("name",3,!1,!0,function(a,b){var c=b?Ra(b.a(a)):a.a;return c?c.nodeName.toLowerCase():""},0,1,!0);R("namespace-uri",3,!0,!1,function(){return""},0,1,!0); 52 | R("normalize-space",3,!1,!0,function(a,b){return(b?L(b,a):z(a.a)).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1);R("not",2,!1,!1,function(a,b){return!M(b,a)},1);R("number",1,!1,!0,function(a,b){return b?K(b,a):+z(a.a)},0,1);R("position",1,!0,!1,function(a){return a.b},0);R("round",1,!1,!1,function(a,b){return Math.round(K(b,a))},1);R("starts-with",2,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);return 0==b.lastIndexOf(a,0)},2);R("string",3,!1,!0,function(a,b){return b?L(b,a):z(a.a)},0,1); 53 | R("string-length",1,!1,!0,function(a,b){return(b?L(b,a):z(a.a)).length},0,1);R("substring",3,!1,!1,function(a,b,c,d){c=K(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return"";d=d?K(d,a):Infinity;if(isNaN(d)||-Infinity===d)return"";c=Math.round(c)-1;var e=Math.max(c,0);a=L(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);R("substring-after",3,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);c=b.indexOf(a);return-1==c?"":b.substring(c+a.length)},2); 54 | R("substring-before",3,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);a=b.indexOf(a);return-1==a?"":b.substring(0,a)},2);R("sum",1,!1,!1,function(a,b){for(var c=H(b.a(a)),d=0,e=I(c);e;e=I(c))d+=+z(e);return d},1,1,!0);R("translate",3,!1,!1,function(a,b,c,d){b=L(b,a);c=L(c,a);var e=L(d,a);a={};for(d=0;d]=|\s+|./g,hb=/^\s/;function S(a,b){return a.b[a.a+(b||0)]}function T(a){return a.b[a.a++]}function ib(a){return a.b.length<=a.a};function jb(a){n.call(this,3);this.c=a.substring(1,a.length-1)}m(jb);jb.prototype.a=function(){return this.c};jb.prototype.toString=function(){return"Literal: "+this.c};function E(a,b){this.j=a.toLowerCase();var c;c="*"==this.j?"*":"http://www.w3.org/1999/xhtml";this.c=b?b.toLowerCase():c}E.prototype.a=function(a){var b=a.nodeType;if(1!=b&&2!=b)return!1;b=void 0!==a.localName?a.localName:a.nodeName;return"*"!=this.j&&this.j!=b.toLowerCase()?!1:"*"==this.c?!0:this.c==(a.namespaceURI?a.namespaceURI.toLowerCase():"http://www.w3.org/1999/xhtml")};E.prototype.f=function(){return this.j}; 56 | E.prototype.toString=function(){return"Name Test: "+("http://www.w3.org/1999/xhtml"==this.c?"":this.c+":")+this.j};function kb(a,b){n.call(this,a.i);this.h=a;this.c=b;this.g=a.g;this.b=a.b;if(1==this.c.length){var c=this.c[0];c.u||c.c!=lb||(c=c.o,"*"!=c.f()&&(this.f={name:c.f(),s:null}))}}m(kb);function mb(){n.call(this,4)}m(mb);mb.prototype.a=function(a){var b=new C;a=a.a;9==a.nodeType?F(b,a):F(b,a.ownerDocument);return b};mb.prototype.toString=function(){return"Root Helper Expression"};function nb(){n.call(this,4)}m(nb);nb.prototype.a=function(a){var b=new C;F(b,a.a);return b};nb.prototype.toString=function(){return"Context Helper Expression"}; 57 | function ob(a){return"/"==a||"//"==a}kb.prototype.a=function(a){var b=this.h.a(a);if(!(b instanceof C))throw Error("Filter expression must evaluate to nodeset.");a=this.c;for(var c=0,d=a.length;ca.length)throw Error("Unclosed literal string");return new jb(a)} 69 | function Hb(a){var b,c=[],d;if(ob(S(a.a))){b=T(a.a);d=S(a.a);if("/"==b&&(ib(a.a)||"."!=d&&".."!=d&&"@"!=d&&"*"!=d&&!/(?![0-9])[\w]/.test(d)))return new mb;d=new mb;W(a,"Missing next location step.");b=Ib(a,b);c.push(b)}else{a:{b=S(a.a);d=b.charAt(0);switch(d){case "$":throw Error("Variable reference not allowed in HTML XPath");case "(":T(a.a);b=Cb(a);W(a,'unclosed "("');Eb(a,")");break;case '"':case "'":b=Gb(a);break;default:if(isNaN(+b))if(!db(b)&&/(?![0-9])[\w]/.test(d)&&"("==S(a.a,1)){b=T(a.a); 70 | b=cb[b]||null;T(a.a);for(d=[];")"!=S(a.a);){W(a,"Missing function argument list.");d.push(Cb(a));if(","!=S(a.a))break;T(a.a)}W(a,"Unclosed function argument list.");Fb(a);b=new ab(b,d)}else{b=null;break a}else b=new Ab(+T(a.a))}"["==S(a.a)&&(d=new sb(Jb(a)),b=new Za(b,d))}if(b)if(ob(S(a.a)))d=b;else return b;else b=Ib(a,"/"),d=new nb,c.push(b)}for(;ob(S(a.a));)b=T(a.a),W(a,"Missing next location step."),b=Ib(a,b),c.push(b);return new kb(d,c)} 71 | function Ib(a,b){var c,d,e;if("/"!=b&&"//"!=b)throw Error('Step op should be "/" or "//"');if("."==S(a.a))return d=new U(yb,new G("node")),T(a.a),d;if(".."==S(a.a))return d=new U(xb,new G("node")),T(a.a),d;var f;if("@"==S(a.a))f=lb,T(a.a),W(a,"Missing attribute name");else if("::"==S(a.a,1)){if(!/(?![0-9])[\w]/.test(S(a.a).charAt(0)))throw Error("Bad token: "+T(a.a));c=T(a.a);f=wb[c]||null;if(!f)throw Error("No axis with name: "+c);T(a.a);W(a,"Missing node name")}else f=tb;c=S(a.a);if(/(?![0-9])[\w\*]/.test(c.charAt(0)))if("("== 72 | S(a.a,1)){if(!db(c))throw Error("Invalid node type: "+c);c=T(a.a);if(!db(c))throw Error("Invalid type name: "+c);Eb(a,"(");W(a,"Bad nodetype");e=S(a.a).charAt(0);var g=null;if('"'==e||"'"==e)g=Gb(a);W(a,"Bad nodetype");Fb(a);c=new G(c,g)}else if(c=T(a.a),e=c.indexOf(":"),-1==e)c=new E(c);else{var g=c.substring(0,e),h;if("*"==g)h="*";else if(h=a.b(g),!h)throw Error("Namespace prefix not declared: "+g);c=c.substr(e+1);c=new E(c,h)}else throw Error("Bad token: "+T(a.a));e=new sb(Jb(a),f.a);return d|| 73 | new U(f,c,e,"//"==b)}function Jb(a){for(var b=[];"["==S(a.a);){T(a.a);W(a,"Missing predicate expression.");var c=Cb(a);b.push(c);W(a,"Unclosed predicate expression.");Eb(a,"]")}return b}function Db(a){if("-"==S(a.a))return T(a.a),new zb(Db(a));var b=Hb(a);if("|"!=S(a.a))a=b;else{for(b=[b];"|"==T(a.a);)W(a,"Missing next union location path."),b.push(Hb(a));a.a.a--;a=new rb(b)}return a};function Kb(a){switch(a.nodeType){case 1:return ea(Lb,a);case 9:return Kb(a.documentElement);case 11:case 10:case 6:case 12:return Mb;default:return a.parentNode?Kb(a.parentNode):Mb}}function Mb(){return null}function Lb(a,b){if(a.prefix==b)return a.namespaceURI||"http://www.w3.org/1999/xhtml";var c=a.getAttributeNode("xmlns:"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?Lb(a.parentNode,b):null};function Nb(a,b){if(!a.length)throw Error("Empty XPath expression.");var c=fb(a);if(ib(c))throw Error("Invalid XPath expression.");b?"function"==aa(b)||(b=da(b.lookupNamespaceURI,b)):b=function(){return null};var d=Cb(new Bb(c,b));if(!ib(c))throw Error("Bad token: "+T(c));this.evaluate=function(a,b){var c=d.a(new Q(a));return new Y(c,b)}} 74 | function Y(a,b){if(0==b)if(a instanceof C)b=4;else if("string"==typeof a)b=2;else if("number"==typeof a)b=1;else if("boolean"==typeof a)b=3;else throw Error("Unexpected evaluation result.");if(2!=b&&1!=b&&3!=b&&!(a instanceof C))throw Error("value could not be converted to the specified type");this.resultType=b;var c;switch(b){case 2:this.stringValue=a instanceof C?Sa(a):""+a;break;case 1:this.numberValue=a instanceof C?+Sa(a):+a;break;case 3:this.booleanValue=a instanceof C?0=c.length?null:c[f++]};this.snapshotItem=function(a){if(6!=b&&7!=b)throw Error("snapshotItem called with wrong result type");return a>=c.length|| 76 | 0>a?null:c[a]}}Y.ANY_TYPE=0;Y.NUMBER_TYPE=1;Y.STRING_TYPE=2;Y.BOOLEAN_TYPE=3;Y.UNORDERED_NODE_ITERATOR_TYPE=4;Y.ORDERED_NODE_ITERATOR_TYPE=5;Y.UNORDERED_NODE_SNAPSHOT_TYPE=6;Y.ORDERED_NODE_SNAPSHOT_TYPE=7;Y.ANY_UNORDERED_NODE_TYPE=8;Y.FIRST_ORDERED_NODE_TYPE=9;function Ob(a){this.lookupNamespaceURI=Kb(a)} 77 | function Pb(a,b){var c=a||k,d=c.Document&&c.Document.prototype||c.document;if(!d.evaluate||b)c.XPathResult=Y,d.evaluate=function(a,b,c,d){return(new Nb(a,c)).evaluate(b,d)},d.createExpression=function(a,b){return new Nb(a,b)},d.createNSResolver=function(a){return new Ob(a)}}var Qb=["wgxpath","install"],Z=k;Qb[0]in Z||!Z.execScript||Z.execScript("var "+Qb[0]);for(var Rb;Qb.length&&(Rb=Qb.shift());)Qb.length||void 0===Pb?Z[Rb]?Z=Z[Rb]:Z=Z[Rb]={}:Z[Rb]=Pb;}).call(this) 78 | -------------------------------------------------------------------------------- /view/frontend/web/js/wgxpath.install.min.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";var t=this;function n(t){return"string"==typeof t}function e(t,n,e){return t.call.apply(t.bind,arguments)}function r(t,n,e){if(!t)throw Error();if(2n?1:0}var c,f=Array.prototype.indexOf?function(t,n,e){return Array.prototype.indexOf.call(t,n,e)}:function(t,e,r){if(r=null==r?0:0>r?Math.max(0,t.length+r):r,n(t))return n(e)&&1==e.length?t.indexOf(e,r):-1;for(;rparseFloat(T)){y=String(I);break t}}y=T}var O={};function k(t){if(!O[t]){for(var n=0,e=a(String(y)).split("."),r=a(String(t)).split("."),o=Math.max(e.length,r.length),i=0;0==n&&i",4,2,function(t,n,e){return ct(function(t,n){return t>n},t,n,e)}),ht("<=",4,2,function(t,n,e){return ct(function(t,n){return t<=n},t,n,e)}),ht(">=",4,2,function(t,n,e){return ct(function(t,n){return t>=n},t,n,e)});var pt=ht("=",3,2,function(t,n,e){return ct(function(t,n){return t==n},t,n,e,!0)});function dt(t,n,e){this.a=t,this.b=n||1,this.f=e||1}function gt(t,n){if(n.a.length&&4!=t.i)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");nt.call(this,t.i),this.c=t,this.h=n,this.g=t.g,this.b=t.b}function vt(t,n){if(n.lengtht.v)throw Error("Function "+t.j+" expects at most "+t.v+" arguments, "+n.length+" given");t.B&&l(n,function(n,e){if(4!=n.i)throw Error("Argument "+e+" to function "+t.j+" is not of type Nodeset: "+n)}),nt.call(this,t.i),this.h=t,this.c=n,rt(this,t.g||d(n,function(t){return t.g})),ot(this,t.D&&!n.length||t.C&&!!n.length||d(n,function(t){return t.b}))}function yt(t,n,e,r,o,i,a,u,s){this.j=t,this.i=n,this.g=e,this.D=r,this.C=o,this.m=i,this.A=a,this.v=void 0!==u?u:a,this.B=!!s}ht("!=",3,2,function(t,n,e){return ct(function(t,n){return t!=n},t,n,e,!0)}),ht("and",2,2,function(t,n,e){return ut(t,e)&&ut(n,e)}),ht("or",1,2,function(t,n,e){return ut(t,e)||ut(n,e)}),i(gt),gt.prototype.a=function(t){return t=this.c.a(t),jt(this.h,t)},gt.prototype.toString=function(){return"Filter:"+et(this.c)+et(this.h)},i(vt),vt.prototype.a=function(t){return this.h.m.apply(null,function(t){return Array.prototype.concat.apply(Array.prototype,arguments)}(t,this.c))},vt.prototype.toString=function(){var t="Function: "+this.h;if(this.c.length)t=t+et(p(this.c,function(t,n){return t+et(n)},"Arguments:"));return t},yt.prototype.toString=function(){return this.j};var wt={};function bt(t,n,e,r,o,i,a,u){if(wt.hasOwnProperty(t))throw Error("Function already created: "+t+".");wt[t]=new yt(t,n,e,r,!1,o,i,a,u)}function mt(t,n){switch(this.h=t,this.c=void 0!==n?n:null,this.b=null,t){case"comment":this.b=8;break;case"text":this.b=3;break;case"processing-instruction":this.b=7;break;case"node":break;default:throw Error("Unexpected argument")}}function Et(t){return"comment"==t||"text"==t||"processing-instruction"==t||"node"==t}bt("boolean",2,!1,!1,function(t,n){return ut(n,t)},1),bt("ceiling",1,!1,!1,function(t,n){return Math.ceil(it(n,t))},1),bt("concat",3,!1,!1,function(t,n){return p(function(t,n,e){return 2>=arguments.length?Array.prototype.slice.call(t,n):Array.prototype.slice.call(t,n,e)}(arguments,1),function(n,e){return n+at(e,t)},"")},2,null),bt("contains",2,!1,!1,function(t,n,e){return u(at(n,t),at(e,t))},2),bt("count",1,!1,!1,function(t,n){return n.a(t).l},1,1,!0),bt("false",2,!1,!1,function(){return!1},0),bt("floor",1,!1,!1,function(t,n){return Math.floor(it(n,t))},1),bt("id",4,!1,!1,function(t,e){function r(t){if(R){var e=o.all[t];if(e){if(e.nodeType&&t==e.id)return e;if(e.length)return function(t,e){var r;t:{r=t.length;for(var o=n(t)?t.split(""):t,i=0;ir?null:n(t)?t.charAt(r):t[r]}(e,function(n){return t==n.id})}return null}return o.getElementById(t)}var o=9==(i=t.a).nodeType?i:i.ownerDocument,i=at(e,t).split(/\s+/),a=[];l(i,function(t){!(t=r(t))||0<=f(a,t)||a.push(t)}),a.sort(H);var u=new G;return l(a,function(t){q(u,t)}),u},1),bt("lang",2,!1,!1,function(){return!1},1),bt("last",1,!0,!1,function(t){if(1!=arguments.length)throw Error("Function last expects ()");return t.f},0),bt("local-name",3,!1,!0,function(t,n){var e=n?J(n.a(t)):t.a;return e?e.localName||e.nodeName.toLowerCase():""},0,1,!0),bt("name",3,!1,!0,function(t,n){var e=n?J(n.a(t)):t.a;return e?e.nodeName.toLowerCase():""},0,1,!0),bt("namespace-uri",3,!0,!1,function(){return""},0,1,!0),bt("normalize-space",3,!1,!0,function(t,n){return(n?at(n,t):M(t.a)).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1),bt("not",2,!1,!1,function(t,n){return!ut(n,t)},1),bt("number",1,!1,!0,function(t,n){return n?it(n,t):+M(t.a)},0,1),bt("position",1,!0,!1,function(t){return t.b},0),bt("round",1,!1,!1,function(t,n){return Math.round(it(n,t))},1),bt("starts-with",2,!1,!1,function(t,n,e){return n=at(n,t),t=at(e,t),0==n.lastIndexOf(t,0)},2),bt("string",3,!1,!0,function(t,n){return n?at(n,t):M(t.a)},0,1),bt("string-length",1,!1,!0,function(t,n){return(n?at(n,t):M(t.a)).length},0,1),bt("substring",3,!1,!1,function(t,n,e,r){if(e=it(e,t),isNaN(e)||1/0==e||-1/0==e)return"";if(r=r?it(r,t):1/0,isNaN(r)||-1/0===r)return"";e=Math.round(e)-1;var o=Math.max(e,0);return t=at(n,t),1/0==r?t.substring(o):t.substring(o,e+Math.round(r))},2,3),bt("substring-after",3,!1,!1,function(t,n,e){return n=at(n,t),t=at(e,t),-1==(e=n.indexOf(t))?"":n.substring(e+t.length)},2),bt("substring-before",3,!1,!1,function(t,n,e){return n=at(n,t),t=at(e,t),-1==(t=n.indexOf(t))?"":n.substring(0,t)},2),bt("sum",1,!1,!1,function(t,n){for(var e=Z(n.a(t)),r=0,o=tt(e);o;o=tt(e))r+=+M(o);return r},1,1,!0),bt("translate",3,!1,!1,function(t,n,e,r){n=at(n,t),e=at(e,t);var o=at(r,t);for(t={},r=0;r]=|\s+|./g,xt=/^\s/;function St(t,n){return t.b[t.a+(n||0)]}function Tt(t){return t.b[t.a++]}function At(t){return t.b.length<=t.a}function It(t){nt.call(this,3),this.c=t.substring(1,t.length-1)}function Ot(t,n){var e;this.j=t.toLowerCase(),e="*"==this.j?"*":"http://www.w3.org/1999/xhtml",this.c=n?n.toLowerCase():e}function kt(t,n){if(nt.call(this,t.i),this.h=t,this.c=n,this.g=t.g,this.b=t.b,1==this.c.length){var e=this.c[0];e.u||e.c!=Ft||"*"!=(e=e.o).f()&&(this.f={name:e.f(),s:null})}}function Dt(){nt.call(this,4)}function Pt(){nt.call(this,4)}function Rt(t){return"/"==t||"//"==t}function Ct(t){nt.call(this,4),this.c=t,rt(this,d(this.c,function(t){return t.g})),ot(this,d(this.c,function(t){return t.b}))}function _t(t,n){this.a=t,this.b=!!n}function jt(t,n,e){for(e=e||0;e(t=Tt(t.a)).length)throw Error("Unclosed literal string");return new It(t)}function tn(t){var n,e,r=[];if(Rt(St(t.a))){if(n=Tt(t.a),e=St(t.a),"/"==n&&(At(t.a)||"."!=e&&".."!=e&&"@"!=e&&"*"!=e&&!/(?![0-9])[\w]/.test(e)))return new Dt;e=new Dt,qt(t,"Missing next location step."),n=nn(t,n),r.push(n)}else{t:{switch(e=(n=St(t.a)).charAt(0)){case"$":throw Error("Variable reference not allowed in HTML XPath");case"(":Tt(t.a),n=zt(t),qt(t,'unclosed "("'),Jt(t,")");break;case'"':case"'":n=Zt(t);break;default:if(isNaN(+n)){if(Et(n)||!/(?![0-9])[\w]/.test(e)||"("!=St(t.a,1)){n=null;break t}for(n=Tt(t.a),n=wt[n]||null,Tt(t.a),e=[];")"!=St(t.a)&&(qt(t,"Missing function argument list."),e.push(zt(t)),","==St(t.a));)Tt(t.a);qt(t,"Unclosed function argument list."),Qt(t),n=new vt(n,e)}else n=new Wt(+Tt(t.a))}"["==St(t.a)&&(n=new gt(n,e=new _t(en(t))))}if(n){if(!Rt(St(t.a)))return n;e=n}else n=nn(t,"/"),e=new Pt,r.push(n)}for(;Rt(St(t.a));)n=Tt(t.a),qt(t,"Missing next location step."),n=nn(t,n),r.push(n);return new kt(e,r)}function nn(t,n){var e,r,o,i;if("/"!=n&&"//"!=n)throw Error('Step op should be "/" or "//"');if("."==St(t.a))return r=new Mt(Gt,new mt("node")),Tt(t.a),r;if(".."==St(t.a))return r=new Mt(Xt,new mt("node")),Tt(t.a),r;if("@"==St(t.a))i=Ft,Tt(t.a),qt(t,"Missing attribute name");else if("::"==St(t.a,1)){if(!/(?![0-9])[\w]/.test(St(t.a).charAt(0)))throw Error("Bad token: "+Tt(t.a));if(e=Tt(t.a),!(i=Ut[e]||null))throw Error("No axis with name: "+e);Tt(t.a),qt(t,"Missing node name")}else i=Vt;if(e=St(t.a),!/(?![0-9])[\w\*]/.test(e.charAt(0)))throw Error("Bad token: "+Tt(t.a));if("("==St(t.a,1)){if(!Et(e))throw Error("Invalid node type: "+e);if(!Et(e=Tt(t.a)))throw Error("Invalid type name: "+e);Jt(t,"("),qt(t,"Bad nodetype");var a=null;'"'!=(o=St(t.a).charAt(0))&&"'"!=o||(a=Zt(t)),qt(t,"Bad nodetype"),Qt(t),e=new mt(e,a)}else if(-1==(o=(e=Tt(t.a)).indexOf(":")))e=new Ot(e);else{var u;if("*"==(a=e.substring(0,o)))u="*";else if(!(u=t.b(a)))throw Error("Namespace prefix not declared: "+a);e=new Ot(e=e.substr(o+1),u)}return o=new _t(en(t),i.a),r||new Mt(i,e,o,"//"==n)}function en(t){for(var n=[];"["==St(t.a);){Tt(t.a),qt(t,"Missing predicate expression.");var e=zt(t);n.push(e),qt(t,"Unclosed predicate expression."),Jt(t,"]")}return n}function rn(t){if("-"==St(t.a))return Tt(t.a),new Kt(rn(t));var n=tn(t);if("|"!=St(t.a))t=n;else{for(n=[n];"|"==Tt(t.a);)qt(t,"Missing next union location path."),n.push(tn(t));t.a.a--,t=new Ct(n)}return t}function on(t){switch(t.nodeType){case 1:return function(t,n){var e=Array.prototype.slice.call(arguments,1);return function(){var n=e.slice();return n.push.apply(n,arguments),t.apply(this,n)}}(un,t);case 9:return on(t.documentElement);case 11:case 10:case 6:case 12:return an;default:return t.parentNode?on(t.parentNode):an}}function an(){return null}function un(t,n){if(t.prefix==n)return t.namespaceURI||"http://www.w3.org/1999/xhtml";var e=t.getAttributeNode("xmlns:"+n);return e&&e.specified?e.value||null:t.parentNode&&9!=t.parentNode.nodeType?un(t.parentNode,n):null}function sn(t,n){if(!t.length)throw Error("Empty XPath expression.");var e=function(t){t=t.match(Nt);for(var n=0;n=e.length?null:e[i++]},this.snapshotItem=function(t){if(6!=n&&7!=n)throw Error("snapshotItem called with wrong result type");return t>=e.length||0>t?null:e[t]}}function fn(n,e){var r=n||t,o=r.Document&&r.Document.prototype||r.document;o.evaluate&&!e||(r.XPathResult=cn,o.evaluate=function(t,n,e,r){return new sn(t,e).evaluate(n,r)},o.createExpression=function(t,n){return new sn(t,n)},o.createNSResolver=function(t){return new function(t){this.lookupNamespaceURI=on(t)}(t)})}i(Kt),Kt.prototype.a=function(t){return-it(this.c,t)},Kt.prototype.toString=function(){return"Unary Expression: -"+et(this.c)},i(Wt),Wt.prototype.a=function(){return this.c},Wt.prototype.toString=function(){return"Number: "+this.c},cn.ANY_TYPE=0,cn.NUMBER_TYPE=1,cn.STRING_TYPE=2,cn.BOOLEAN_TYPE=3,cn.UNORDERED_NODE_ITERATOR_TYPE=4,cn.ORDERED_NODE_ITERATOR_TYPE=5,cn.UNORDERED_NODE_SNAPSHOT_TYPE=6,cn.ORDERED_NODE_SNAPSHOT_TYPE=7,cn.ANY_UNORDERED_NODE_TYPE=8,cn.FIRST_ORDERED_NODE_TYPE=9;var ln,hn=["wgxpath","install"],pn=t;hn[0]in pn||!pn.execScript||pn.execScript("var "+hn[0]);for(;hn.length&&(ln=hn.shift());)hn.length||void 0===fn?pn=pn[ln]?pn[ln]:pn[ln]={}:pn[ln]=fn}).call(this); 2 | //# sourceMappingURL=wgxpath.install.min.js.map 3 | --------------------------------------------------------------------------------