diff --git a/Api/Data/UpdateNotificationInterface.php b/Api/Data/UpdateNotificationInterface.php
new file mode 100644
index 0000000..02e1f6c
--- /dev/null
+++ b/Api/Data/UpdateNotificationInterface.php
@@ -0,0 +1,34 @@
+_scopeConfig = $context->getScopeConfig();
@@ -50,8 +65,14 @@ public function __construct(
$this->_resourceConfig = $resourceConfig;
$this->_cacheTypeList = $cacheTypeList;
$this->_cacheFrontendPool = $cacheFrontendPool;
- $cachedData = $this->_getCachedData();
- $this->accountInfo = $cachedData['accountInfo'];
+ $this->_serializer = $serializer;
+ $this->_dataHelper = $dataHelper;
+ $this->_updateNotifier = $updateNotifier;
+
+ $this->_cachedData = $this->_getCachedData();
+
+ $this->_notifyUpdate();
+
parent::__construct($context, $data);
}
@@ -78,8 +99,8 @@ public function render(AbstractElement $element): string
public function getConfig(): array
{
return [
- 'enabled' => $this->_storeConfigHelper->isSetFlag(StoreConfigHelper::PATH['enabled']),
- 'module_version' => $this->_storeConfigHelper->getValue(StoreConfigHelper::PATH['module_version']),
+ 'enabled' => $this->_storeConfigHelper->isEnabled(),
+ 'module_version' => $this->_storeConfigHelper->getModuleVersion(),
'supported_countries' => $this->_storeConfigHelper->getSupportedCountries(),
'account_name' => $this->_storeConfigHelper->getValue(StoreConfigHelper::PATH['account_name']),
'account_status' => $this->_storeConfigHelper->getValue(StoreConfigHelper::PATH['account_status']), // Defaults to "new", see etc/config.xml.
@@ -87,6 +108,26 @@ public function getConfig(): array
];
}
+ /**
+ * Get cached account info.
+ *
+ * @return array
+ */
+ public function getAccountInfo(): array
+ {
+ return $this->_cachedData['accountInfo'] ?? [];
+ }
+
+ /**
+ * Get cached module info.
+ *
+ * @return array
+ */
+ public function getModuleInfo(): array
+ {
+ return $this->_cachedData['moduleInfo'] ?? [];
+ }
+
/**
* Get short description of API status.
*
@@ -110,6 +151,27 @@ public function getApiStatusDescription(): string
}
}
+ /**
+ * Get cached data.
+ *
+ * @return array
+ */
+ private function _getCachedData(): array
+ {
+ $cache = $this->_cacheFrontendPool->get(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER);
+ $cachedData = $cache->load(self::CACHE_ID);
+
+ if ($cachedData === false) {
+ $data = [];
+ $data['accountInfo'] = $this->_getAccountInfo();
+ $data['moduleInfo'] = $this->_dataHelper->getModuleInfo();
+ $cache->save($this->_serializer->serialize($data), self::CACHE_ID, [], self::CACHE_LIFETIME_SECONDS);
+ return $data;
+ }
+
+ return $this->_serializer->unserialize($cachedData);
+ }
+
/**
* Get Postcode.eu API account info.
*
@@ -118,30 +180,21 @@ public function getApiStatusDescription(): string
private function _getAccountInfo(): array
{
$status = $this->_storeConfigHelper->getValue(StoreConfigHelper::PATH['account_status']);
- if ($status === \Flekto\Postcode\Helper\ApiClientHelper::API_ACCOUNT_STATUS_ACTIVE)
+ if ($status === \Flekto\Postcode\Helper\ApiClientHelper::API_ACCOUNT_STATUS_ACTIVE) {
return $this->_apiClientHelper->getApiClient()->accountInfo();
+ }
return [];
}
/**
- * Get cached data.
- *
- * @return array
+ * Set a notification if an update is available.
*/
- private function _getCachedData(): array
+ private function _notifyUpdate(): void
{
- $cache = $this->_cacheFrontendPool->get(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER);
- $cachedData = $cache->load(self::CACHE_ID);
-
- if ($cachedData === false)
- {
- $data = [];
- $data['accountInfo'] = $this->_getAccountInfo();
- $cache->save(serialize($data), self::CACHE_ID, [], self::CACHE_LIFETIME_SECONDS);
- return $data;
+ $moduleInfo = $this->getModuleInfo();
+ if ($moduleInfo['has_update'] ?? false) {
+ $this->_updateNotifier->notifyVersion($moduleInfo['latest_version']);
}
-
- return unserialize($cachedData);
}
}
diff --git a/Cron/NotifyModuleUpdate.php b/Cron/NotifyModuleUpdate.php
new file mode 100644
index 0000000..3f67fb8
--- /dev/null
+++ b/Cron/NotifyModuleUpdate.php
@@ -0,0 +1,49 @@
+_logger = $logger;
+ $this->_dataHelper = $dataHelper;
+ $this->_updateNotifier = $updateNotifier;
+ }
+
+ /**
+ * Run cron job.
+ *
+ * @access public
+ * @return void
+ */
+ public function execute(): void
+ {
+ $moduleInfo = $this->_dataHelper->getModuleInfo();
+ if (($moduleInfo['has_update'] ?? false)
+ && $this->_updateNotifier->notifyVersion($moduleInfo['latest_version'])
+ ) {
+ $this->_logger->info(__('Added notification for Postcode.eu Address API %1 update.', $moduleInfo['latest_version']));
+ }
+ }
+}
diff --git a/Cron/UpdateApiData.php b/Cron/UpdateApiData.php
index 0630177..fd87282 100644
--- a/Cron/UpdateApiData.php
+++ b/Cron/UpdateApiData.php
@@ -15,7 +15,7 @@ class UpdateApiData
protected $_storeConfigHelper;
/**
- * __construct function.
+ * Constructor
*
* @access public
* @param LoggerInterface $logger
diff --git a/Helper/Data.php b/Helper/Data.php
index 933c56a..abcd63f 100644
--- a/Helper/Data.php
+++ b/Helper/Data.php
@@ -7,27 +7,58 @@
use Flekto\Postcode\Model\Config\Source\ShowHideAddressFields;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Filesystem\DirectoryList;
+use Magento\Framework\Filesystem\DriverInterface;
+use Magento\Framework\HTTP\Client\Curl;
class Data extends AbstractHelper
{
+ public const MODULE_RELEASE_URL = 'https://github.com/postcode-nl/PostcodeNl_Api_Magento2/releases/latest';
+ public const PACKAGIST_URL = 'https://repo.packagist.org/p2/postcode-nl/api-magento2-module.json';
+
/**
* @var StoreConfigHelper
*/
private $_storeConfigHelper;
+ /**
+ * @var DirectoryList
+ */
+ private $_dir;
+
+ /**
+ * @var DriverInterface
+ */
+ private $_fs;
+
+ /**
+ * @var Curl
+ */
+ private $_curl;
+
/**
* Constructor
*
* @access public
* @param Context $context
* @param StoreConfigHelper $storeConfigHelper
+ * @param DirectoryList $dir
+ * @param DriverInterface $filesystem
+ * @param Curl $curl
* @return void
*/
public function __construct(
Context $context,
- StoreConfigHelper $storeConfigHelper
+ StoreConfigHelper $storeConfigHelper,
+ DirectoryList $dir,
+ DriverInterface $filesystem,
+ Curl $curl,
) {
$this->_storeConfigHelper = $storeConfigHelper;
+ $this->_dir = $dir;
+ $this->_fs = $filesystem;
+ $this->_curl = $curl;
parent::__construct($context);
}
@@ -37,7 +68,7 @@ public function __construct(
* @access public
* @return bool
*/
- public function isFormattedOutputDisabled()
+ public function isFormattedOutputDisabled(): bool
{
return
$this->isDisabled()
@@ -50,7 +81,7 @@ public function isFormattedOutputDisabled()
* @access public
* @return bool
*/
- public function isNlComponentDisabled()
+ public function isNlComponentDisabled(): bool
{
return
$this->isDisabled()
@@ -64,7 +95,7 @@ public function isNlComponentDisabled()
* @access public
* @return bool
*/
- public function isDisabled()
+ public function isDisabled(): bool
{
return
false === $this->_storeConfigHelper->isSetFlag(StoreConfigHelper::PATH['enabled'])
@@ -77,11 +108,94 @@ public function isDisabled()
* @access public
* @return bool
*/
- public function isAutofillBypassDisabled()
+ public function isAutofillBypassDisabled(): bool
{
return
$this->isDisabled()
|| ShowHideAddressFields::SHOW == $this->_storeConfigHelper->getValue(StoreConfigHelper::PATH['show_hide_address_fields'])
|| $this->_storeConfigHelper->isSetFlag(StoreConfigHelper::PATH['allow_autofill_bypass']) === false;
}
+
+ /**
+ * Get module info.
+ *
+ * @return array
+ */
+ public function getModuleInfo(): array
+ {
+ $version = $this->_storeConfigHelper->getModuleVersion();
+
+ try {
+ $data = $this->_getPackageData();
+ $latest_version = $data['packages']['postcode-nl/api-magento2-module'][0]['version'];
+ } catch (LocalizedException $e) {
+ $this->_logger->error(__('Failed to get package data: "%1".', $e->getMessage()));
+ $latest_version = $version;
+ }
+
+ return [
+ 'version' => $version,
+ 'latest_version' => $latest_version,
+ 'has_update' => version_compare($latest_version, $version, '>'),
+ 'release_url' => $this->getModuleReleaseUrl(),
+ ];
+ }
+
+ /**
+ * Request module info from Packagist.
+ *
+ * Will only download from Packagist if their file is newer.
+ *
+ * @throws LocalizedException
+ * @return array - Decoded JSON data.
+ */
+ private function _getPackageData(): array
+ {
+ $path = $this->_dir->getPath('var') . '/Flekto_Postcode';
+ if (!$this->_fs->isDirectory($path)) {
+ $this->_fs->createDirectory($path, 0755);
+ }
+
+ $filePath = $path . '/package-data.json';
+ if ($this->_fs->isExists($filePath)) {
+ $lastModified = $this->_fs->stat($filePath)['mtime'];
+
+ if ($lastModified !== false) {
+ $this->_curl->setHeaders(['If-Modified-Since' => gmdate('D, d M Y H:i:s T', $lastModified)]);
+ }
+ }
+
+ $this->_curl->get(self::PACKAGIST_URL);
+ $status = $this->_curl->getStatus();
+ if ($status == 200) {
+ $response = $this->_curl->getBody();
+
+ if ($this->_fs->filePutContents($filePath, $response) === false) {
+ throw new LocalizedException(__('Failed to write package data to %1.', $filePath));
+ }
+
+ return json_decode($response, true);
+
+ } elseif ($status == 304) { // Not modified, use cached file.
+ $data = $this->_fs->fileGetContents($filePath);
+
+ if ($data === false) {
+ throw new LocalizedException(__('Failed to read package data from %1.', $filePath));
+ }
+
+ return json_decode($data, true);
+ }
+
+ throw new LocalizedException(__('Unexpected status code %1 while fetching package data.', $status));
+ }
+
+ /**
+ * Get URL to the latest version of the module.
+ *
+ * @return string
+ */
+ public function getModuleReleaseUrl(): string
+ {
+ return self::MODULE_RELEASE_URL;
+ }
}
diff --git a/Helper/ModuleHelper.php b/Helper/ModuleHelper.php
new file mode 100644
index 0000000..e69de29
diff --git a/Helper/StoreConfigHelper.php b/Helper/StoreConfigHelper.php
index 8c92fd2..93e65e0 100644
--- a/Helper/StoreConfigHelper.php
+++ b/Helper/StoreConfigHelper.php
@@ -105,4 +105,15 @@ public function hasCredentials(): bool
return isset($key, $secret);
}
+
+ /**
+ * Get current module version.
+ *
+ * @access public
+ * @return string
+ */
+ public function getModuleVersion(): string
+ {
+ return $this->getValue(static::PATH['module_version']);
+ }
}
diff --git a/Model/ResourceModel/UpdateNotification.php b/Model/ResourceModel/UpdateNotification.php
new file mode 100644
index 0000000..542e35c
--- /dev/null
+++ b/Model/ResourceModel/UpdateNotification.php
@@ -0,0 +1,16 @@
+_init(self::MAIN_TABLE, self::ID_FIELD);
+ }
+}
diff --git a/Model/ResourceModel/UpdateNotification/Collection.php b/Model/ResourceModel/UpdateNotification/Collection.php
new file mode 100644
index 0000000..39a136a
--- /dev/null
+++ b/Model/ResourceModel/UpdateNotification/Collection.php
@@ -0,0 +1,18 @@
+_init(
+ UpdateNotification::class,
+ UpdateNotificationResourceModel::class
+ );
+ }
+}
diff --git a/Model/UpdateNotification.php b/Model/UpdateNotification.php
new file mode 100644
index 0000000..b2d8ce4
--- /dev/null
+++ b/Model/UpdateNotification.php
@@ -0,0 +1,34 @@
+_init(ResourceModel\UpdateNotification::class);
+ }
+
+ public function getVersion(): string
+ {
+ return $this->getData(self::VERSION);
+ }
+
+ public function setVersion(string $version): UpdateNotification
+ {
+ return $this->setData(self::VERSION, $version);
+ }
+
+ public function getNotified(): bool
+ {
+ return $this->getData(self::NOTIFIED);
+ }
+
+ public function setNotified(bool $notified): UpdateNotification
+ {
+ return $this->setData(self::NOTIFIED, $notified);
+ }
+}
diff --git a/Model/UpdateNotification/UpdateNotifier.php b/Model/UpdateNotification/UpdateNotifier.php
new file mode 100644
index 0000000..ac95c33
--- /dev/null
+++ b/Model/UpdateNotification/UpdateNotifier.php
@@ -0,0 +1,52 @@
+_notifier = $notifier;
+ $this->_updateNotification = $updateNotification;
+ }
+
+ /**
+ * Notifies about a new version.
+ *
+ * @param string $version
+ * @return bool - True if notified about a new version, false otherwise.
+ */
+ public function notifyVersion(string $version): bool
+ {
+ if ($this->_updateNotification->isVersionNotified($version)) {
+ return false;
+ }
+
+ $this->_notifier->addNotice(
+ __('Postcode.eu Address API update available'),
+ __('Stay ahead with our latest update.
+ Get the newest features and improvements for our Postcode.eu address validation module.'),
+ \Flekto\Postcode\Helper\Data::MODULE_RELEASE_URL
+ );
+ $this->_updateNotification->setVersionNotified($version);
+ return $this->_updateNotification->isVersionNotified($version);
+ }
+}
diff --git a/Model/UpdateNotificationRepository.php b/Model/UpdateNotificationRepository.php
new file mode 100644
index 0000000..e7b5174
--- /dev/null
+++ b/Model/UpdateNotificationRepository.php
@@ -0,0 +1,71 @@
+_resource = $resource;
+ $this->_notificationFactory = $notificationFactory;
+ }
+
+ public function getByVersion(string $version): UpdateNotificationInterface
+ {
+ $notification = $this->_notificationFactory->create();
+ $this->_resource->load($notification, $version, 'version');
+
+ if (!$notification->getId()) {
+ throw new NoSuchEntityException(__('Version "%1" not found', $version));
+ }
+
+ return $notification;
+ }
+
+ public function save(UpdateNotificationInterface $notification): UpdateNotificationInterface
+ {
+ try {
+ $this->_resource->save($notification);
+ } catch (\Exception $e) {
+ throw new CouldNotSaveException(__($e->getMessage()));
+ }
+
+ return $notification;
+ }
+
+ public function setVersionNotified(string $version): void
+ {
+ try {
+ $notification = $this->getByVersion($version);
+ } catch (NoSuchEntityException $e) {
+ $notification = $this->_notificationFactory->create();
+ }
+
+ $notification->setVersion($version);
+ $notification->setNotified(true);
+ $this->_resource->save($notification);
+ }
+
+ public function isVersionNotified(string $version): bool
+ {
+ try {
+ $notification = $this->getByVersion($version);
+ return $notification->getNotified();
+ } catch (NoSuchEntityException $e) {
+ return false;
+ }
+ }
+}
diff --git a/Observer/System/Config.php b/Observer/System/Config.php
index fba6083..8bfb0b7 100644
--- a/Observer/System/Config.php
+++ b/Observer/System/Config.php
@@ -32,6 +32,7 @@ class Config implements ObserverInterface
* @param CacheFrontendPool $cacheFrontendPool
* @param ApiClientHelper $apiClientHelper
* @param StoreConfigHelper $storeConfigHelper
+ * @param RequestInterface $request
* @return void
*/
public function __construct(
@@ -123,6 +124,7 @@ public function execute(Observer $observer): void
/**
* Clean config cache.
+ *
* @return void
*/
protected function _cleanConfigCache(): void
diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml
index ef8c61d..ce566c1 100644
--- a/etc/adminhtml/di.xml
+++ b/etc/adminhtml/di.xml
@@ -1,5 +1,4 @@
-
diff --git a/etc/config.xml b/etc/config.xml
index 467dc38..8bafb93 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -3,7 +3,7 @@
- 3.1.10
+ 3.1.11
new
diff --git a/etc/crontab.xml b/etc/crontab.xml
index a948717..9144a4a 100644
--- a/etc/crontab.xml
+++ b/etc/crontab.xml
@@ -4,5 +4,8 @@
3 3 * * *
+
+ 3 3 * * *
+
diff --git a/etc/db_schema.xml b/etc/db_schema.xml
new file mode 100644
index 0000000..2be74f0
--- /dev/null
+++ b/etc/db_schema.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/etc/di.xml b/etc/di.xml
index 6f78329..63e31b7 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -1,6 +1,9 @@
+
+
+
diff --git a/etc/module.xml b/etc/module.xml
index 26ad5ed..87d584a 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/view/adminhtml/templates/system/config/status.phtml b/view/adminhtml/templates/system/config/status.phtml
index 5a5167c..6a6da43 100644
--- a/view/adminhtml/templates/system/config/status.phtml
+++ b/view/adminhtml/templates/system/config/status.phtml
@@ -1,6 +1,8 @@
getConfig();
+$accountInfo = $block->getAccountInfo();
+$moduleInfo = $block->getModuleInfo();
?>