From 5b9493904ee33e5865b050b6f295ddf0574333dc Mon Sep 17 00:00:00 2001 From: bb Date: Thu, 26 Mar 2026 19:35:03 +0200 Subject: [PATCH] 12850-Support-Updates-Renewal-Admin-Notifications-v2 --- Block/Adminhtml/Info.php | 290 ++++++++++++++++++++++++++++ etc/adminhtml/di.xml | 11 ++ view/adminhtml/layout/default.xml | 4 + view/adminhtml/templates/info.phtml | 159 +++++++++++++++ 4 files changed, 464 insertions(+) create mode 100644 Block/Adminhtml/Info.php create mode 100644 view/adminhtml/templates/info.phtml diff --git a/Block/Adminhtml/Info.php b/Block/Adminhtml/Info.php new file mode 100644 index 0000000..469d98a --- /dev/null +++ b/Block/Adminhtml/Info.php @@ -0,0 +1,290 @@ + 'Magefan_ModuleName'] + * + * @var array + */ + private $fullActionModuleMap; + + /** + * @param Context $context + * @param RouteConfigInterface $routeConfig + * @param ModuleListInterface $moduleList + * @param array $data + * @param GetModuleVersionInterface|null $getModuleVersion + * @param SecureHtmlRendererInterface|null $mfSecureRenderer + * @param GetModuleInfoInterface|null $getModuleInfo + * @param array $fullActionModuleMap + */ + public function __construct( + Context $context, + RouteConfigInterface $routeConfig, + ModuleListInterface $moduleList, + array $data = [], + ?GetModuleVersionInterface $getModuleVersion = null, + ?SecureHtmlRendererInterface $mfSecureRenderer = null, + ?GetModuleInfoInterface $getModuleInfo = null, + array $fullActionModuleMap = [] + ) { + parent::__construct($context, $data); + $this->routeConfig = $routeConfig; + $this->moduleList = $moduleList; + $this->fullActionModuleMap = $fullActionModuleMap; + $this->getModuleVersion = $getModuleVersion ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + GetModuleVersionInterface::class + ); + $this->mfSecureRenderer = $mfSecureRenderer ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(SecureHtmlRendererInterface::class); + $this->getModuleInfo = $getModuleInfo ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(GetModuleInfoInterface::class); + } + + /** + * Set default template + */ + protected function _construct() + { + parent::_construct(); + $this->setTemplate('Magefan_Community::info.phtml'); + } + + /** + * Resolve the Magefan module name for the current admin page. + * + * Priority: + * 1. module_name data argument (set explicitly via layout XML) + * 2. Route-based detection (extensions with their own admin route) + * 3. Full-action map (extensions that enhance native Magento pages) + * + * @return string e.g. "Magefan_Blog", or empty string if not a Magefan page + */ + public function getModuleName(): string + { + $moduleName = $this->getData('module_name'); + if ($moduleName) { + return (string)$moduleName; + } + + $request = $this->getRequest(); + $frontName = $request->getRouteName(); + + if ($frontName) { + $modules = $this->routeConfig->getModulesByFrontName($frontName, 'adminhtml'); + foreach ($modules as $module) { + if (strpos($module, 'Magefan_') === 0) { + return $module; + } + } + } + + $fullAction = $request->getFullActionName(); + if ($fullAction && isset($this->fullActionModuleMap[$fullAction])) { + return $this->fullActionModuleMap[$fullAction]; + } + + return ''; + } + + /** + * Get the module info DataObject from the remote API + * + * @return DataObject + */ + public function getModuleInfo(): DataObject + { + return $this->getModuleInfo->execute($this->getModuleName()); + } + + /** + * Human-readable module title, e.g. "Blog Extension", "Better Order Grid Extension" + * + * @return string + */ + public function getModuleTitle(): string + { + $productName = $this->getModuleInfo()->getProductName(); + if ($productName) { + return trim(str_replace(['Magento 2 ', 'Magento '], '', (string)$productName)); + } + $parts = explode('_', $this->getModuleName()); + return isset($parts[1]) ? ucwords(str_replace('_', ' ', $parts[1])) . ' Extension' : ''; + } + + /** + * Get the product URL for this module + * + * @return string + */ + public function getModuleUrl(): string + { + return (string)($this->getModuleInfo()->getProductUrl() ?: 'https://magefan.com/'); + } + + /** + * Currently installed version (uses Plus/Extra variant if active) + * + * @return string + */ + public function getCurrentVersion(): string + { + $moduleName = $this->getModuleName(); + foreach (['Extra', 'Plus'] as $plan) { + if ($v = $this->getModuleVersion->execute($moduleName . $plan)) { + return $v; + } + } + return $this->getModuleVersion->execute($moduleName); + } + + /** + * Latest available version from remote + * + * @return string + */ + public function getLatestVersion(): string + { + return (string)$this->getModuleInfo()->getVersion(); + } + + /** + * Whether the extension is enabled, using its own Model/Config::isEnabled(). + * Auto-resolves Magefan\{ShortName}\Model\Config from the module name. + * Returns true if the Config class does not exist (no enable toggle). + * + * @return bool + */ + public function isExtensionEnabled(): bool + { + $parts = explode('_', $this->getModuleName()); + if (!isset($parts[1])) { + return true; + } + $configClass = 'Magefan\\' . $parts[1] . '\\Model\\Config'; + if (!class_exists($configClass)) { + return true; + } + try { + $config = \Magento\Framework\App\ObjectManager::getInstance()->get($configClass); + return $config->isEnabled(); + } catch (\Exception $e) { + return true; + } + } + + /** + * Whether a newer version is available + * + * @return bool + */ + public function needToUpdate(): bool + { + $latest = $this->getLatestVersion(); + $current = $this->getCurrentVersion(); + return $latest && $current && version_compare($latest, $current) > 0; + } + + /** + * Whether a plan upgrade is available (e.g. free → Plus/Extra) + * + * @return bool + */ + public function canUpgradeToMaxPlan(): bool + { + $maxPlan = $this->getModuleInfo()->getMaxPlan(); + if (!$maxPlan) { + return false; + } + return !$this->getModuleVersion->execute($this->getModuleName() . ucfirst($maxPlan)); + } + + /** + * Expose secure HTML renderer to the template + * + * @return SecureHtmlRendererInterface + */ + public function getMfSecureRenderer(): SecureHtmlRendererInterface + { + return $this->mfSecureRenderer; + } + + /** + * Only render on Magefan extension pages where the module is installed. + * For own-route extensions: restricted to index/grid actions. + * For native-page extensions: shown on any action listed in the map. + * + * @return string + */ + protected function _toHtml(): string + { + $moduleName = $this->getModuleName(); + if (!$moduleName) { + return ''; + } + + $fullAction = $this->getRequest()->getFullActionName(); + $isMappedAction = isset($this->fullActionModuleMap[$fullAction]); + + if (!$isMappedAction && $this->getRequest()->getActionName() !== 'index') { + return ''; + } + + if (!$this->getModuleVersion->execute($moduleName)) { + return ''; + } + + if (!$this->isExtensionEnabled()) { + return ''; + } + + return parent::_toHtml(); + } +} diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index ed54255..bdbb4d1 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -12,4 +12,15 @@ type="Magefan\Community\Plugin\Magento\Backend\Model\Menu\BuilderPlugin" /> + + + + + + Magefan_XmlSitemap + Magefan_BetterOrderGrid + Magefan_ProductGridInline + + + diff --git a/view/adminhtml/layout/default.xml b/view/adminhtml/layout/default.xml index 0d2e6ed..2eda48f 100644 --- a/view/adminhtml/layout/default.xml +++ b/view/adminhtml/layout/default.xml @@ -15,5 +15,9 @@ + + + + diff --git a/view/adminhtml/templates/info.phtml b/view/adminhtml/templates/info.phtml new file mode 100644 index 0000000..f04e3e4 --- /dev/null +++ b/view/adminhtml/templates/info.phtml @@ -0,0 +1,159 @@ +getMfSecureRenderer(); +$moduleTitle = $block->getModuleTitle(); +$needToUpdate = $block->needToUpdate(); +$canUpgradePlan = $block->canUpgradeToMaxPlan(); +$latestVersion = $block->getLatestVersion(); +$moduleUrl = $block->getModuleUrl(); +$utmParam = '?utm_source=admin&utm_medium=grid'; + +if (!$needToUpdate && !$canUpgradePlan) { + return; +} +?> + +
+ +
+
+
+ escapeHtml($moduleTitle) ?> + escapeHtml($latestVersion) ?> +
+
+ + +
+
+
+ + +
+
+
+ escapeHtml($moduleTitle), + strtoupper((string)$block->getModuleInfo()->getMaxPlan()) + ) ?> +
+
+ + +
+
+
+ +
+escapeJs($moduleTitle); +$remindLaterUrl = $escaper->escapeJs($block->escapeUrl($block->getUrl('mfcommunity/remindlater/index'))); +$script = <<renderTag('script', [], $script, false); +?>