├── 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)[^>]*?>.*?\\1>@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:enable13 | 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-content16 | 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:upgrade17 | 8. Compile (This command is only required in production mode.)
php bin/magento setup:di:compile18 | 9. Deploy static content (This command is only required in production mode.)
php bin/magento setup:static-content:deploy19 | 10. Disable maintenance mode.
php bin/magento maintenance:disable20 | -------------------------------------------------------------------------------- /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 |
To set up Trustpilot integration change the current configuration scope to Website or Store View.
3 |