559 lines
16 KiB
PHP
Executable File
559 lines
16 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* @author Olegnax
|
|
* @package Olegnax_Core
|
|
* @copyright Copyright (c) 2023 Olegnax (http://olegnax.com/). All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
|
|
namespace Olegnax\Core\Model;
|
|
|
|
use Exception;
|
|
use Magento\AdminNotification\Model\InboxFactory;
|
|
use Magento\Framework\App\DeploymentConfig;
|
|
use Magento\Framework\App\ObjectManager;
|
|
use Magento\Framework\App\ProductMetadataInterface;
|
|
use Magento\Framework\Component\ComponentRegistrar;
|
|
use Magento\Framework\Component\ComponentRegistrarInterface;
|
|
use Magento\Framework\Config\ConfigOptionsListConstants;
|
|
use Magento\Framework\DataObject;
|
|
use Magento\Framework\Filesystem\Directory\ReadFactory;
|
|
use Magento\Framework\HTTP\Adapter\CurlFactory;
|
|
use Magento\Framework\HTTP\AsyncClient\Request;
|
|
use Magento\Framework\Model\AbstractModel;
|
|
use Magento\Framework\Module\ModuleListInterface;
|
|
use Magento\Framework\Notification\MessageInterface;
|
|
use Magento\Store\Model\StoreManagerInterface;
|
|
use Olegnax\Core\Helper\Helper;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\ExistsFactory;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\ExpiredFactory;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\OxContent;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\OxContentP;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\OXFactory;
|
|
use Olegnax\Core\Model\ResourceModel\Inbox\Collection\OxUpdate;
|
|
use SimpleXMLElement;
|
|
|
|
class Feed extends AbstractModel
|
|
{
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
const FEED_URL = "olegnax.com/extras/products-status/feed.xml";
|
|
/**
|
|
* @var integer
|
|
*/
|
|
const FREQUENCY = 24;
|
|
/**
|
|
* @var integer
|
|
*/
|
|
const REMOVE_FREQUENCY = 6;
|
|
|
|
/**
|
|
*
|
|
* @var ObjectManager
|
|
*/
|
|
public $_objectManager;
|
|
/**
|
|
* Feed url
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_feedUrl;
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function checkUpdate()
|
|
{
|
|
if ($this->getFrequency() + $this->getLastUpdate() > time()) {
|
|
return $this;
|
|
}
|
|
|
|
$feedData = [];
|
|
|
|
$feedXml = $this->getFeedXml();
|
|
|
|
$installDate = strtotime($this->_loadObject(DeploymentConfig::class)->get(ConfigOptionsListConstants::CONFIG_PATH_INSTALL_DATE));
|
|
$types = [];
|
|
|
|
if ($feedXml && $feedXml->channel && $feedXml->channel->item) {
|
|
foreach ($feedXml->channel->item as $item) {
|
|
$severity = isset($item->severity) ? (int)$item->severity : MessageInterface::SEVERITY_NOTICE;
|
|
$type = isset($item->type) ? $this->escapeString($item->type) : null;
|
|
$extensionUpdate = $this->validateExtNeedUpdate($item->extensionUpdate, 'Olegnax');
|
|
$validate = [];
|
|
if ($extensionUpdate) {
|
|
$type = OxUpdate::TYPE_UPDATE;
|
|
$validate = [
|
|
'extension_update' => $extensionUpdate,
|
|
];
|
|
}
|
|
$pubDate = strtotime((string)$item->pubDate);
|
|
$expirationDate = (string)$item->expirationDate ? strtotime((string)$item->expirationDate) : null;
|
|
|
|
if ($installDate > $pubDate) {
|
|
continue;
|
|
}
|
|
if ($expirationDate && $expirationDate < gmdate('U')) {
|
|
continue;
|
|
}
|
|
if ($item->extension_update && !$extensionUpdate) {
|
|
continue;
|
|
}
|
|
if (!$type && MessageInterface::SEVERITY_NOTICE > $severity) {
|
|
continue;
|
|
}
|
|
if (!$this->validateExtInstalled($item->extension, 'Olegnax')) {
|
|
continue;
|
|
}
|
|
if (!$this->validateExtNotInstalled($item->extensionNot, 'Olegnax')) {
|
|
continue;
|
|
}
|
|
if (!$this->validateExtInstalled($item->extension3D)) {
|
|
continue;
|
|
}
|
|
|
|
if ($type) {
|
|
if (OxContent::TYPE_CONTENT > $severity) {
|
|
$severity = OxContent::TYPE_CONTENT;
|
|
}
|
|
$types[] = $type;
|
|
} else {
|
|
if (OxContent::TYPE_CONTENT == $severity || OxContentP::TYPE_CONTENT == $severity) {
|
|
$type = 'global';
|
|
|
|
$types[] = $type;
|
|
}
|
|
}
|
|
$_feedData = [
|
|
'severity' => $severity,
|
|
'date_added' => date('Y-m-d H:i:s', $pubDate),
|
|
'date_expire' => $expirationDate,
|
|
'title' => $this->escapeString($item->title),
|
|
'description' => $this->escapeString($item->description),
|
|
'url' => $this->escapeString($item->link),
|
|
'ox_content' => (string)$item->content,
|
|
'ox_image' => (string)$item->image,
|
|
'ox_type' => $type,
|
|
'ox_validate' => json_encode($validate),
|
|
'isOX' => 1,
|
|
];
|
|
|
|
if ($type && !in_array($type, ['update'])) {
|
|
$_feedData['is_read'] = 1;
|
|
$_feedData['is_remove'] = 1;
|
|
}
|
|
|
|
$feedData[] = $_feedData;
|
|
}
|
|
|
|
if ($feedData) {
|
|
if (!empty($types)) {
|
|
$this->removeOXType($types);
|
|
}
|
|
$this->_loadObject(InboxFactory::class)->create()->parse(array_reverse($feedData));
|
|
}
|
|
}
|
|
$this->setLastUpdate();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getFrequency()
|
|
{
|
|
return self::FREQUENCY * 3600;
|
|
}
|
|
|
|
/**
|
|
* @return Helper
|
|
*/
|
|
protected function helper(){
|
|
return $this->_loadObject( Helper::class);
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getLastUpdate()
|
|
{
|
|
return (int)$this->helper()->getModuleConfig('admin_notifications/lastcheck');
|
|
}
|
|
|
|
/**
|
|
* @return SimpleXMLElement
|
|
*/
|
|
public function getFeedXml()
|
|
{
|
|
try {
|
|
$data = $this->getFeedData();
|
|
$xml = new SimpleXMLElement($data);
|
|
} catch (Exception $e) {
|
|
$xml = new SimpleXMLElement('<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"></rss>');
|
|
}
|
|
|
|
return $xml;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getFeedData()
|
|
{
|
|
$curl = $this->_loadObject(CurlFactory::class)->create();
|
|
$curl->setConfig(
|
|
[
|
|
'timeout' => 2,
|
|
'maxredirects' => 5,
|
|
'useragent' => $this->getUserAgent(),
|
|
'referer' => $this->getCurrentUrl(),
|
|
'verifypeer' => false,
|
|
'verifyhost' => false,
|
|
]
|
|
);
|
|
$curl->write( Request::METHOD_GET, $this->getFeedUrl(), '1.0');
|
|
$data = $curl->read();
|
|
if ($data === false || $data === '') {
|
|
return '';
|
|
}
|
|
$data = preg_split('/^\r?$/m', $data, 2);
|
|
$data = trim($data[1]);
|
|
$curl->close();
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* @param string $object
|
|
* @return mixed
|
|
*/
|
|
protected function _loadObject($object)
|
|
{
|
|
return $this->_getObjectManager()->get($object);
|
|
}
|
|
|
|
/**
|
|
* @return ObjectManager
|
|
*/
|
|
protected function _getObjectManager()
|
|
{
|
|
if (!$this->_objectManager) {
|
|
$this->_objectManager = ObjectManager::getInstance();
|
|
}
|
|
|
|
return $this->_objectManager;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
protected function getUserAgent()
|
|
{
|
|
return sprintf("Olegnax: %s/%s (%s)", $this->getProductMetadata()->getName(),
|
|
$this->getProductMetadata()->getVersion(), $this->getProductMetadata()->getEdition());
|
|
}
|
|
|
|
/**
|
|
* @return ProductMetadataInterface
|
|
*/
|
|
protected function getProductMetadata()
|
|
{
|
|
return $this->_loadObject(ProductMetadataInterface::class);
|
|
}
|
|
|
|
/**
|
|
* @return StoreManagerInterface
|
|
*/
|
|
public function getCurrentUrl()
|
|
{
|
|
return $this->_loadObject(StoreManagerInterface::class)->getStore()->getBaseUrl();
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getFeedUrl()
|
|
{
|
|
$httpPath = $this->getScheme();
|
|
if ($this->_feedUrl === null) {
|
|
$this->_feedUrl = $httpPath . self::FEED_URL;
|
|
}
|
|
return $this->_feedUrl;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
private function getScheme()
|
|
{
|
|
return 'https://';
|
|
// $url = $this->_loadObject(StoreManagerInterface::class)->getStore()->getBaseUrl();
|
|
// $scheme = parse_url($url, PHP_URL_SCHEME);
|
|
// if (empty($scheme)) {
|
|
// $scheme = 'http';
|
|
// }
|
|
//
|
|
// return $scheme . '://';
|
|
}
|
|
|
|
/**
|
|
* @param SimpleXMLElement $data
|
|
* @return string
|
|
*/
|
|
private function escapeString(SimpleXMLElement $data)
|
|
{
|
|
return htmlspecialchars((string)$data);
|
|
}
|
|
|
|
/**
|
|
* @param string $extensions
|
|
* @param string $vendor
|
|
* @return bool|string
|
|
*/
|
|
protected function validateExtNeedUpdate($extensions, $vendor = '')
|
|
{
|
|
$extensionsName = $this->validateExt($extensions, $vendor);
|
|
if (!is_array($extensionsName)) {
|
|
return false;
|
|
}
|
|
$extensionsName = array_filter($extensionsName, function ($value) {
|
|
return 1 === $value;
|
|
});
|
|
$extensionsName = array_keys($extensionsName);
|
|
if (empty($extensionsName)) {
|
|
return false;
|
|
}
|
|
$extensions = $this->prepareExtValue($extensions);
|
|
$_extensions = [];
|
|
foreach ($extensionsName as $extensionName) {
|
|
$_extensions[] = sprintf('%s (%s)', $extensionName, $extensions[$extensionName]);
|
|
}
|
|
return implode(', ', $_extensions);
|
|
}
|
|
|
|
/**
|
|
* @param string $extensions
|
|
* @param string $vendor
|
|
* @param bool $diff
|
|
* @return array|bool
|
|
*/
|
|
protected function validateExt($extensions, $vendor = '', $diff = false)
|
|
{
|
|
$extensions = $this->prepareExtValue($extensions);
|
|
if (empty($extensions)) {
|
|
return true;
|
|
}
|
|
|
|
$extensionsName = array_keys($extensions);
|
|
$modules = $this->getInstalledExt($vendor);
|
|
if ($diff) {
|
|
$extensionsName = array_diff($extensionsName, $modules);
|
|
} else {
|
|
$extensionsName = array_intersect($extensionsName, $modules);
|
|
}
|
|
|
|
if (empty($extensionsName)) {
|
|
return false;
|
|
}
|
|
|
|
$_extensions = [];
|
|
foreach ($extensionsName as $extensionName) {
|
|
$_extensions[$extensionName] = $this->validateVersion($extensionName, $extensions[$extensionName]);
|
|
}
|
|
|
|
return $_extensions;
|
|
}
|
|
|
|
/**
|
|
* @param string $extension
|
|
* @return array
|
|
*/
|
|
protected function prepareExtValue($extension)
|
|
{
|
|
$extensions = [];
|
|
if (preg_match_all('/([a-z0-9_]+)(\s\(([^\(\)]+)\))*/i', $extension, $matches, PREG_SET_ORDER)) {
|
|
foreach ($matches as $matche) {
|
|
$name = $matche[1];
|
|
$version = isset($matche[3]) ? $matche[3] : null;
|
|
$extensions[$name] = $version;
|
|
}
|
|
}
|
|
|
|
return $extensions;
|
|
}
|
|
|
|
/**
|
|
* @param string $vendor
|
|
* @return array
|
|
*/
|
|
protected function getInstalledExt($vendor = '')
|
|
{
|
|
$modules = $this->_loadObject(ModuleListInterface::class)->getNames();
|
|
|
|
$dispatchResult = new DataObject($modules);
|
|
$modules = $dispatchResult->toArray();
|
|
|
|
if (!empty($filter)) {
|
|
$_modules = [];
|
|
foreach ($modules as $module) {
|
|
$_module = explode('_', $module);
|
|
$_vendor = array_shift($_module);
|
|
if ($vendor == $_vendor) {
|
|
$_modules[] = $module;
|
|
}
|
|
}
|
|
$modules = $_modules;
|
|
}
|
|
$modules = array_filter($modules);
|
|
sort($modules, SORT_STRING);
|
|
|
|
return $modules;
|
|
}
|
|
|
|
/**
|
|
* @param string $moduleName
|
|
* @param string $version
|
|
* @return string|null
|
|
*/
|
|
protected function validateVersion($moduleName, $version)
|
|
{
|
|
$cur_version = $this->getComposerVersion($moduleName);
|
|
if (is_string($version) && is_string($cur_version)) {
|
|
return version_compare($version, $cur_version);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param string $moduleName
|
|
* @param string $type
|
|
* @return string|null
|
|
*/
|
|
protected function getComposerVersion($moduleName, $type = ComponentRegistrar::MODULE)
|
|
{
|
|
$path = $this->_loadObject(ComponentRegistrarInterface::class)->getPath($type, $moduleName);
|
|
|
|
if ($path) {
|
|
$dirReader = $this->_loadObject(ReadFactory::class)->create($path);
|
|
|
|
if ($dirReader->isExist('composer.json')) {
|
|
$data = $dirReader->readFile('composer.json');
|
|
$data = json_decode($data, true);
|
|
if (isset($data['version'])) {
|
|
return $data['version'];
|
|
}
|
|
}
|
|
if ($dirReader->isExist('etc/module.xml')) {
|
|
$data = $dirReader->readFile('etc/module.xml');
|
|
if (preg_match('/setup_version="([^\"]+)"/i', $data, $matches)) {
|
|
return $matches[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @param string $extensions
|
|
* @param string $vendor
|
|
* @return array|bool
|
|
*/
|
|
protected function validateExtInstalled($extensions, $vendor = '')
|
|
{
|
|
return $this->validateExt($extensions, $vendor);
|
|
}
|
|
|
|
/**
|
|
* @param string $extensions
|
|
* @param string $vendor
|
|
* @return array|bool
|
|
*/
|
|
protected function validateExtNotInstalled($extensions, $vendor = '')
|
|
{
|
|
return $this->validateExt($extensions, $vendor);
|
|
}
|
|
|
|
/**
|
|
* @param array $types
|
|
* @return $this
|
|
*/
|
|
public function removeOXType($types = [])
|
|
{
|
|
|
|
if (empty($types) || !is_array($types)) {
|
|
return $this;
|
|
}
|
|
|
|
$types = array_unique($types);
|
|
/** @var OXFactory $collection */
|
|
$collection = $this->_loadObject(OXFactory::class)->create()->addFieldToFilter('ox_type',
|
|
array('in' => $types));
|
|
foreach ($collection as $model) {
|
|
$model->setIsRemove(1)->save()->delete();
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setLastUpdate()
|
|
{
|
|
$this->helper()->setModuleConfig('admin_notifications/lastcheck', time());
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function removeExpiredItems()
|
|
{
|
|
if ($this->getLastRemove() + self::REMOVE_FREQUENCY > time()) {
|
|
return $this;
|
|
}
|
|
|
|
/** @var ExpiredFactory $collection */
|
|
$collection = $this->_loadObject(ExpiredFactory::class)->create();
|
|
foreach ($collection as $model) {
|
|
$model->setIsRemove(1)->save()->delete();
|
|
}
|
|
|
|
$this->setLastRemove();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getLastRemove()
|
|
{
|
|
return (int)$this->helper()->getModuleConfig('admin_notifications/lastremove');
|
|
}
|
|
|
|
/**
|
|
* @return $this
|
|
*/
|
|
public function setLastRemove()
|
|
{
|
|
$this->helper()->setModuleConfig('admin_notifications/lastremove', time());
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param SimpleXMLElement $link
|
|
* @return mixed
|
|
*/
|
|
private function isItemExists($link)
|
|
{
|
|
return $this->_loadObject(ExistsFactory::class)->create()->execute($this->escapeString($link));
|
|
}
|
|
|
|
}
|