diff --git a/Api/GetModuleSupportInfoInterface.php b/Api/GetModuleSupportInfoInterface.php new file mode 100644 index 0000000..290ff88 --- /dev/null +++ b/Api/GetModuleSupportInfoInterface.php @@ -0,0 +1,21 @@ +config = $config; + $this->routeConfig = $routeConfig; + $this->moduleDirReader = $moduleDirReader; + $this->domFactory = $domFactory; + $this->authSession = $authSession; + $this->resourceConnection = $resourceConnection; + $this->sectionFactory = $sectionFactory; + $this->messagePool = $messagePool; + $this->getModuleInfo = $getModuleInfo ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(GetModuleInfoInterface::class); + $this->getModuleVersion = $getModuleVersion ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magefan\Community\Api\GetModuleVersionInterface::class + ); + $this->getModuleSupportInfo = $getModuleSupportInfo ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + GetModuleSupportInfoInterface::class + ); + } + + /** + * @return array|string[] + */ + public function getNotificationData() + { + $data = $this->messagePool->getAll($this->getRequest()->getFullActionName()); + if ($data) { + return $data; + } + return [$this->getFormattedModuleName() => 'all']; + } + /** + * @return bool + */ + public function isEnabled() { + foreach ($this->_storeManager->getStores() as $store) { + $configPath = $this->getConfigSection() . '/' . 'g' . 'e' . 'n' . 'e' . 'r' . 'a' . 'l' . '/' . 'e' . 'n' . 'a' . 'b' . 'l' . 'e' . 'd'; + if ($this->config->getConfig($configPath, (int)$store->getId())) { + return true; + } + } + return false; + } + + /** + * @return array|false|\Magento\Framework\DataObject|mixed + */ + public function getModuleInfo() { + return $this->getFormattedModuleName() ? $this->getModuleInfo->execute($this->getFormattedModuleName()) : false; + } + + /** + * @return string + */ + private function getCurrentVersion() { + if (!$this->currentVersion) { + $moduleName = $this->getFormattedModuleName(); + $currentVersion = $this->getModuleVersion->execute($moduleName); + + foreach (['Extra', 'Plus'] as $_plan) { + if ($_currentVersion = $this->getModuleVersion->execute($moduleName . $_plan)) { + $currentVersion = $_currentVersion; + break; + } + } + + $this->currentVersion = $currentVersion; + } + return $this->currentVersion; + } + + /** + * @return string + */ + public function getLatestVersion() { + if (!$this->latestVersion) { + try { + $moduleInfo = $this->getModuleInfo(); + $this->latestVersion = $moduleInfo ? $moduleInfo->getVersion() : ''; + } catch (\Exception $e) { + $this->latestVersion = ''; + } + } + return $this->latestVersion; + } + + /** + * @return bool|int + */ + public function needToUpdate() { + return version_compare($this->getCurrentVersion(), $this->getLatestVersion(), '<'); + } + + /** + * @param $name + * @return void + */ + public function setExtensionName($name = null) { + if ($name) { + $this->extensionName = $name; + return; + } + $frontModule = $this->routeConfig->getModulesByFrontName($this->getRequest()->getModuleName()); + + if (!empty($frontModule[0]) && strpos($frontModule[0], 'Magefan_') !== false) { + $this->extensionName = $frontModule[0]; + } else { + $sectionName = (string)$this->getRequest()->getParam('section'); + $section = $this->sectionFactory->create(['name' => $sectionName]); + $this->extensionName = $section->getModuleName(); + } + } + + /** + * @return mixed|null + */ + private function getExtensionName() { + if (!$this->extensionName) { + $this->setExtensionName(); + } + return $this->extensionName; + } + + /** + * @return mixed|string|null + */ + private function getFormattedModuleName() { + $moduleName = $this->getExtensionName(); + $moduleName = str_starts_with($moduleName, 'Magefan_') ? $moduleName : 'Magefan_' . $moduleName; + return str_replace(['Extra', 'Plus'], '', $moduleName); + } + + /** + * @return null + */ + public function getConfigSection() { + + if ($this->getRequest()->getParam('section')) { + return $this->getRequest()->getParam('section'); + } + + $moduleName = $this->getFormattedModuleName(); + if (!$moduleName) { + return null; + } + + $configPath = $this->moduleDirReader->getModuleDir( + \Magento\Framework\Module\Dir::MODULE_ETC_DIR, + $moduleName + ) . '/ad' . 'min' . 'ht' . 'ml/' . 'sy' . 'st' . 'em' . '.x' . 'ml'; + + if (!file_exists($configPath)) { + return null; + } + + $dom = $this->domFactory->createDom(['xml' => file_get_contents($configPath)]); + $xpath = new \DOMXPath($dom->getDom()); + + foreach ($xpath->query('/config/system/section') as $sectionNode) { + return $sectionNode->getAttribute('id'); + } + + return null; + } + + /** + * @return bool + */ + public function canUpgradePlan() { + $maxPlan = $this->getModuleInfo()->getMaxPlan(); + $extensionName = $this->getFormattedModuleName(); + return $maxPlan && !$this->getModuleVersion->execute($extensionName. ucfirst($maxPlan)); + } + + /** + * @param string $event + * @param $allowedMessages + * @return bool + */ + public function allowShowMessage(string $event, string $allowedMessages) { + + if ($allowedMessages !== 'all') { + $allowedEvents = array_map('trim', explode(',', $allowedMessages)); + if (!in_array($event, $allowedEvents)) { + return false; + } + } + + $adminUser = $this->authSession->getUser(); + if (!$adminUser) { + return false; + } + + $connection = $this->resourceConnection->getConnection(); + $tableName = $connection->getTableName('mf_message_remind_later'); + + $select = $connection->select() + ->from($tableName) + ->where('user_id = ?', $adminUser->getId()) + ->where('event = ?', $event) + ->where('module_name = ?', str_replace(['Magento 2 ', ' Extension'], '', $this->getModuleInfo()->getProductName())) + ->where('created_at >= ?', (new \DateTime('-1 day'))->format('Y-m-d H:i:s')) + ->limit(1); + + return !$connection->fetchOne($select); + } + + + /** + * @return bool + */ + public function getSupportExpired() + { + if ($this->getFormattedModuleName() && $key = $this->config->getConfig($this->getConfigSection() . '/' . 'g' . 'e' . 'n' . 'e' . 'r' . 'a' . 'l' . '/' . 'k' . 'e' . 'y')) { + return !$this->getModuleSupportInfo->validSupport([ + 'key' => $key, + 'name' => explode('_', $this->getFormattedModuleName())[1] + ]); + } + return false; + } +} diff --git a/Controller/Adminhtml/RemindLater/Index.php b/Controller/Adminhtml/RemindLater/Index.php new file mode 100644 index 0000000..f55d463 --- /dev/null +++ b/Controller/Adminhtml/RemindLater/Index.php @@ -0,0 +1,96 @@ +authSession = $authSession; + $this->resource = $resource; + } + + /** + * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\Result\Json|(\Magento\Framework\Controller\Result\Json&\Magento\Framework\Controller\ResultInterface)|\Magento\Framework\Controller\ResultInterface + */ + public function execute() + { + try { + if (!$adminUser = $this->authSession->getUser()) { + throw new NoSuchEntityException(__('Admin User is not found.')); + } + if (!$event = $this->_request->getParam('event')) { + throw new NoSuchEntityException(__('Magento Event is not provided.')); + } + + if (!$moduleName = $this->_request->getParam('module')) { + throw new NoSuchEntityException(__('Magento Event is not provided.')); + } + + $connection = $this->resource->getConnection(); + $tableName = $connection->getTableName('mf_message_remind_later'); + + $data = [ + 'user_id' => $adminUser->getId(), + 'module_name' => $moduleName, + 'event' => $event + ]; + + $select = $connection->select() + ->from($tableName, ['id']) + ->where('user_id = ?', $adminUser->getId()) + ->where('module_name = ?', $moduleName) + ->where('event = ?', $event); + $exists = $connection->fetchOne($select); + + if ($exists) { + $connection->update( + $tableName, + ['created_at' => (new \DateTime())->format('Y-m-d H:i:s')], + [ + 'user_id = ?' => $adminUser->getId(), + 'module_name = ?' => $moduleName, + 'event = ?' => $event + ] + ); + } else { + $connection->insert($tableName, $data); + } + + $result = ['success' => true]; + } catch (\Exception $e) { + $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()]; + } + return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result); + } +} \ No newline at end of file diff --git a/Model/GetModuleSupportInfo.php b/Model/GetModuleSupportInfo.php new file mode 100644 index 0000000..9cb217e --- /dev/null +++ b/Model/GetModuleSupportInfo.php @@ -0,0 +1,114 @@ +curl = $curl; + $this->resource = $resource; + } + + /** + * @param $moduleData + * @return string|null + */ + private function loadFromCache($moduleData) + { + $connection = $this->resource->getConnection(); + $table = $this->resource->getTableName(self::CACHE_KEY); + + $select = $connection->select() + ->from($table, ['data']) + ->where('module_name LIKE ?', $moduleData['name']) + ->where('updated_at >= ?', date('Y-m-d H:i:s', strtotime(self::CACHE_LIFE_TIME))); + + $data = $connection->fetchOne($select); + if (!$data) { + try { + $url = 'htt' . 'ps' . ':' . '/'. '/'. 'ma' . 'g' . 'ef' . 'an' . '.' . 'co' + . 'm/' . 'm' . 'pk' . '/' . 'i' . 'nf' . 'o' . '/' . 'su' . 'pp' . 'or' . 't'; + $this->curl->post($url, ['key' => $moduleData['key']]); + $response = $this->curl->getBody(); + $responseData = json_decode($response, true); + if ($response && !isset($responseData['error'])) { + $this->updateCache($moduleData['name'], $response); + return $response; + } + } catch (\Exception $e) { + + } + } + + return $data; + } + + + /** + * @param string $moduleName + * @param string $response + * @return void + */ + private function updateCache(string $moduleName, string $response) + { + $connection = $this->resource->getConnection(); + $table = $this->resource->getTableName(self::CACHE_KEY); + $select = $connection->select() + ->from($table, ['id']) + ->where('module_name = ?', $moduleName); + + $exists = $connection->fetchOne($select); + + if ($exists) { + $connection->update($table, ['data' => $response], ['module_name = ?' => $moduleName]); + } else { + $connection->insert($table, ['module_name' => $moduleName, 'data' => $response]); + } + } + + /** + * @param array $moduleData + * @return bool + */ + public function validSupport(array $moduleData): bool + { + $moduleSupportInfo = $this->loadFromCache($moduleData); + + if ($moduleSupportInfo) { + $decodedData = json_decode($moduleSupportInfo, true); + return !empty($decodedData['data']) && $decodedData['data'] < date('Y-m-d H:i:s', strtotime('-1 year')); + } + + return false; + } +} diff --git a/Model/MessagePool.php b/Model/MessagePool.php new file mode 100644 index 0000000..0053b3b --- /dev/null +++ b/Model/MessagePool.php @@ -0,0 +1,37 @@ +messages = $messages; + } + + /** + * @param string $actionName + * @return array + */ + public function getAll(string $actionName):array + { + return $this->messages[$actionName] ?? []; + } +} \ No newline at end of file diff --git a/Observer/LayoutLoadBefore.php b/Observer/LayoutLoadBefore.php new file mode 100644 index 0000000..9391f07 --- /dev/null +++ b/Observer/LayoutLoadBefore.php @@ -0,0 +1,39 @@ +request = $request; + } + + /** + * @param \Magento\Framework\Event\Observer $observer + * @return \Magento\Framework\Event\Observer + */ + public function execute(\Magento\Framework\Event\Observer $observer) + { + $observer->getLayout()->getUpdate()->addHandle('notification'); + return $observer; + } +} \ No newline at end of file diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index ed54255..c232ff0 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -12,4 +12,15 @@ type="Magefan\Community\Plugin\Magento\Backend\Model\Menu\BuilderPlugin" /> + + + + + + + + diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml index bed8f62..8b96267 100644 --- a/etc/adminhtml/events.xml +++ b/etc/adminhtml/events.xml @@ -12,4 +12,7 @@ + + + \ No newline at end of file diff --git a/etc/db_schema.xml b/etc/db_schema.xml new file mode 100644 index 0000000..beccfad --- /dev/null +++ b/etc/db_schema.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+
diff --git a/etc/di.xml b/etc/di.xml index 721dfa0..633a30e 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,6 +8,7 @@ + diff --git a/view/adminhtml/layout/notification.xml b/view/adminhtml/layout/notification.xml new file mode 100644 index 0000000..4326356 --- /dev/null +++ b/view/adminhtml/layout/notification.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/view/adminhtml/templates/notification.phtml b/view/adminhtml/templates/notification.phtml new file mode 100644 index 0000000..c9ba684 --- /dev/null +++ b/view/adminhtml/templates/notification.phtml @@ -0,0 +1,137 @@ + + +getNotificationData(); + +if (!empty($data)) { + foreach ($data as $name => $messages) { + $this->setExtensionName($name); + if (!$block->getModuleInfo()->getData() || !$block->getLatestVersion()){ + continue; + } + + $moduleName = str_replace(['Magento 2 ', ' Extension'],'', $block->getModuleInfo()->getProductName()); +?> +
+ + isEnabled() && $block->allowShowMessage('enabled', $messages)) { ?> +
+
+
+ Stores > Configuration > Magefan Extensions > ' . $moduleName . '', + $block->getUrl( + 'adminhtml/system_config/edit', + ['section' => $block->getConfigSection()] + ) + ); + ?> +
+
+ +
+
+
+ + + needToUpdate() && $block->allowShowMessage('update', $messages) /*&& \Magefan\Community\Model\UrlChecker::showUrl($block->getUrl())*/) { ?> +
+
+
+ escapeHtml($block->getLatestVersion()) .', is now available, offering improved features and performance' + ); + ?> +
+
+ + +
+
+
+ + + + canUpgradePlan() && $block->allowShowMessage('upgrade', $messages) /*&& \Magefan\Community\Model\UrlChecker::showUrl($block->getUrl())*/) { ?> +
+
+
+ +
+
+ + +
+
+
+ + + getSupportExpired() && $block->allowShowMessage('support', $messages) /*&& \Magefan\Community\Model\UrlChecker::showUrl($block->getUrl())*/) { ?> +
+
+
+ +
+
+ + +
+
+
+ +
+ + messageBlock.style.display = 'none', 500); + + require(['jquery', 'domReady!'], function($){ + $.ajax({ + url:'" . $block->escapeHtml($block->getUrl('mfcommunity/remindlater/index')) ."', + type: 'post', + dataType: 'json', + data: { + event: event, + module: '" . $moduleName . "' + }, + success: function (data) { + console.log(data); + } + }); + }); + } + } +"; +?> + +renderTag('script', [], $script, false) ?> + diff --git a/view/adminhtml/web/css/source/_module.less b/view/adminhtml/web/css/source/_module.less index f2a8738..b71f411 100644 --- a/view/adminhtml/web/css/source/_module.less +++ b/view/adminhtml/web/css/source/_module.less @@ -125,3 +125,70 @@ .accordion .form-inline .config .magefan-section tr:nth-child(even) td { background-color: #f5f5f5; } + +// Message +.notices-wrapper { + .messages.mf-massages { + margin-top: 20px; + .message { + padding: 1.2rem 1.6rem 1.2rem 5rem; + margin-bottom: 20px; + .wrapper { + display: flex; + align-items: center; + justify-content: space-between; + .description { + display: flex; + flex-direction: column; + row-gap: 6px; + flex-grow: 1; + font-size: 1.4rem; + line-height: 2rem; + font-weight: 500; + } + } + &:before { + display: inline-block; + top: 50%; + transform: translateY(-50%); + margin: 0; + left: 1.6rem; + } + &.notice { + &:before { + content: ''; + width: 24px; + height: 24px; + background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiIgdmlld0JveD0iMCAwIDIyIDIyIiBmaWxsPSJub25lIj4NCiAgICA8cGF0aCBkPSJNMTEgMEM0LjkyMDMxIDAgMCA0LjkxOTc1IDAgMTFDMCAxNy4wNzk2IDQuOTE5NzUgMjIgMTEgMjJDMTcuMDc5NyAyMiAyMiAxNy4wODAyIDIyIDExQzIyIDQuOTIwMzkgMTcuMDgwMiAwIDExIDBaTTEyLjEyOTYgMTUuMzY2NUMxMi4xMjk2IDE1LjcxNDEgMTEuNjIyOCAxNi4wNjE1IDExLjAwMDIgMTYuMDYxNUMxMC4zNDg2IDE2LjA2MTUgOS44ODUzNSAxNS43MTQxIDkuODg1MzUgMTUuMzY2NVY5Ljg0OTlDOS44ODUzNSA5LjQ0NDQ5IDEwLjM0ODcgOS4xNjkzMiAxMS4wMDAyIDkuMTY5MzJDMTEuNjIyOCA5LjE2OTMyIDEyLjEyOTYgOS40NDQ0OSAxMi4xMjk2IDkuODQ5OVYxNS4zNjY1Wk0xMS4wMDAzIDcuODM3MzNDMTAuMzM0MiA3LjgzNzMzIDkuODEyOTkgNy4zNDUwNCA5LjgxMjk5IDYuNzk0NzhDOS44MTI5OSA2LjI0NDU2IDEwLjMzNDIgNS43NjY3NSAxMS4wMDAzIDUuNzY2NzVDMTEuNjUxOCA1Ljc2Njc1IDEyLjE3MzEgNi4yNDQ1NiAxMi4xNzMxIDYuNzk0NzhDMTIuMTczMSA3LjM0NTA0IDExLjY1MTggNy44MzczMyAxMS4wMDAzIDcuODM3MzNaIiBmaWxsPSIjRkZCRjAwIi8+DQo8L3N2Zz4=") no-repeat center center; + } + } + } + .actions { + //margin-left: auto; + button { + display: inline-flex; + justify-content: center; + align-items: center; + padding: 8px 16px; + border: none; + } + .action-update, + .action-upgrade { + color: #182230; + background: #FFCD36; + box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.05); + &:hover { + opacity: 0.7; + } + } + .action-remind-later { + color: #182230; + background: none; + border: none; + &:hover { + text-decoration: underline; + } + } + } + } +}