Commit a176bf4c by lmf

增加aitoc邮件发送插件

parent 5132e606
<?php
namespace Aitoc\Core\Block;
use Magento\Framework\Data\Form\Element\AbstractElement;
class InstalledExtensions extends \Magento\Config\Block\System\Config\Form\Fieldset
{
const AITOC_UPDATE_EXTENSION_VERSION_LINK = 'https://www.aitoc.com/customer/account/login/';
const AITOC_PRODUCT_LINK_DEFAULT = 'https://www.aitoc.com/magento-2-extensions.html';
const AITOC_SUPPROT_LINK = 'https://www.aitoc.com/get-support.html';
/**
* @var \Magento\Framework\Module\ModuleListInterface
*/
protected $_moduleList;
/**
* @var \Magento\Framework\View\LayoutFactory
*/
protected $_layoutFactory;
/**
* @var \Aitoc\Core\Helper\Extensions
*/
private $extensionsHelper;
public function __construct(
\Magento\Backend\Block\Context $context,
\Magento\Backend\Model\Auth\Session $authSession,
\Magento\Framework\View\Helper\Js $jsHelper,
\Magento\Framework\Module\ModuleListInterface $moduleList,
\Magento\Framework\View\LayoutFactory $layoutFactory,
\Aitoc\Core\Helper\Extensions $extensionsHelper,
array $data = []
) {
parent::__construct($context, $authSession, $jsHelper, $data);
$this->_moduleList = $moduleList;
$this->_layoutFactory = $layoutFactory;
$this->extensionsHelper = $extensionsHelper;
$this->_scopeConfig = $context->getScopeConfig();
}
/**
* Render fieldset html
*
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
$html = $this->_getHeaderHtml($element);
$modules = $this->extensionsHelper->getAitocExtensions(true);
if ($modules) {
foreach ($modules as $ext) {
$html .= $this->getRenderExtensionLine($ext);
}
}
$html .= $this->_getFooterHtml($element);
return $html;
}
/**
* Return footer html for fieldset
* Add extra tooltip comments to elements
*
* @param AbstractElement $element
* @return string
*/
protected function _getFooterHtml($element)
{
$html = '</tbody></table>';
$html .= $this->addCommentToHtml();
foreach ($element->getElements() as $field) {
if ($field->getTooltip()) {
$html .= sprintf(
'<div id="row_%s_comment" class="system-tooltip-box" style="display:none;">%s</div>',
$field->getId(),
$field->getTooltip()
);
}
}
$html .= '</fieldset>' . $this->_getExtraJs($element);
if ($element->getIsNested()) {
$html .= '</td></tr>';
} else {
$html .= '</div>';
}
return $html;
}
/**
* @param $fieldset
* @param $moduleCode
* @return string
*/
private function getRenderExtensionLine($extName)
{
$extensionsEnabled = $this->extensionsHelper->isModuleEnabled($extName);
$resultHtml = '';
$extInfo = $this->extensionsHelper->getExtInfo($extName);
$packageData = [];
$versionOld = false;
$productUrl = self::AITOC_UPDATE_EXTENSION_VERSION_LINK;
if (!is_array($extInfo) ||
!array_key_exists('version', $extInfo) ||
!array_key_exists('description', $extInfo) ||
!array_key_exists('name', $extInfo)
) {
return '';
}
$allExtensionsData = $this->extensionsHelper->getAllExtensions();
if (isset($allExtensionsData[$extInfo['name']])) {
$packageData = $allExtensionsData[$extInfo['name']];
if ($packageData && isset($packageData['version'])) {
$versionOld = $this->extensionsHelper
->compareExtensionComposerVersions($packageData['version'], $extInfo['version']);
if (isset($packageData['product_url']) && $packageData['product_url']) {
$productUrl = $packageData['product_url'];
}
}
}
$resultHtml .= '<tr id="aitoc_core_' . strtolower($extName) . '"><td class="label"><label for="aitoc_core_' .
strtolower($extName) . '"><span><a href="' . $productUrl . '" target="_blank">'
. str_replace('extension', '', str_replace('by Aitoc', '', $extInfo['description']))
. '</a> (' . ($extensionsEnabled ? __('Enabled') : __('Disabled')) . ')</span></label></td>';
$resultHtml .= '<td class="value">'
. $extInfo['version'] . ' '
. '<b>' .
($versionOld ?
__("(New version %1 is available in your account: ", $packageData['version'])
. '<a class="aitoc-button-get-new-version" href="'
. self::AITOC_UPDATE_EXTENSION_VERSION_LINK .
'" target="_blank">' . __('Get Update') . '</a> )' : '' )
. '</b></td>';
return $resultHtml . '</tr>';
}
/**
* @param $html
* @return string
*/
private function addCommentToHtml()
{
$html = '<div class="comment aitoc-support">';
$html .= 'Have any issues with <b>Aitoc extensions</b>?' .
' Please <a href="' . self::AITOC_SUPPROT_LINK
. '" class="aitoc-get-support-button" target="_blank">Contact Support</a>';
return $html . '</div>';
}
/**
* @return \Magento\Framework\View\Element\BlockInterface
*/
protected function _getFieldRenderer()
{
if (empty($this->_fieldRenderer)) {
$layout = $this->_layoutFactory->create();
$this->_fieldRenderer = $layout->createBlock(
\Magento\Config\Block\System\Config\Form\Field::class
);
}
return $this->_fieldRenderer;
}
}
<?php
namespace Aitoc\Core\Block;
/**
* Class Shopfeed
* @package Aitoc\Core\Block
*/
class Shopfeed extends \Magento\Config\Block\System\Config\Form\Fieldset
{
/**
* Render text
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element)
{
$html = $this->_getHeaderHtml($element);
$html .= $this->_getFooterHtml($element);
return $html;
}
/**
* Return element html
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _getElementHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element)
{
return $this->_toHtml();
}
}
<?php
namespace Aitoc\Core\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface;
/**
* Backend system config datetime field renderer
*/
class Notification extends \Magento\Config\Block\System\Config\Form\Field
{
/**
* @var DateTimeFormatterInterface
*/
protected $dateTimeFormatter;
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param DateTimeFormatterInterface $dateTimeFormatter
* @param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
DateTimeFormatterInterface $dateTimeFormatter,
array $data = []
) {
parent::__construct($context, $data);
$this->dateTimeFormatter = $dateTimeFormatter;
}
/**
* @param AbstractElement $element
* @return string
*/
protected function _getElementHtml(AbstractElement $element)
{
$element->setValue($this->_cache->load(\Aitoc\Core\Model\Feed::AITOC_CACHE_NAME));
$format = $this->_localeDate->getDateTimeFormat(
\IntlDateFormatter::MEDIUM
);
return $this->dateTimeFormatter->formatObject($this->_localeDate->date(intval($element->getValue())), $format);
}
}
<?php
namespace Aitoc\Core\Components\Model\ResourceModel;
use Magento\Framework\Api\ExtensibleDataInterface;
use Magento\Framework\Api\Search\AggregationInterface;
use Magento\Framework\Api\Search\DocumentInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
trait GridCollectionTrait
{
/**
* @var AggregationInterface
*/
private $aggregations;
/**
* Retrieve all ids for collection
* Backward compatibility with EAV collection
*
* @param int $limit
* @param int $offset
* @return array
*/
public function getAllIds($limit = null, $offset = null)
{
return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
}
/**
* @return AggregationInterface
*/
public function getAggregations()
{
return $this->aggregations;
}
/**
* @param AggregationInterface $aggregations
* @return $this
*/
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
}
/**
* Get search criteria.
*
* @return SearchCriteriaInterface|null
*/
public function getSearchCriteria()
{
return null;
}
/**
* Set search criteria.
*
* @param SearchCriteriaInterface $searchCriteria
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setSearchCriteria(SearchCriteriaInterface $searchCriteria = null)
{
return $this;
}
/**
* Get total count.
*
* @return int
*/
public function getTotalCount()
{
return $this->getSize();
}
/**
* Set total count.
*
* @param int $totalCount
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setTotalCount($totalCount)
{
return $this;
}
/**
* Set items list.
*
* @param ExtensibleDataInterface[] $items
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setItems(array $items = null)
{
return $this;
}
/**
* @return DocumentInterface[]
*/
public function getItems()
{
return $this;
}
}
<?php
namespace Aitoc\Core\Components\Model\ResourceModel;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
abstract class LinkedFieldResource extends AbstractDb
{
/**
* @var array
*/
protected $linkedFields = [];
/**
* Order is important. Key is required. For example:
* ["store_labels" => ["aitoc_shipping_carrier_labels", "label", "store_id"]
*
* @return array
*/
protected function getLinkedFields()
{
return $this->linkedFields;
}
/**
* @inheritDoc
*/
protected function _afterSave(AbstractModel $object)
{
parent::_afterSave($object);
foreach (array_keys($this->getLinkedFields()) as $linkedField) {
$this->updateLinkedField($object, $linkedField);
}
return $this;
}
/**
* @param AbstractModel $object
*/
public function loadAllLinkedData(AbstractModel $object)
{
foreach (array_keys($this->getLinkedFields()) as $linkedField) {
$this->loadLinkedValue($object, $linkedField);
}
}
/**
* @param AbstractModel $object
* @param string $linkedField
* @return array
* @throws \Magento\Framework\Exception\LocalizedException
*/
protected function loadLinkedValue(AbstractModel $object, $linkedField)
{
list($linkedTable, $valueField, $keyField) = array_values($this->linkedFields[$linkedField]);
$fetchingValues = [$valueField];
if ($keyField) {
array_unshift($fetchingValues, $keyField);
}
$select = $this->getConnection()->select()
->from($this->getTable($linkedTable), $fetchingValues)
->where($this->getIdFieldName() . ' = :id');
if ($keyField) {
$linkedData = $this->getConnection()->fetchPairs($select, [':id' => $object->getId()]);
} else {
$linkedData = $this->getConnection()->fetchCol($select, [':id' => $object->getId()]);
}
$object->setData($linkedField, $linkedData);
}
/**
* @param AbstractModel $object
* @param string $linkedField
* @return $this
* @throws \Magento\Framework\Exception\LocalizedException
*/
private function updateLinkedField(AbstractModel $object, $linkedField)
{
list($linkedTable, $valueField, $keyField) = array_values($this->linkedFields[$linkedField]);
$idField = $this->getIdFieldName();
$linkedTable = $this->getTable($linkedTable);
$connection = $this->getConnection();
if ($object->hasData($linkedField)) {
$connection->delete($linkedTable, $connection->quoteInto("$idField = ?", $object->getId()));
}
$data = [];
foreach ((array)$object->getData($linkedField) as $key => $value) {
if ($value !== null) {
$new = [
$idField => $object->getId(),
$valueField => $value,
];
if (!empty($keyField)) {
$new[$keyField] = $key;
}
$data[] = $new;
}
}
if ($data) {
$connection->insertMultiple($this->getTable($linkedTable), $data);
}
return $this;
}
}
\ No newline at end of file
<?php
namespace Aitoc\Core\Components\Model\Source;
use Magento\Customer\Api\GroupRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Convert\DataObject;
class CustomerGroupsOptions implements \Magento\Framework\Data\OptionSourceInterface
{
/**
* @var GroupRepositoryInterface
*/
private $groupRepository;
/**
* @var SearchCriteriaBuilder
*/
private $criteriaBuilder;
/**
* @var DataObject
*/
private $dataObjectConverter;
public function __construct(
GroupRepositoryInterface $groupRepository,
SearchCriteriaBuilder $criteriaBuilder,
DataObject $dataObjectConverter
) {
$this->groupRepository = $groupRepository;
$this->criteriaBuilder = $criteriaBuilder;
$this->dataObjectConverter = $dataObjectConverter;
}
/**
* @return array
*/
public function toOptionArray()
{
$customerGroups = $this->groupRepository->getList($this->criteriaBuilder->create())->getItems();
return $this->dataObjectConverter->toOptionArray($customerGroups, 'id', 'code');
}
}
<?php
namespace Aitoc\Core\Components\Model\Source;
use Magento\Framework\Data\OptionSourceInterface;
use Magento\Store\Model\System\Store;
class StoreOptions implements OptionSourceInterface
{
/**
* @var Store
*/
private $store;
/**
* @param Store $store
*/
public function __construct(Store $store)
{
$this->store = $store;
}
/**
* @return array
*/
public function toOptionArray()
{
return $this->store->getStoreValuesForForm(false, true);
}
}
<?php
namespace Aitoc\Core\Components\Model\Source;
use Magento\Store\Model\System\Store;
use Magento\Framework\Data\OptionSourceInterface;
class WebsitesOptions implements OptionSourceInterface
{
/**
* @var Store
*/
protected $store;
public function __construct(Store $store)
{
$this->store = $store;
}
/**
* @inheritDoc
*/
public function toOptionArray()
{
return $this->store->getWebsiteValuesForForm();
}
}
<?php
namespace Aitoc\Core\Components\Model\Source;
/**
* includes "All Websites" option
*/
class WebsitesOptionsAll extends WebsitesOptions
{
/**
* @inheritDoc
*/
public function toOptionArray()
{
$options = $this->store->getWebsiteValuesForForm(false, true);
foreach ($options as &$option) {
if ($option['value'] === 0) {
$option['label'] = __('All Websites');
break;
}
}
return $options;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Model\Config\Source;
use Magento\Framework\Option\ArrayInterface;
namespace Aitoc\Core\Components\Model\Source;
class Authtype implements ArrayInterface
/**
* doesn't contain "-- Please Select--" option
*/
class WebsitesOptionsMultiselect extends WebsitesOptions
{
/**
* @return array
* @inheritDoc
*/
public function toOptionArray()
{
return [
['value' => 'none', 'label' => __('None')],
['value' => 'ssl', 'label' => 'SSL'],
['value' => 'tls', 'label' => 'TLS']
];
return $this->store->getWebsiteValuesForForm(true);
}
}
<?php
namespace Aitoc\Core\Components\Model\Source;
/**
* includes "All Websites" option
* doesn't contain "-- Please Select--" option
*/
class WebsitesOptionsMultiselectAll extends WebsitesOptions
{
/**
* @inheritDoc
*/
public function toOptionArray()
{
$options = $this->store->getWebsiteValuesForForm(true, true);
foreach ($options as &$option) {
if ($option['value'] === 0) {
$option['label'] = __('All Websites');
break;
}
}
return $options;
}
}
<?php
namespace Aitoc\Core\Components\Ui\Component\Listing\Column;
use Magento\Customer\Api\GroupRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Convert\DataObject;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
class CustomerGroups extends Column
{
/**
* @var array
*/
private $customerGroups;
/**
* @var DataObject
*/
private $objectConverter;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
GroupRepositoryInterface $groupRepository,
SearchCriteriaBuilder $searchCriteriaBuilder,
DataObject $objectConverter,
array $components = [],
array $data = []
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
$this->objectConverter = $objectConverter;
$this->customerGroups = $groupRepository->getList($searchCriteriaBuilder->create())->getItems();
}
/**
* @inheritDoc
*/
public function prepareDataSource(array $dataSource)
{
$groups = $this->objectConverter->toOptionHash($this->customerGroups, 'id', 'code');
if (isset($dataSource['data']['items'])) {
$fieldName = $this->getData('name');
foreach ($dataSource['data']['items'] as & $item) {
$groupNames = [];
foreach ($item[$fieldName] as $groupId) {
if (!isset($groups[$groupId])) {
continue;
}
$groupNames[] = $groups[$groupId];
}
$item[$fieldName] = implode(', ', $groupNames);
}
}
return $dataSource;
}
}
<?php
namespace Aitoc\Core\Components\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Store\Model\StoreManagerInterface;
class Price extends Column
{
/**
* @var PriceCurrencyInterface
*/
protected $priceFormatter;
/**
* @var StoreManagerInterface
*/
private $storeManager;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
PriceCurrencyInterface $priceFormatter,
StoreManagerInterface $storeManager,
array $components = [],
array $data = []
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
$this->priceFormatter = $priceFormatter;
$this->storeManager = $storeManager;
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
$currencyCode = $this->storeManager->getStore()->getBaseCurrency()->getCurrencySymbol();
foreach ($dataSource['data']['items'] as &$item) {
if ($item[$this->getData('name')] !== null) {
$item[$this->getData('name')] = $this->priceFormatter->format(
$item[$this->getData('name')],
false,
null,
null,
$currencyCode
);
}
}
}
return $dataSource;
}
}
<?php
namespace Aitoc\Core\Components\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Store\Model\StoreManagerInterface;
class Websites extends Column
{
/**
* @var StoreManagerInterface
*/
private $storeManager;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
StoreManagerInterface $storeManager,
array $components = [],
array $data = []
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
$this->storeManager = $storeManager;
}
/**
* @inheritDoc
*/
public function prepareDataSource(array $dataSource)
{
$websiteNames = [0 => __('All Websites')];
foreach ($this->getData('options') as $website) {
$websiteNames[$website->getWebsiteId()] = $website->getName();
}
if (isset($dataSource['data']['items'])) {
$fieldName = $this->getData('name');
foreach ($dataSource['data']['items'] as & $item) {
$websites = [];
foreach ($item[$fieldName] as $websiteId) {
if (!isset($websiteNames[$websiteId])) {
continue;
}
$websites[] = $websiteNames[$websiteId];
if ($websiteId == 0) {
break;
}
}
$item[$fieldName] = implode(', ', $websites);
}
}
return $dataSource;
}
/**
* @inheritDoc
*/
public function prepare()
{
parent::prepare();
if ($this->storeManager->isSingleStoreMode()) {
$this->_data['config']['componentDisabled'] = true;
}
}
}
<?php
namespace Aitoc\Core\Components\Ui\DataProvider;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Api\AttributeValueFactory;
/**
* Helping to use a Model in a Grid Collection
* @see \Magento\Framework\View\Element\UiComponent\DataProvider\Document
*/
trait DocumentTrait
{
/**
* Get an attribute value.
*
* @param string $attributeCode
* @return \Magento\Framework\Api\AttributeInterface|null
*/
public function getCustomAttribute($attributeCode)
{
$attributeValueFactory = ObjectManager::getInstance()->get(AttributeValueFactory::class);
/** @var \Magento\Framework\Api\AttributeInterface $attributeValue */
$attributeValue = $attributeValueFactory->create();
$attributeValue->setAttributeCode($attributeCode);
$attributeValue->setValue($this->getData($attributeCode));
return $attributeValue;
}
/**
* Set an attribute value for a given attribute code
*
* @param string $attributeCode
* @param mixed $attributeValue
* @return $this
*/
public function setCustomAttribute($attributeCode, $attributeValue)
{
$this->setData($attributeCode, $attributeValue);
return $this;
}
/**
* Retrieve custom attributes values.
*
* @return \Magento\Framework\Api\AttributeInterface[]|null
*/
public function getCustomAttributes()
{
$output = [];
$attributeValueFactory = ObjectManager::getInstance()->get(AttributeValueFactory::class);
foreach ($this->getData() as $key => $value) {
$attribute = $attributeValueFactory->create();
$output[] = $attribute->setAttributeCode($key)->setValue($value);
}
return $output;
}
/**
* Set array of custom attributes
*
* @param \Magento\Framework\Api\AttributeInterface[] $attributes
* @return $this
* @throws \LogicException
*/
public function setCustomAttributes(array $attributes)
{
/** @var \Magento\Framework\Api\AttributeInterface $attribute */
foreach ($attributes as $attribute) {
$this->setData(
$attribute->getAttributeCode(),
$attribute->getValue()
);
}
return $this;
}
}
\ No newline at end of file
<?php
namespace Aitoc\Core\Components\Ui\DataProvider\Form\Modifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Ui\DataProvider\Modifier\ModifierInterface;
class Price implements ModifierInterface
{
/**
* @var StoreManagerInterface
*/
private $storeManager;
/**
* @var ArrayManager
*/
private $arrayManager;
/**
* @var string
*/
private $fieldset;
/**
* @var array
*/
private $priceFields;
public function __construct(
StoreManagerInterface $storeManager,
ArrayManager $arrayManager,
$fieldset = 'general',
$priceFields = []
) {
$this->storeManager = $storeManager;
$this->arrayManager = $arrayManager;
$this->fieldset = $fieldset;
$this->priceFields = $priceFields;
}
/**
* @inheritDoc
*/
public function modifyData(array $data)
{
return $data;
}
/**
* @inheritDoc
*/
public function modifyMeta(array $meta)
{
return $this->preparePriceFields($meta);
}
/**
* @param $meta
* @return array
*/
protected function preparePriceFields($meta)
{
$symbol = $this->storeManager->getStore()->getBaseCurrency()->getCurrencySymbol();
foreach ($this->priceFields as $priceField) {
$pricePath = $this->fieldset . '/children/' . $priceField . '/arguments/data/config/';
$meta = $this->arrayManager->set($pricePath . 'addbefore', $meta, $symbol);
// $meta = $this->arrayManager->set($pricePath . 'validation', $meta, ['validate-zero-or-greater' => true]);
// $meta = $this->arrayManager->set($pricePath . 'additionalClasses', $meta, ['admin__field-small' => true]);
}
return $meta;
}
}
<?php
namespace Aitoc\Core\Components\Ui\DataProvider\Form\Modifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\DataProvider\Modifier\ModifierInterface;
class StoreViews implements ModifierInterface
{
/**
* @var StoreRepositoryInterface
*/
private $storeRepository;
/**
* @var ArrayManager
*/
private $arrayManager;
/**
* @var string
*/
private $fieldset;
/**
* @var string
*/
private $field;
public function __construct(
StoreRepositoryInterface $storeRepository,
ArrayManager $arrayManager,
$field = 'store_labels',
$fieldset = null
) {
$this->storeRepository = $storeRepository;
$this->arrayManager = $arrayManager;
$this->fieldset = $fieldset ?: $field;
$this->field = $field;
}
/**
* @inheritDoc
*/
public function modifyData(array $data)
{
if (!empty($data[$this->field])) {
foreach ($data[$this->field] as $id => $label) {
$data[$this->field . '[' . $id . ']'] = $label;
}
}
return $data;
}
/**
* @inheritDoc
*/
public function modifyMeta(array $meta)
{
$labelConfigs = [];
foreach ($this->storeRepository->getList() as $store) {
$storeId = $store->getId();
if (!$storeId) {
continue;
}
$labelConfigs[$this->field . '[' . $storeId . ']'] = $this->arrayManager->set(
'arguments/data/config',
[],
[
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'label' => $store->getName(),
'dataType' => Text::NAME,
'dataScope' => $this->field . '[' . $storeId . ']',
]
);
}
$meta[$this->fieldset]['children'] = $labelConfigs;
return $meta;
}
}
<?php
namespace Aitoc\Core\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;
class Config extends AbstractHelper
{
const AITOC_CORE_XML_PATH_NOTIFICATIONS = 'aitoc_core/notifications/';
const AITOC_CORE_XML_PATH_EXTENSIONS = 'aitoc_core/extensions/';
const AITOC_CORE_XML_PATH_MENU = 'aitoc_core/menu/';
/**
* @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress
*/
private $remoteAddress;
public function __construct(
\Magento\Framework\App\Helper\Context $context
) {
parent::__construct($context);
$this->remoteAddress = $context->getRemoteAddress();
}
/**
* @param $path
* @param int $storeId
* @return mixed
*/
public function getModuleConfig($path, $storeId = null)
{
return $this->scopeConfig->getValue(
$path,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
/**
* @return string
*/
public function getCurrentIp()
{
return $this->remoteAddress->getRemoteAddress();
}
/**
* @return boolean
*/
public function getNotificationsEnable()
{
return (bool)$this->getModuleConfig(self::AITOC_CORE_XML_PATH_NOTIFICATIONS . 'notice_enable');
}
/**
* @return array
*/
public function getNotificationsType()
{
$data = $this->getModuleConfig(self::AITOC_CORE_XML_PATH_NOTIFICATIONS . 'notice_type');
return $data ? explode(',', $data) : [];
}
/**
* @return mixed
*/
public function getNotificationsFrequency()
{
return $this->getModuleConfig(self::AITOC_CORE_XML_PATH_NOTIFICATIONS . 'frequency');
}
/**
* @return mixed
*/
public function getMenuEnable()
{
return $this->getModuleConfig(self::AITOC_CORE_XML_PATH_MENU . 'menu_enable');
}
}
<?php
namespace Aitoc\Core\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use SimpleXMLElement;
use Zend\Http\Client\Adapter\Curl as CurlClient;
use Zend\Http\Response as HttpResponse;
use Zend\Uri\Http as HttpUri;
use Magento\Framework\Json\DecoderInterface;
class Extensions extends AbstractHelper
{
const EXTENSIONS_PATH = 'aitoc_extensions';
const URL_EXTENSIONS = 'http://www.aitoc.com/shopfeed/index/extensiondata';
/**
* @var CurlClient
*/
protected $curlClient;
/**
* @var \Magento\Framework\App\CacheInterface
*/
protected $cache;
/**
* @var \Magento\Framework\Module\Dir\Reader
*/
private $moduleReader;
/**
* @var \Magento\Framework\Filesystem\Driver\File
*/
private $filesystem;
/**
* @var DecoderInterface
*/
private $jsonDecoder;
/**
* @var \Magento\Framework\Module\ModuleListInterface
*/
private $moduleList;
/**
* @var array
*/
private $aitocExtensions = [];
/**
* @var \Magento\Framework\App\ProductMetadataInterface
*/
private $productMetadata;
/**
* @var \Magento\Framework\Module\Manager
*/
private $moduleManager;
/**
* @var array
*/
private $moduleIgnoreList = ['Aitoc_Core', 'Aitoc_Tips'];
/**
* @var array
*/
private $aitocPrefixList = ['Aitoc_', 'AdjustWare_'];
/**
* @var array
*/
private $moduleDirs = ['Aitoc', 'AdjustWare'];
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Framework\App\CacheInterface $cache,
\Magento\Framework\Module\Dir\Reader $moduleReader,
\Magento\Framework\Filesystem\Driver\File $filesystem,
DecoderInterface $jsonDecoder,
\Magento\Framework\App\ProductMetadataInterface $productMetadata,
CurlClient $curl,
\Magento\Framework\Module\ModuleListInterface $moduleList
) {
parent::__construct($context);
$this->cache = $cache;
$this->curlClient = $curl;
$this->moduleReader = $moduleReader;
$this->filesystem = $filesystem;
$this->jsonDecoder = $jsonDecoder;
$this->productMetadata = $productMetadata;
$this->moduleList = $moduleList;
$this->moduleManager = $context->getModuleManager();
}
/**
* @return bool|mixed
*/
public function getAllExtensions()
{
$data = $this->cache->load(self::EXTENSIONS_PATH);
if (!$data) {
$extensionsData = $this->getExtensionsPackagesData();
$this->cache->save(json_encode($extensionsData), self::EXTENSIONS_PATH);
}
return json_decode($this->cache->load(self::EXTENSIONS_PATH), true);
}
/**
* @param $version1
* @param $version2
* @param string $operator
* @return mixed
*/
public function compareExtensionComposerVersions($version1, $version2, $operator = '>')
{
return version_compare($version1, $version2, $operator);
}
/**
* Save extensions data to magento cache
*/
protected function getExtensionsPackagesData()
{
$resultData = [];
$extensionsParsedJson = $this->getExtensionsData();
if ($extensionsParsedJson && is_array($extensionsParsedJson)) {
foreach ($extensionsParsedJson as $extName => $extData) {
if ($extName && $extData) {
$resultData[$extName] = $extData;
}
}
}
return $resultData;
}
/**
* Read data from xml file with curl
* @return bool|SimpleXMLElement
*/
protected function getExtensionsData()
{
$result = [];
try {
$extensionsData = file_get_contents(self::URL_EXTENSIONS);
if ($extensionsData && is_string($extensionsData)) {
$result = json_decode($extensionsData, true);
}
} catch (\Exception $e) {
return false;
}
return $result;
}
/**
* @param $extName
* @return array|mixed
* @throws \Magento\Framework\Exception\FileSystemException
*/
public function getExtInfo($extName)
{
$dir = $this->moduleReader->getModuleDir('', $extName);
if ($this->filesystem->isReadable($dir)) {
return $this->readExtComposerFile($dir);
}
return [];
}
/**
* @param $extDir
* @return array|mixed
* @throws \Magento\Framework\Exception\FileSystemException
*/
public function readExtComposerFile($extDir)
{
$file = $extDir . '/composer.json';
if ($this->filesystem->isExists($file)) {
$string = $this->filesystem->fileGetContents($file);
return json_decode($string, true);
}
return [];
}
/**
* @param bool $restrictedDelete
* @return array
*/
public function getAitocExtensions($restrictedDelete = false)
{
if (!$this->aitocExtensions) {
$modules = $this->moduleList->getNames();
$dispatchResult = new \Magento\Framework\DataObject($modules);
$modules = $dispatchResult->toArray();
foreach ($this->aitocPrefixList as $prefix) {
foreach ($modules as $item) {
if (strpos($item, $prefix) !== false) {
$this->aitocExtensions[] = $item;
}
}
}
}
if ($restrictedDelete) {
foreach ($this->moduleIgnoreList as $value) {
if (array_search($value, $this->aitocExtensions) !== false) {
unset($this->aitocExtensions[array_search($value, $this->aitocExtensions)]);
}
}
}
sort($this->aitocExtensions);
return $this->aitocExtensions;
}
/**
*
* @param string $moduleName Fully-qualified module name
* @return boolean
*/
public function isModuleEnabled($moduleName)
{
return $this->_moduleManager->isEnabled($moduleName);
}
/**
* @return string
*/
public function getMagentoEdition()
{
return $this->productMetadata->getEdition();
}
/**
* @return string
*/
public function getMagentoVersion()
{
return $this->productMetadata->getVersion();
}
}
<?php
namespace Aitoc\Core\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use SimpleXMLElement;
use Zend\Http\Client\Adapter\Curl as CurlClient;
use Zend\Http\Response as HttpResponse;
use Zend\Uri\Http as HttpUri;
use Magento\Framework\Json\DecoderInterface;
class Notice extends AbstractHelper
{
/**
* @var CurlClient
*/
protected $curlClient;
/**
* @var \Magento\Framework\App\CacheInterface
*/
protected $cache;
/**
* @var \Magento\Framework\Module\Dir\Reader
*/
private $moduleReader;
/**
* @var \Magento\Framework\Filesystem\Driver\File
*/
private $filesystem;
/**
* @var DecoderInterface
*/
private $jsonDecoder;
/**
* @var Config
*/
private $config;
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Framework\App\CacheInterface $cache,
\Magento\Framework\Module\Dir\Reader $moduleReader,
\Magento\Framework\Filesystem\Driver\File $filesystem,
DecoderInterface $jsonDecoder,
CurlClient $curl,
\Aitoc\Core\Helper\Config $config
) {
parent::__construct($context);
$this->cache = $cache;
$this->curlClient = $curl;
$this->moduleReader = $moduleReader;
$this->filesystem = $filesystem;
$this->jsonDecoder = $jsonDecoder;
$this->config = $config;
}
/**
* @return array
*/
public function getNotificationTypes()
{
return $this->config->getNotificationsType();
}
/**
* @return bool
*/
public function isEnable()
{
return $this->config->getNotificationsEnable();
}
/**
* @return mixed
*/
public function getFrequency()
{
return $this->config->getNotificationsFrequency();
}
}
<?php
namespace Aitoc\Core\Model\Config\Source;
class Frequency implements \Magento\Framework\Option\ArrayInterface
{
/**
* @return array
*/
public function toOptionArray()
{
$options = [
[
'value' => 1,
'label' => __('1 Day')
],
[
'value' => 5,
'label' => __('5 Days')
],
[
'value' => 10,
'label' => __('10 Days')
]
];
return $options;
}
}
<?php
namespace Aitoc\Core\Model\Config\Source;
class NoticeType implements \Magento\Framework\Option\ArrayInterface
{
const PROMO = 'PROMO';
const EXTENSION_UPDATE_CUSTOMER = 'EXTENSION_UPDATE_CUSTOMER';
const EXTENSION_UPDATE = 'EXTENSION_UPDATE';
const NEW_EXTENSION = 'NEW_EXTENSION';
const NEWS = 'NEWS';
const TIPS_TRICKS = 'TIPS_TRICKS';
public function toOptionArray()
{
$types = [
[
'value' => self::NEWS,
'label' => __('Common News')
],
[
'value' => self::PROMO,
'label' => __('Promotions/Discounts')
],
[
'value' => self::EXTENSION_UPDATE_CUSTOMER,
'label' => __('My Extensions Updates')
],
[
'value' => self::EXTENSION_UPDATE,
'label' => __('All Extensions Updates')
],
[
'value' => self::NEW_EXTENSION,
'label' => __('New Extensions')
],
[
'value' => self::TIPS_TRICKS,
'label' => __('Magento Tricks & Tips')
]
];
return $types;
}
}
<?php
namespace Aitoc\Core\Model;
use Magento\Framework\Config\ConfigOptionsListConstants;
use Magento\Framework\Notification\MessageInterface;
/**
* AdminNotification Feed model
*/
class Feed extends \Magento\AdminNotification\Model\Feed
{
const AITOC_CACHE_NAME = 'aitoc_notifications_lastcheck';
const XML_USE_HTTPS_PATH = 'system/adminnotification/use_https';
const XML_FEED_URL_PATH = 'www.aitoc.com/feedrss';
const XML_FREQUENCY_PATH = 'system/adminnotification/frequency';
const XML_LAST_UPDATE_PATH = 'system/adminnotification/last_update';
/**
* Feed url
*
* @var string
*/
protected $_feedUrl;
/**
* @var \Magento\Backend\App\ConfigInterface
*/
protected $_backendConfig;
/**
* @var \Magento\AdminNotification\Model\InboxFactory
*/
protected $_inboxFactory;
/**
* @var \Magento\Framework\HTTP\Adapter\CurlFactory
*
*/
protected $curlFactory;
/**
* Deployment configuration
*
* @var \Magento\Framework\App\DeploymentConfig
*/
protected $_deploymentConfig;
/**
* @var \Magento\Framework\App\ProductMetadataInterface
*/
protected $productMetadata;
/**
* @var \Magento\Framework\UrlInterface
*/
protected $urlBuilder;
/**
* @var \Aitoc\Core\Helper\Notice
*/
private $noticeHelper;
/**
* @var \Aitoc\Core\Helper\Extension
*/
private $extensionHelper;
/**
* @var \Magento\Framework\App\Config\Storage\WriterInterface
*/
private $configWriter;
/**
* @var \Magento\Backend\App\ConfigInterface
*/
private $config;
/**
* @var \Magento\Framework\App\Config\ReinitableConfigInterface
*/
private $reinitableConfig;
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Backend\App\ConfigInterface $backendConfig
* @param InboxFactory $inboxFactory
* @param \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory
* @param \Magento\Framework\App\DeploymentConfig $deploymentConfig
* @param \Magento\Framework\App\ProductMetadataInterface $productMetadata
* @param \Magento\Framework\UrlInterface $urlBuilder
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
* @param array $data
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Backend\App\ConfigInterface $backendConfig,
\Magento\AdminNotification\Model\InboxFactory $inboxFactory,
\Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory,
\Magento\Framework\App\DeploymentConfig $deploymentConfig,
\Magento\Framework\App\ProductMetadataInterface $productMetadata,
\Magento\Framework\UrlInterface $urlBuilder,
\Aitoc\Core\Helper\Notice $noticeHelper,
\Aitoc\Core\Helper\Extensions $extensionHelper,
\Magento\Backend\App\ConfigInterface $config,
\Magento\Framework\App\Config\ReinitableConfigInterface $reinitableConfig,
\Magento\Framework\App\Config\Storage\WriterInterface $configWriter,
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = []
) {
parent::__construct(
$context,
$registry,
$backendConfig,
$inboxFactory,
$curlFactory,
$deploymentConfig,
$productMetadata,
$urlBuilder,
$resource,
$resourceCollection,
$data
);
$this->noticeHelper = $noticeHelper;
$this->extensionHelper = $extensionHelper;
$this->config = $config;
$this->configWriter = $configWriter;
$this->reinitableConfig = $reinitableConfig;
}
/**
* Init model
*
* @return void
*/
protected function _construct()
{
}
/**
* Retrieve feed url
*
* @return string
*/
public function getFeedUrl()
{
$httpPath = $this->_backendConfig->isSetFlag(self::XML_USE_HTTPS_PATH) ? 'https://' : 'http://';
if ($this->_feedUrl === null) {
$this->_feedUrl = $httpPath . self::XML_FEED_URL_PATH;
}
return $this->_feedUrl;
}
/**
* Check feed for modification
*
* @return $this
*/
public function checkUpdate()
{
if ($this->getFrequency() + $this->getLastUpdate() > time()) {
return $this;
}
if (!$this->noticeHelper->isEnable()) {
return $this;
}
$feedData = [];
$feedXml = $this->getFeedData();
$installDate = $this->getFirstAitocRun();
if ($feedXml && $feedXml->channel && $feedXml->channel->item) {
foreach ($feedXml->channel->item as $item) {
$pubDate = strtotime((string)$item->pubDate);
$itemPublicationDate = strtotime((string)$item->pubDate);
if ($itemPublicationDate <= $installDate || !$this->isInteresting($item)) {
continue;
}
$feedData[] = [
'severity' => MessageInterface::SEVERITY_NOTICE,
'date_added' => date('Y-m-d H:i:s', $pubDate),
'title' => $this->escapeString($item->title),
'description' => $this->escapeString($item->description),
'url' => $this->escapeString($item->link),
'aitoc_notification' => 1
];
}
if ($feedData) {
$this->_inboxFactory->create()->parse(array_reverse($feedData));
}
}
$this->setLastUpdate();
return $this;
}
/**
* @return int|mixed
*/
private function getFirstAitocRun()
{
$coreConfigRunPath = 'aitoc_core/notifications/first_aitoc_run';
$result = $this->config->getValue($coreConfigRunPath);
if (!$result) {
$result = time();
$this->configWriter->save($coreConfigRunPath, $result);
$this->reinitableConfig->reinit();
}
return $result;
}
/**
* @param $item
* @return bool
*/
private function isInteresting($item)
{
$interests = $this->getTypes();
if (!$interests) {
return false;
}
if ($item->type != \Aitoc\Core\Model\Config\Source\NoticeType::EXTENSION_UPDATE
&& in_array((string)$item->type, $interests)
) {
return true;
}
if ($item->type == \Aitoc\Core\Model\Config\Source\NoticeType::EXTENSION_UPDATE
&& in_array(\Aitoc\Core\Model\Config\Source\NoticeType::EXTENSION_UPDATE_CUSTOMER, $interests)
&& !in_array(\Aitoc\Core\Model\Config\Source\NoticeType::EXTENSION_UPDATE, $interests)
) {
$extData = explode('-', (string)$item->extension);
if (!$extData) {
return false;
}
$extension = $extData[0];
if (array_key_exists(1, $extData)) {
$platform = $extData[1];
}
$isMagentoEE = $this->extensionHelper->getMagentoEdition() != 'Community';
if ($isMagentoEE && $platform == 'EE'
|| !$isMagentoEE && empty($platform)
) {
return $this->extensionHelper->isModuleEnabled($extension);
}
}
return false;
}
/**
* @return array
*/
private function getTypes()
{
return $this->noticeHelper->getNotificationTypes();
}
/**
* Retrieve Update Frequency
*
* @return int
*/
public function getFrequency()
{
return (int)$this->noticeHelper->getFrequency() * 60 * 60 * 24;
}
/**
* Retrieve Last update time
*
* @return int
*/
public function getLastUpdate()
{
return $this->_cacheManager->load(self::AITOC_CACHE_NAME);
}
/**
* Set last update time (now)
*
* @return $this
*/
public function setLastUpdate()
{
$this->_cacheManager->save(time(), self::AITOC_CACHE_NAME);
return $this;
}
/**
* Converts incoming data to string format and escapes special characters.
*
* @param \SimpleXMLElement $data
* @return string
*/
private function escapeString(\SimpleXMLElement $data)
{
return htmlspecialchars((string)$data);
}
}
<?php
namespace Aitoc\Core\Model\Helpers;
class Date
{
/**
* @var \Magento\Framework\Stdlib\DateTime
*/
private $dateTime;
/**
* @var \Magento\Framework\Stdlib\DateTime\DateTime
*/
private $date;
/**
* @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
*/
private $timezone;
public function __construct(
\Magento\Framework\Stdlib\DateTime $dateTime,
\Magento\Framework\Stdlib\DateTime\DateTime $date,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
) {
$this->dateTime = $dateTime;
$this->date = $date;
$this->timezone = $timezone;
}
/**
* @param bool $includedTime
*
* @return null|string
*/
public function getCurrentDate($includedTime = true)
{
return $this->dateTime->formatDate($this->date->gmtTimestamp(), $includedTime);
}
/**
* @param $stringTime
* @param bool $includedTime
*
* @return null|string
*/
public function getDateFromString($stringTime, $includedTime = true)
{
return $this->dateTime->formatDate($this->dateTime->strToTime($stringTime), $includedTime);
}
/**
* @param $stringTime
*
* @return int
*/
public function getTimestampFromString($stringTime)
{
return $this->dateTime->strToTime($stringTime);
}
/**
* @param $days
* @param bool $seconds
*
* @return int|null|string
*/
public function getCurrentDateAfterDays($days, $seconds = false)
{
$timestamp = $this->date->gmtTimestamp() + ($days * 24 * 3600);
return $seconds ? $timestamp : $this->dateTime->formatDate($timestamp);
}
/**
* @param $days
* @param bool $seconds
*
* @return int|null|string
*/
public function getCurrentDateBeforeDays($days, $seconds = false)
{
$timestamp = $this->date->gmtTimestamp() - ($days * 24 * 3600);
return $seconds ? $timestamp : $this->dateTime->formatDate($timestamp);
}
/**
* @return int
*/
public function getTimestamp()
{
return $this->date->gmtTimestamp();
}
/**
* @return string
*/
public function getTimezoneDate()
{
return $this->timezone->formatDate(null, \IntlDateFormatter::SHORT, true);
}
}
<?php
namespace Aitoc\Core\Model;
class Logger extends \Magento\Framework\Logger\Monolog { }
\ No newline at end of file
<?php
namespace Aitoc\Core\Model;
/**
* Class Platform
* @package Aitoc\Core\Model
*/
class Platform
{
const PLATFORMFILE_SUFFIX = '.platform.xml';
const CACHE_CLEAR_VERSION = '2.21.0';
const DEFAULT_VAR_PATH = 'var';
}
<?php
namespace Aitoc\Core\Observer;
use Magento\Framework\Event\ObserverInterface;
/**
* AdminNotification observer
*
*/
class PredispatchAdminActionControllerObserver implements ObserverInterface
{
/**
* @var \Magento\AdminNotification\Model\FeedFactory
*/
protected $_feedFactory;
/**
* @var \Magento\Backend\Model\Auth\Session
*/
protected $_backendAuthSession;
/**
* @param \Aitoc\Core\Model\FeedFactory $feedFactory
* @param \Magento\Backend\Model\Auth\Session $backendAuthSession
*/
public function __construct(
\Aitoc\Core\Model\FeedFactory $feedFactory,
\Magento\Backend\Model\Auth\Session $backendAuthSession
) {
$this->_feedFactory = $feedFactory;
$this->_backendAuthSession = $backendAuthSession;
}
/**
* Predispatch admin action controller
*
* @param \Magento\Framework\Event\Observer $observer
* @return void
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function execute(\Magento\Framework\Event\Observer $observer)
{
if ($this->_backendAuthSession->isLoggedIn()) {
$feedModel = $this->_feedFactory->create();
/* @var $feedModel \Magento\AdminNotification\Model\Feed */
$feedModel->checkUpdate();
}
}
}
<?php
namespace Aitoc\Core\Plugin\BackendMenu;
use Magento\Backend\Model\Menu\Config;
use Magento\Backend\Model\Menu;
use Aitoc\Core\Helper\Extensions;
use Magento\Config\Model\Config\Structure;
use Magento\Framework\App\ProductMetadataInterface;
use Magento\Backend\Model\Menu\Item;
use Magento\Backend\Model\Menu\ItemFactory;
use Magento\Backend\Model\Menu\Filter\IteratorFactory;
use Aitoc\Core\Model\Logger;
use Aitoc\Core\Helper\Config as AitocConfig;
class Builder
{
/**
* @var Config
*/
private $config;
/**
* @var AitocConfig
*/
private $aitocConfig;
/**
* @var ProductMetadataInterface
*/
private $productMetadata;
/**
* @var Extensions
*/
private $extensionHelper;
/**
* @var Structure
*/
private $configStructure;
/**
* @var ItemFactory
*/
private $itemFactory;
/**
* @var IteratorFactory
*/
private $iteratorFactory;
/**
* @var Logger
*/
private $logger;
public function __construct(
Config $config,
AitocConfig $aitocConfig,
ProductMetadataInterface $productMetadata,
Extensions $extensionHelper,
Structure $configStructure,
ItemFactory $itemFactory,
IteratorFactory $iteratorFactory,
Logger $logger
) {
$this->config = $config;
$this->aitocConfig = $aitocConfig;
$this->productMetadata = $productMetadata;
$this->extensionHelper = $extensionHelper;
$this->configStructure = $configStructure;
$this->itemFactory = $itemFactory;
$this->iteratorFactory = $iteratorFactory;
$this->logger = $logger;
}
/**
* @param $subject
* @param Menu $menu
* @return Menu
*/
public function afterGetResult($subject, Menu $menu)
{
return $menu->get('Aitoc_Core::menu') ? $this->buildMenu($menu): $menu;
}
/**
* @param Menu $menu
* @return Menu
*/
private function buildMenu(Menu $menu)
{
$menuWithoutAitoc = $this->isRemoveAitocTab($menu);
if ($menuWithoutAitoc) {
return $menuWithoutAitoc;
}
try {
$configItems = $this->getItemsFromConfig();
$extensionData = [];
$aitocExtensions = $this->extensionHelper->getAitocExtensions(true);
foreach ($aitocExtensions as $extCode) {
if (!empty($configItems[$extCode]['label']) && $configItems[$extCode]['label']) {
$label = $configItems[$extCode]['label'];
} elseif ($extInfo = $this->extensionHelper->getExtInfo($extCode) && !empty($extInfo['description'])) {
$label = $extInfo['description'];
} else {
$label = explode('_', $extCode)[1];
}
$extensionData[$label] = $extCode;
}
$this->render($extensionData, $configItems, $menu);
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
return $menu;
}
/**
* @param array $extensionData
* @param array $configItems
* @param Menu $menu
* @throws \Exception
*/
private function render($extensionData, $configItems, Menu $menu)
{
$menuItems = $this->getMenuItems($this->config->getMenu());
foreach ($extensionData as $label => $extCode) {
$renderedItems = [];
if (!empty($menuItems[$extCode])) {
$renderedItems = array_merge($renderedItems, $this->getRenderedItemsByExtension($menuItems[$extCode], $menu));
}
if (!empty($configItems[$extCode]['id'])) {
$renderedItems[] = $this->renderItem(
$extCode . '_aitocMenu',
$extCode,
__('🔧 Configuration')->render(),
'Aitoc_Core::menu',
'adminhtml/system_config/edit/section/' . $configItems[$extCode]['id']
);
}
if ($renderedItems) {
$itemId = $extCode . 'aitocMenu';
/** @var Item $mainItem */
$mainItem = $this->itemFactory->create(['data' => [
'id' => $itemId,
'title' => $label,
'module' => $extCode,
'resource' => 'Aitoc_Core::menu'
]]);
$menu->add($mainItem, 'Aitoc_Core::menu', 1);
foreach ($renderedItems as $item) {
$menu->add($item, $itemId);
}
}
}
}
/**
* @param Menu $menu
* @return bool|Menu
*/
private function isRemoveAitocTab(Menu $menu)
{
if (!$this->aitocConfig->getMenuEnable()
|| version_compare($this->productMetadata->getVersion(), '2.2', '<')
) {
$menu->remove('Aitoc_Core::menu');
return $menu;
}
return false;
}
/**
* @param array $menuItems
* @param Menu $menu
* @return array
*/
private function getRenderedItemsByExtension($menuItems, Menu $menu)
{
$newItems = [];
foreach ($menuItems as $itemId) {
$item = $menu->get($itemId);
if ($item) {
$item = $item->toArray();
if (isset($item['id']) && isset($item['title']) && isset($item['resource']) && isset($item['action'])) {
$extension = empty($item['module']) ? explode('::', $item['resource'])[0] : $item['module'];
$newItems[] = $this->renderItem(
$item['id'] . '_aitocMenu',
$extension,
$item['title'],
$item['resource'],
$item['action']
);
}
}
}
return $newItems;
}
/**
* @param string $id
* @param string $extension
* @param string $title
* @param string $resource
* @param string $url
* @return Item|null
*/
private function renderItem($id, $extension, $title, $resource, $url)
{
try {
$item = $this->itemFactory->create(['data' => [
'id' => $id,
'module' => $extension,
'title' => $title,
'action' => $url,
'resource' => $resource
]]);
} catch (\Exception $e) {
$this->logger->warning($e->getMessage());
$item = null;
}
return $item;
}
/**
* @param Menu $menu
* @return array
*/
private function getMenuItems(Menu $menu)
{
$menuItems = [];
foreach ($this->generateMenuItems($menu) as $item) {
$name = explode('::', $item)[0];
if (!isset($menuItems[$name])) {
$menuItems[$name] = [];
}
$menuItems[$name][] = $item;
}
return $menuItems;
}
/**
* @param Menu $menu
* @return array
*/
private function generateMenuItems(Menu $menu)
{
$menuItems = [];
foreach ($this->iteratorFactory->create(['iterator' => $menu->getIterator()]) as $menuItem) {
/** @var Item $menuItem */
if ($this->validateMenuItem($menuItem)) {
$menuItems[] = $menuItem->getId();
}
if ($menuItem->hasChildren()) {
$menuItems = array_merge($menuItems, $this->generateMenuItems($menuItem->getChildren()));
}
}
return $menuItems;
}
/**
* @return array
*/
private function getItemsFromConfig()
{
$configItems = [];
foreach ($this->configStructure->getTabs() as $tab) {
if ($tab->getId() == 'aitoc_extensions') {
foreach ($tab->getChildren() as $item) {
$itemData = $item->getData('resource');
if (!empty($itemData['id']) && $itemData['id'] && !empty($itemData['resource'])) {
$name = explode('::', $itemData['resource'])[0];
$configItems[$name] = $itemData;
}
}
break;
}
}
return $configItems;
}
/**
* @param Item $menuItem
* @return bool
*/
private function validateMenuItem(Item $menuItem)
{
return $menuItem->getAction()
&& strpos($menuItem->getAction(), 'system_config') === false
&& strpos($menuItem->getId(), 'Aitoc') !== false
&& strpos($menuItem->getId(), 'Aitoc_Core') === false;
}
}
<?php
namespace Aitoc\Core\Plugin\BackendMenu;
use Magento\Backend\Model\Menu\Item as NativeItem;
class Item
{
/**
* @param NativeItem $subject
* @param $url
* @return string
*/
public function afterGetUrl(NativeItem $subject, $url)
{
$id = $subject->getId();
if ($id == 'Aitoc_Core::marketplace') {
return 'https://www.aitoc.com/magento-2-extensions.html?utm_source=extensions_promo&utm_medium=backend&utm_campaign=from_magento_2_menu';
} else {
return $url;
}
}
}
<?php
namespace Aitoc\Core\Plugin\Notifications;
/**
* Class AitocNotificationLogoAdd
* @package Aitoc\Core\Plugin\Notifications
*/
class AitocNotificationLogoAdd
{
/**
* @param \Magento\AdminNotification\Block\Grid\Renderer\Notice $subject
* @param \Closure $proceed
* @param \Magento\Framework\DataObject $row
* @return mixed|string
*/
public function aroundRender(
\Magento\AdminNotification\Block\Grid\Renderer\Notice $subject,
\Closure $proceed,
\Magento\Framework\DataObject $row
) {
$result = $proceed($row);
if ($row->getData(\Aitoc\Core\Setup\UpgradeSchema::AITOC_NOTIFICATION_FIELD)) {
return '<div class="aitoc-grid-message"><div class="aitoc-notif-logo"></div>' . $result . '</div>';
} else {
return $result;
}
}
}
<?php
namespace Aitoc\Core\Plugin\Notifications;
use Magento\AdminNotification\Block\ToolbarEntry as NativeToolbarEntry;
class AitocNotificationLogoAddInToolbar
{
/**
* @param NativeToolbarEntry $subject
* @param $html
* @return mixed
*/
public function afterToHtml(
NativeToolbarEntry $subject,
$html
) {
return $this->getReplacedLogoWithHtml($subject, $html);
}
/**
* @param NativeToolbarEntry $subject
* @return \Magento\AdminNotification\Model\ResourceModel\Inbox\Collection
*/
private function getAitocNotificationsCollection(NativeToolbarEntry $subject)
{
return $subject->getLatestUnreadNotifications()
->clear()
->addFieldToFilter(\Aitoc\Core\Setup\UpgradeSchema::AITOC_NOTIFICATION_FIELD, 1);
}
/**
* @param NativeToolbarEntry $subject
* @param $html
* @return mixed
*/
private function getReplacedLogoWithHtml(NativeToolbarEntry $subject, $html)
{
foreach ($this->getAitocNotificationsCollection($subject) as $item) {
$search = 'data-notification-id="' . $item->getId() . '"';
$html = str_replace($search, $search . ' data-aitcore-logo="1"', $html);
}
return $html;
}
}
<?php
namespace Aitoc\Core\Plugin\Notifications;
class GridActions
{
const CONFIG_AITOC_CORE_SECTION_NAME = 'aitoc_core';
/**
* @var \Magento\Framework\UrlInterface
*/
private $urlBuilder;
/**
* GridActions constructor.
* @param \Magento\Framework\UrlInterface $urlBuilder
*/
public function __construct(
\Magento\Framework\UrlInterface $urlBuilder
) {
$this->urlBuilder = $urlBuilder;
}
/**
* @param NativeActions $subject
* @param \Closure $proceed
* @param \Magento\Framework\DataObject $row
* @return mixed|string
*/
public function aroundRender(
\Magento\AdminNotification\Block\Grid\Renderer\Actions $subject,
\Closure $proceed,
\Magento\Framework\DataObject $row
) {
$result = $proceed($row);
if ($row->getData(\Aitoc\Core\Setup\UpgradeSchema::AITOC_NOTIFICATION_FIELD)) {
$result .= sprintf(
'<a class="action" href="%s" title="%s">%s</a>',
$this->getDisableUrl(),
__('Disable Notifications'),
__('Disable Notifications')
);
}
return $result;
}
/**
* @return string
*/
private function getDisableUrl()
{
return $this->urlBuilder->getUrl('adminhtml/system_config/edit/'). 'section/'
. self::CONFIG_AITOC_CORE_SECTION_NAME;
}
}
<?php
namespace Aitoc\Core\Setup;
use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
class UpgradeSchema implements UpgradeSchemaInterface
{
const AITOC_NOTIFICATION_FIELD = 'aitoc_notification';
/**
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
*/
public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();
if (version_compare($context->getVersion(), '1.0.1', '<')) {
$this->addAitocNotificationField($setup);
}
}
/**
* @param SchemaSetupInterface $setup
*/
private function addAitocNotificationField(SchemaSetupInterface $setup)
{
$setup->getConnection()->addColumn(
$setup->getTable('adminnotification_inbox'),
self::AITOC_NOTIFICATION_FIELD,
\Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
null,
['nullable' => false, 'default' => 0],
'Aitoc Notification'
);
}
}
{
"name": "aitoc/core",
"description": "Core extension by Aitoc",
"require": {
"php": "~5.6.5|7.0.2|7.0.4|~7.0.6|~7.0.13|~7.1.0|~7.2.0|~7.3.0|~7.4.0"
},
"type": "magento2-module",
"version": "1.0.12",
"license": [
"Commercial"
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Aitoc\\Core\\": ""
}
}
}
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Aitoc_Core::config"
title="Aitoc - General Information"
sortOrder="100" />
</resource>
</resource>
</resource>
<resource id="Aitoc_Core::menu" title="Aitoc Extensions" translate="title" sortOrder="101">
<resource id="Aitoc_Core::marketplace" title="Aitoc Marketplace" translate="title" sortOrder="1"/>
</resource>
</resource>
</resources>
</acl>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\AdminNotification\Block\ToolbarEntry">
<plugin name="Aitoc_Core::add-aitoc-logo-in-toolbar"
type="Aitoc\Core\Plugin\Notifications\AitocNotificationLogoAddInToolbar"/>
</type>
<type name="Magento\AdminNotification\Block\Grid\Renderer\Notice">
<plugin name="Aitoc_Core::add-aitoc-logo"
type="Aitoc\Core\Plugin\Notifications\AitocNotificationLogoAdd"/>
</type>
<type name="Magento\AdminNotification\Block\Grid\Renderer\Actions">
<plugin name="Aitoc_Core::add-disable-notifications"
type="Aitoc\Core\Plugin\Notifications\GridActions"/>
</type>
<type name="Magento\Backend\Model\Menu\Item">
<plugin name="Aitoc_Core:replaceMarketplaceUrl"
type="Aitoc\Core\Plugin\BackendMenu\Item" />
</type>
<type name="Magento\Backend\Model\Menu\Builder">
<plugin name="Aitoc_Core::aitocMenuBuilder" type="Aitoc\Core\Plugin\BackendMenu\Builder" />
</type>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="controller_action_predispatch">
<observer name="aitoc_adminnotification" instance="Aitoc\Core\Observer\PredispatchAdminActionControllerObserver" />
</event>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
<menu>
<add id="Aitoc_Core::menu" title="Aitoc" module="Aitoc_Core" translate="title" sortOrder="81" resource="Aitoc_Core::menu"/>
<add id="Aitoc_Core::common" title="Configuration &amp; Promo" translate="title" module="Aitoc_Core" sortOrder="1498" parent="Aitoc_Core::menu" resource="Aitoc_Core::menu"/>
</menu>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="aitcore" frontName="aitcore">
<module name="Aitoc_Core"/>
</route>
</router>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="aitoc_extensions" translate="label" sortOrder="999998" class="aitoc-tab">
<label>Aitoc Extensions</label>
</tab>
<section id="aitoc_core" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label><![CDATA[Information &amp; Marketplace]]></label>
<tab>aitoc_extensions</tab>
<resource>Aitoc_Core::config</resource>
<group id="menu" translate="label" type="text" sortOrder="15" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Aitoc Menu</label>
<field id="menu_enable" translate="label comment" type="select" sortOrder="25" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable Aitoc Menu</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
<group id="notifications" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Notifications</label>
<field id="notice_enable" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable Notifications</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<comment>Select Yes if you want get latest information about new or updated Aitoc extensions, discounts, atricles and etc.</comment>
</field>
<field id="notice_type" translate="label comment" type="multiselect" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0">
<label>I'd like to be informed by Aitoc about:</label>
<source_model>Aitoc\Core\Model\Config\Source\NoticeType</source_model>
<comment>Select notice types that you want to receive notifications</comment>
<depends>
<field id="notice_enable">1</field>
</depends>
</field>
<field id="frequency" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Update Frequency</label>
<source_model>Aitoc\Core\Model\Config\Source\Frequency</source_model>
</field>
<field id="last_update" translate="label" type="label" sortOrder="40" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Last Update</label>
<frontend_model>Aitoc\Core\Block\System\Config\Form\Field\Notification</frontend_model>
</field>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<aitoc_core>
<notifications>
<notice_type>PROMO,EXTENSION_UPDATE_CUSTOMER,EXTENSION_UPDATE,NEW_EXTENSION,NEWS,TIPS_TRICKS</notice_type>
<notice_enable>1</notice_enable>
<frequency>1</frequency>
</notifications>
<menu>
<menu_enable>1</menu_enable>
</menu>
</aitoc_core>
</default>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Aitoc\Core\Model\VirtualLoggerHandler" type="Magento\Framework\Logger\Handler\Base">
<arguments>
<argument name="fileName" xsi:type="string">/var/log/aitoc.log</argument>
</arguments>
</virtualType>
<type name="Aitoc\Core\Model\Logger">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="system" xsi:type="object">Aitoc\Core\Model\VirtualLoggerHandler</item>
<item name="debug" xsi:type="object">Aitoc\Core\Model\VirtualLoggerHandler</item>
<item name="error" xsi:type="object">Aitoc\Core\Model\VirtualLoggerHandler</item>
<item name="critical" xsi:type="object">Aitoc\Core\Model\VirtualLoggerHandler</item>
</argument>
</arguments>
</type>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
<module name="Aitoc_Core" setup_version="1.0.5" />
</config>
Enabled,Enabled
Disabled,Disabled
"(New version %1 is available in your account: ","(New version %1 is available in your account: "
"Get Update","Get Update"
"1 Day","1 Day"
"5 Days","5 Days"
"10 Days","10 Days"
"Common News","Common News"
Promotions/Discounts,Promotions/Discounts
"My Extensions Updates","My Extensions Updates"
"All Extensions Updates","All Extensions Updates"
"New Extensions","New Extensions"
"Magento Tricks & Tips","Magento Tricks & Tips"
"Disable Notifications","Disable Notifications"
"<style>
.config-nav-block.aitoc-tab .title {
padding-top: 15px;
padding-bottom: 15px;
}
.config-nav-block.aitoc-tab strong:before {
background: no-repeat url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIzLjAuNCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzBCQzdGRjt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTI3MC4zLDMyNy44SDIxOWwyNS42LTYyLjRsLTQxLjEtMTAwbC0xMDcsMjYwLjJjLTIuNyw2LjYsMi4xLDEzLjgsOS4yLDEzLjhoNTMuOWM4LjEsMCwxNS40LTQuOSwxOC41LTEyLjRsMi44LTYuNwoJCQljNi4yLTE1LDIwLjgtMjQuOCwzNy0yNC44aDc2YzEuNCwwLDIuOCwwLjEsNC4yLDAuMkwyNzAuMywzMjcuOHoiLz4KCTwvZz4KPC9nPgo8cGF0aCBkPSJNMjI5LjgsMTM3LjdoNTMuOWM4LjEsMCwxNS40LDQuOSwxOC41LDEyLjRsMTEzLjIsMjc1LjRjMi43LDYuNi0yLjEsMTMuOC05LjIsMTMuOGgtNTMuOWMtOC4xLDAtMTUuNC00LjktMTguNS0xMi40CglMMjIwLjYsMTUxLjVDMjE3LjksMTQ1LDIyMi43LDEzNy43LDIyOS44LDEzNy43eiIvPgo8Y2lyY2xlIGN4PSIyNTYiIGN5PSI3Ny4zIiByPSIzMy4xIi8+Cjwvc3ZnPgo=');
background-size: 28px 28px;
width: 26px;
height: 30px;
content: '';
display: inline-block;
vertical-align: middle;
}
</style>
Aitoc Extensions","<style>
.config-nav-block.aitoc-tab .title {
padding-top: 15px;
padding-bottom: 15px;
}
.config-nav-block.aitoc-tab strong:before {
background: no-repeat url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIzLjAuNCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzBCQzdGRjt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTI3MC4zLDMyNy44SDIxOWwyNS42LTYyLjRsLTQxLjEtMTAwbC0xMDcsMjYwLjJjLTIuNyw2LjYsMi4xLDEzLjgsOS4yLDEzLjhoNTMuOWM4LjEsMCwxNS40LTQuOSwxOC41LTEyLjRsMi44LTYuNwoJCQljNi4yLTE1LDIwLjgtMjQuOCwzNy0yNC44aDc2YzEuNCwwLDIuOCwwLjEsNC4yLDAuMkwyNzAuMywzMjcuOHoiLz4KCTwvZz4KPC9nPgo8cGF0aCBkPSJNMjI5LjgsMTM3LjdoNTMuOWM4LjEsMCwxNS40LDQuOSwxOC41LDEyLjRsMTEzLjIsMjc1LjRjMi43LDYuNi0yLjEsMTMuOC05LjIsMTMuOGgtNTMuOWMtOC4xLDAtMTUuNC00LjktMTguNS0xMi40CglMMjIwLjYsMTUxLjVDMjE3LjksMTQ1LDIyMi43LDEzNy43LDIyOS44LDEzNy43eiIvPgo8Y2lyY2xlIGN4PSIyNTYiIGN5PSI3Ny4zIiByPSIzMy4xIi8+Cjwvc3ZnPgo=');
background-size: 28px 28px;
width: 26px;
height: 30px;
content: '';
display: inline-block;
vertical-align: middle;
}
</style>
Aitoc Extensions"
"Information &amp; Marketplace","Information &amp; Marketplace"
Notifications,Notifications
"Enable Notifications","Enable Notifications"
"Select Yes if you want get latest information about new or updated Aitoc extensions, discounts, atricles and etc.","Select Yes if you want get latest information about new or updated Aitoc extensions, discounts, atricles and etc."
"I'd like to be informed by Aitoc about:","I'd like to be informed by Aitoc about:"
"Select notice types that you want to receive notifications","Select notice types that you want to receive notifications"
"Update Frequency","Update Frequency"
"Last Update","Last Update"
"Installed Extensions","Installed Extensions"
"Aitoc Marketplace","Aitoc Marketplace"
<?php
/**
* Copyright © 2019 Aitoc. All rights reserved.
*/
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Aitoc_Core',
__DIR__
);
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Aitoc_Core::css/config.css"/>
</head>
</page>
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<head>
<css src="Aitoc_Core::css/main.css"/>
</head>
</page>
.aitoc-button-get-new-version {
text-decoration: none;
border-bottom: 2px solid #0bc7ff;
color: #fff;
background-color: #0bc7ff;
border: 2px solid #0bc7ff;
padding: 5px;
height: 55px;
font-size: 16px;
font-weight: 600;
border-radius: 6px;
}
.aitoc-button-get-new-version:hover {
color: #fff;
text-decoration: unset;
}
.comment.aitoc-support {
font-size: 18px;
text-align: center;
margin-bottom: 2rem;
margin-top: 4rem;
}
.config-nav-block.aitoc-tab .title {
padding-top: 15px;
padding-bottom: 15px;
}
.config-nav-block.aitoc-tab strong:before {
background: no-repeat url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIzLjAuNCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzBCQzdGRjt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTI3MC4zLDMyNy44SDIxOWwyNS42LTYyLjRsLTQxLjEtMTAwbC0xMDcsMjYwLjJjLTIuNyw2LjYsMi4xLDEzLjgsOS4yLDEzLjhoNTMuOWM4LjEsMCwxNS40LTQuOSwxOC41LTEyLjRsMi44LTYuNwoJCQljNi4yLTE1LDIwLjgtMjQuOCwzNy0yNC44aDc2YzEuNCwwLDIuOCwwLjEsNC4yLDAuMkwyNzAuMywzMjcuOHoiLz4KCTwvZz4KPC9nPgo8cGF0aCBkPSJNMjI5LjgsMTM3LjdoNTMuOWM4LjEsMCwxNS40LDQuOSwxOC41LDEyLjRsMTEzLjIsMjc1LjRjMi43LDYuNi0yLjEsMTMuOC05LjIsMTMuOGgtNTMuOWMtOC4xLDAtMTUuNC00LjktMTguNS0xMi40CglMMjIwLjYsMTUxLjVDMjE3LjksMTQ1LDIyMi43LDEzNy43LDIyOS44LDEzNy43eiIvPgo8Y2lyY2xlIGN4PSIyNTYiIGN5PSI3Ny4zIiByPSIzMy4xIi8+Cjwvc3ZnPgo=');
background-size: 28px 28px;
width: 26px;
height: 30px;
content: '';
display: inline-block;
vertical-align: middle;
}
.aitoc-get-support-button {
width: 160px;
height: 40px;
border-radius: 16px;
background-color: #7ed321;
line-height: 38px;
font-size: 18px !important;
margin-left: 5px;
text-align: center;
display: inline-block !important;
color: #fff !important;
text-decoration: none;
vertical-align: middle;
font-weight: 600 !important;
}
.label.aitcore-legend {
margin: auto !important;
text-align: center !important;
font-size: 20px !important;
padding: 20px !important;
}
\ No newline at end of file
.aitoc-grid-message .aitoc-notif-logo {
margin-right: 15px;
height: 75px;
width: 60px;
float: left;
display: inline-block;
background: url(../images/logo-53x53.png) no-repeat;
background-position-y: 10px;
background-size: 53px;
border-right: 1px dashed gray;
}
.admin__action-dropdown-menu .notifications-entry[data-aitcore-logo="1"] {
padding-left: 37px;
background: url(../images/logo-30x30.png) no-repeat 5px 15px;
background-size: 30px;
}
div.aitoc-grid-message > div.ambase-grid-message {
padding-left: 0px;
min-height: 0px;
background: unset;
}
.config-nav-block.aitoc-tab .title {
padding-top: 15px;
padding-bottom: 15px;
}
.config-nav-block.aitoc-tab strong:before {
background: no-repeat url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIzLjAuNCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzBCQzdGRjt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTI3MC4zLDMyNy44SDIxOWwyNS42LTYyLjRsLTQxLjEtMTAwbC0xMDcsMjYwLjJjLTIuNyw2LjYsMi4xLDEzLjgsOS4yLDEzLjhoNTMuOWM4LjEsMCwxNS40LTQuOSwxOC41LTEyLjRsMi44LTYuNwoJCQljNi4yLTE1LDIwLjgtMjQuOCwzNy0yNC44aDc2YzEuNCwwLDIuOCwwLjEsNC4yLDAuMkwyNzAuMywzMjcuOHoiLz4KCTwvZz4KPC9nPgo8cGF0aCBkPSJNMjI5LjgsMTM3LjdoNTMuOWM4LjEsMCwxNS40LDQuOSwxOC41LDEyLjRsMTEzLjIsMjc1LjRjMi43LDYuNi0yLjEsMTMuOC05LjIsMTMuOGgtNTMuOWMtOC4xLDAtMTUuNC00LjktMTguNS0xMi40CglMMjIwLjYsMTUxLjVDMjE3LjksMTQ1LDIyMi43LDEzNy43LDIyOS44LDEzNy43eiIvPgo8Y2lyY2xlIGN4PSIyNTYiIGN5PSI3Ny4zIiByPSIzMy4xIi8+Cjwvc3ZnPgo=');
background-size: 28px 28px;
width: 26px;
height: 30px;
margin-left: -3px;
content: '';
display: inline-block;
vertical-align: middle;
}
.admin__menu [data-ui-id^="menu-aitoc-"] .submenu-group-title span:before,
.admin__menu [data-ui-id^="menu-magento-"] [data-ui-id^="menu-magento-"] [data-ui-id^="menu-aitoc-"] span:before {
background: url(../images/logo-30x30.png) no-repeat;
width: 27px;
height: 30px;
content: '';
display: inline-block;
margin-right: 6px;
vertical-align: top;
margin-top: -5px;
}
#menu-aitoc-core-menu span::before {
background: unset;
content: '';
width: 0;
height: 0;
margin-top: 0;
margin-right: 0;
}
#menu-aitoc-core-menu > a:before {
content: '' !important;
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTMiIGhlaWdodD0iNTMiPgoJPHN0eWxlPgoJCXRzcGFuIHsgd2hpdGUtc3BhY2U6cHJlIH0KCQkuc2hwMCB7IGZpbGw6ICNhYWE2YTAgfSAKCTwvc3R5bGU+Cgk8ZyA+CgkJPGcgPgoJCQk8cGF0aCBjbGFzcz0ic2hwMCIgZD0iTTI3MC4zLDMyNy44aC01MS4zbDI1LjYsLTYyLjRsLTQxLjEsLTEwMGwtMTA3LDI2MC4yYy0yLjcsNi42IDIuMSwxMy44IDkuMiwxMy44aDUzLjljOC4xLDAgMTUuNCwtNC45IDE4LjUsLTEyLjRsMi44LC02LjdjNi4yLC0xNSAyMC44LC0yNC44IDM3LC0yNC44aDc2YzEuNCwwIDIuOCwwLjEgNC4yLDAuMnoiIC8+CgkJPC9nPgoJPC9nPgoJPHBhdGggY2xhc3M9InNocDAiIGQ9Ik0yMjkuOCwxMzcuN2g1My45YzguMSwwIDE1LjQsNC45IDE4LjUsMTIuNGwxMTMuMiwyNzUuNGMyLjcsNi42IC0yLjEsMTMuOCAtOS4yLDEzLjhoLTUzLjljLTguMSwwIC0xNS40LC00LjkgLTE4LjUsLTEyLjRsLTExMy4yLC0yNzUuNGMtMi43LC02LjUgMi4xLC0xMy44IDkuMiwtMTMuOHoiIC8+Cgk8cGF0aCBjbGFzcz0ic2hwMCIgZD0iTTI4OS4xLDc3LjNjMCwtMTguMyAtMTQuOCwtMzMuMSAtMzMuMSwtMzMuMWMtMTguMywwIC0zMy4xLDE0LjggLTMzLjEsMzMuMWMwLDE4LjMgMTQuOCwzMy4xIDMzLjEsMzMuMWMxOC4zLDAgMzMuMSwtMTQuOCAzMy4xLC0zMy4xeiIgLz4KPC9zdmc+')
no-repeat center !important;;
background-size: contain !important;;
font-size: 3rem !important;;
height: 3.5rem !important;;
}
#menu-aitoc-core-menu > a:hover:before, #menu-aitoc-core-menu._show > a:before {
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTMiIGhlaWdodD0iNTMiPgoJPHN0eWxlPgoJCXRzcGFuIHsgd2hpdGUtc3BhY2U6cHJlIH0KCQkuc2hwMCB7IGZpbGw6ICNmZmZmZmYgfSAKCTwvc3R5bGU+Cgk8ZyA+CgkJPGcgPgoJCQk8cGF0aCBjbGFzcz0ic2hwMCIgZD0iTTI3MC4zLDMyNy44aC01MS4zbDI1LjYsLTYyLjRsLTQxLjEsLTEwMGwtMTA3LDI2MC4yYy0yLjcsNi42IDIuMSwxMy44IDkuMiwxMy44aDUzLjljOC4xLDAgMTUuNCwtNC45IDE4LjUsLTEyLjRsMi44LC02LjdjNi4yLC0xNSAyMC44LC0yNC44IDM3LC0yNC44aDc2YzEuNCwwIDIuOCwwLjEgNC4yLDAuMnoiIC8+CgkJPC9nPgoJPC9nPgoJPHBhdGggY2xhc3M9InNocDAiIGQ9Ik0yMjkuOCwxMzcuN2g1My45YzguMSwwIDE1LjQsNC45IDE4LjUsMTIuNGwxMTMuMiwyNzUuNGMyLjcsNi42IC0yLjEsMTMuOCAtOS4yLDEzLjhoLTUzLjljLTguMSwwIC0xNS40LC00LjkgLTE4LjUsLTEyLjRsLTExMy4yLC0yNzUuNGMtMi43LC02LjUgMi4xLC0xMy44IDkuMiwtMTMuOHoiIC8+Cgk8cGF0aCBjbGFzcz0ic2hwMCIgZD0iTTI4OS4xLDc3LjNjMCwtMTguMyAtMTQuOCwtMzMuMSAtMzMuMSwtMzMuMWMtMTguMywwIC0zMy4xLDE0LjggLTMzLjEsMzMuMWMwLDE4LjMgMTQuOCwzMy4xIDMzLjEsMzMuMWMxOC4zLDAgMzMuMSwtMTQuOCAzMy4xLC0zMy4xeiIgLz4KPC9zdmc+')
no-repeat center;
background-size: contain;
}
.admin__action-dropdown-menu .notifications-entry[data-aitcore-logo="1"]:not(.notifications-entry-last) {
padding-left: 37px;
background: url(../images/logo-30x30.png) no-repeat 5px 5px;
background-size: 30px;
}
.admin__action-dropdown-menu .notifications-entry:not(.notifications-entry-last) {
border-bottom: 1px dashed gray;
}
div.aitoc-grid-message span.grid-row-title {
font-weight: 600;
}
\ No newline at end of file
<?php
namespace Aitoc\Smtp\Api\Data;
/**
* Interface LogInterface
* @package Aitoc\Smtp\Api\Data
*/
interface LogInterface
{
/**
* Constants
*/
const TABLE_NAME = 'aitoc_smtp_log';
const LOG_ID = 'log_id';
const CREATED_AT = 'created_at';
const SUBJECT = 'subject';
const EMAIL_BODY = 'email_body';
const SENDER_EMAIL = 'sender_email';
const RECIPIENT_EMAIL = 'recipient_email';
const CC = 'cc';
const BCC = 'bcc';
const STATUS = 'status';
const STATUS_MESSAGE = 'status_message';
/**
* @return int
*/
public function getLogId();
/**
* @param int $logId
* @return LogInterface
*/
public function setLogId($logId);
}
<?php
namespace Aitoc\Smtp\Block\Adminhtml\Log;
use Aitoc\Smtp\Controller\RegistryConstants;
class Preview extends \Magento\Backend\Block\Widget
{
const LOG_PARAM_URL_KEY = 'log_id';
/**
* @var \Magento\Framework\Filter\Input\MaliciousCode
*/
protected $_maliciousCode;
/**
* @var \Aitoc\Smtp\Model\LogFactory
*/
private $logFactory;
/**
* @var \Aitoc\Smtp\Model\Config
*/
private $config;
/**
* @var string
*/
protected $profilerName = 'email_template_proccessing';
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode
* @param \Magento\Email\Model\TemplateFactory $emailFactory
* @param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Filter\Input\MaliciousCode $maliciousCode,
\Aitoc\Smtp\Model\LogFactory $logFactory,
\Aitoc\Smtp\Model\Config $config,
array $data = []
) {
$this->logFactory = $logFactory;
$this->_maliciousCode = $maliciousCode;
$this->config = $config;
parent::__construct($context, $data);
}
/**
* @return mixed|string
* @throws \Exception
*/
protected function _toHtml()
{
$logModel = $this->getCurrentLog();
$string = $logModel->getEmailBody();
if ($this->config->isNewSender(RegistryConstants::VERSION_COMPARISON_NEW_MAIL)) {
$string = quoted_printable_decode($string);
}
if ($logModel->getId()) {
return '<iframe onload="resizeIframe(this)" srcdoc="'. $string . '" style="width: 100%; height: 100%"></iframe>';
} else {
throw new \Exception('Log with ID not found. Pleas try again');
}
return $logModel;
}
/**
* @return mixed
*/
public function getCurrentLog()
{
return $this->logFactory->create()->getLogById($this->getRequest()->getParam(self::LOG_PARAM_URL_KEY));
}
/**
* Get either default or any store view
*
* @return \Magento\Store\Model\Store|null
*/
protected function getAnyStoreView()
{
$store = $this->_storeManager->getDefaultStoreView();
if ($store) {
return $store;
}
foreach ($this->_storeManager->getStores() as $store) {
return $store;
}
return null;
}
}
<?php
namespace Aitoc\Smtp\Block\Adminhtml;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Provider extends Field
{
protected $_template = 'Aitoc_Smtp::config/provider.phtml';
/**
* @var \Aitoc\Smtp\Model\Providers
*/
private $providers;
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Aitoc\Smtp\Model\Providers $providers,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
array $data = []
) {
$this->providers = $providers;
$this->jsonEncoder = $jsonEncoder;
parent::__construct($context, $data);
}
/**
* @param AbstractElement $element
* @return string
* @throws \Magento\Framework\Exception\LocalizedException
*/
protected function _getElementHtml(AbstractElement $element)
{
/** @var \Magento\Backend\Block\Template $block */
$block = $this->_layout->createBlock(\Magento\Backend\Block\Template::class);
$block->setTemplate($this->_template)->setData('providers', $this->getProviderData());
$html = parent::_getElementHtml($element);
$html .= $block->toHtml();
return $html;
}
/**
* @return string
*/
public function getProviderData()
{
return $this->jsonEncoder->encode($this->providers->getAllProviders());
}
}
<?php
namespace Aitoc\Smtp\Block\Adminhtml;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
class TestButton extends Field
{
const TEMPLATE_PATH = 'Aitoc_Smtp::config/button.phtml';
const CHECK_BUTTON_ID = 'aitoc_send_button';
/**
* @param AbstractElement $element
* @return string
* @throws \Magento\Framework\Exception\LocalizedException
*/
protected function _getElementHtml(AbstractElement $element)
{
/** @var \Magento\Backend\Block\Template $block */
$block = $this->_layout->createBlock(\Magento\Backend\Block\Template::class);
$button = $this->getLayout()->createBlock(
\Magento\Backend\Block\Widget\Button::class
)->setData([
'id' => self::CHECK_BUTTON_ID,
'label' => __('Send Test Email'),
'class' => 'primary'
]);
$block
->setTemplate(self::TEMPLATE_PATH)
->setData('send_button', $button->toHtml())
->setData('ajax_url', $this->getAjaxUrl())
->setData('button_id', self::CHECK_BUTTON_ID);
return $block->toHtml();
}
/**
* Render scope label
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
* @return string
*/
protected function _renderScopeLabel(\Magento\Framework\Data\Form\Element\AbstractElement $element)
{
return '';
}
/**
* @return string
*/
private function getAjaxUrl()
{
return $this->getUrl('aitoc_smtp/smtp/test', ['_current' => true]);
}
/**
* @return string
*/
public function getCheckButtonId()
{
return self::CHECK_BUTTON_ID;
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml;
use Magento\Backend\App\Action;
use Aitoc\Smtp\Controller\RegistryConstants;
abstract class Log extends Action
{
const DEFAULT_ERROR_MESSAGE = 'Something went wrong. See the error log.';
/** @var \Magento\Framework\View\Result\PageFactory */
protected $resultPageFactory;
/** @var \Magento\Framework\Registry */
protected $registry;
/** @var \Aitoc\ShippingRules\Model\RuleFactory */
protected $ruleFactory;
const ADMIN_RESOURCE = 'Aitoc_Smtp::log';
/**
* Rule constructor.
*
* @param Action\Context $context
* @param \Aitoc\ShippingRules\Model\RuleFactory $ruleFactory
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Aitoc\Smtp\Model\LogFactory $ruleFactory,
\Magento\Framework\Registry $registry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->ruleFactory = $ruleFactory;
$this->registry = $registry;
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* @return \Magento\Backend\Model\View\Result\Page
*/
protected function initPage()
{
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$resultPage->setActiveMenu('Aitoc_Smtp::log');
$resultPage->addBreadcrumb(__('Reports'), __('Reports'));
return $resultPage;
}
/**
* @param bool $requireId
* @return \Aitoc\Smtp\Model\Log|\Magento\Framework\App\ResponseInterface
*/
protected function getRule($requireId = false)
{
$ruleId = $this->getRequest()->getParam(RegistryConstants::RULE_PARAM_URL_KEY);
if ($requireId && !$ruleId) {
$this->messageManager->addErrorMessage(__('Log doesn\'t exist.'));
return $this->redirectIndex();
}
$model = $this->ruleFactory->create();
if ($ruleId) {
$model->load($ruleId);
}
if ($ruleId && !$model->getId()) {
$this->messageManager->addErrorMessage(__('Log doesn\'t exist.'));
return $this->redirectIndex();
}
$this->registry->register(RegistryConstants::CURRENT_RULE, $model);
return $model;
}
/**
* @return \Magento\Framework\App\ResponseInterface
*/
protected function redirectIndex()
{
return $this->_redirect('*/*/');
}
/**
* @return bool
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Aitoc_Smtp::log');
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
use Aitoc\Smtp\Model\ResourceModel\Log\CollectionFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
class Clear extends \Magento\Backend\App\Action
{
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param CollectionFactory $collectionFactory
*/
public function __construct(
Context $context,
CollectionFactory $collectionFactory
) {
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
$collection = $this->collectionFactory->create();
$items = $collection->getItems();
$count = count($items);
if ($items) {
foreach ($items as $item) {
$item->delete();
}
}
$this->messageManager->addSuccess(__('A total of %1 email(s) have been deleted.', $count));
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('aitoc_smtp/*/index');
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
class Delete extends \Aitoc\Smtp\Controller\Adminhtml\Log
{
/**
* {@inheritdoc}
*/
public function execute()
{
if ($data = $this->getRequest()->getParams()) {
$rule = $this->getRule();
try {
if ($rule->getId()) {
$rule->delete();
$this->messageManager->addSuccessMessage(
__('Email Log has been successfully deleted')
);
} else {
$this->messageManager->addErrorMessage(__('Unable to find the rule'));
}
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(__(self::DEFAULT_ERROR_MESSAGE));
}
}
return $this->_redirect($this->_redirect->getRefererUrl());
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
class Index extends \Aitoc\Smtp\Controller\Adminhtml\Log
{
/**
*
* @return \Magento\Backend\Model\View\Result\Page
*/
public function execute()
{
$resultPage = $this->initPage();
$resultPage->getConfig()->getTitle()->prepend(__('Emails Log'));
return $resultPage;
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
use Aitoc\Smtp\Model\ResourceModel\Log\CollectionFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
class MassDelete extends \Magento\Backend\App\Action
{
/**
* Massactions filter
*
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(
Context $context,
Filter $filter,
CollectionFactory $collectionFactory
) {
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->delete();
}
$this->messageManager->addSuccess(__('A total of %1 email(s) have been deleted.', $collectionSize));
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('aitoc_smtp/*/index');
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
use Aitoc\Smtp\Model\ResourceModel\Log\CollectionFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Aitoc\Smtp\Model\Sender;
use Magento\Framework\Registry;
use Aitoc\Smtp\Controller\RegistryConstants;
class MassResend extends \Magento\Backend\App\Action
{
/**
* Massactions filter
*
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @var Sender
*/
private $sender;
/**
* @var Registry
*/
private $registry;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(
Context $context,
Filter $filter,
Sender $sender,
Registry $registry,
CollectionFactory $collectionFactory
)
{
$this->filter = $filter;
$this->registry = $registry;
$this->sender = $sender;
$this->collectionFactory = $collectionFactory;
parent::__construct($context);
}
/**
* @return \Magento\Backend\Model\View\Result\Redirect
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$resCount = 0;
if ($collection->getSize()) {
$this->registry->register(RegistryConstants::CURRENT_RULE, true);
}
foreach ($collection as $item) {
if ($this->sender->sendByLogId($item->getLogId())) {
$resCount += 1;
}
}
$this->messageManager->addSuccess(__('A total of %1 email(s) have been sent.', $resCount));
return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath('aitoc_smtp/*/index');
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
/**
* View a rendered template.
*/
class Preview extends \Aitoc\Smtp\Controller\Adminhtml\Log
{
/**
* Preview Newsletter template
*
* @return void|$this
*/
public function execute()
{
try {
$this->_view->loadLayout();
$this->_view->getPage()->getConfig()->getTitle()->prepend(__('Email Preview'));
$this->_view->renderLayout();
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(
__('An error occurred. The email template can not be opened for preview.')
);
$this->_redirect('*/*/');
}
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Log;
use Aitoc\Smtp\Controller\RegistryConstants;
class Resend extends \Aitoc\Smtp\Controller\Adminhtml\Log
{
/**
* @var \Aitoc\Smtp\Model\Sender
*/
private $sender;
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Aitoc\Smtp\Model\LogFactory $ruleFactory,
\Magento\Framework\Registry $registry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory,
\Aitoc\Smtp\Model\Sender $sender
) {
parent::__construct($context, $ruleFactory, $registry, $resultPageFactory);
$this->sender = $sender;
}
/**
* {@inheritdoc}
*/
public function execute()
{
$logId = $this->getRequest()->getParam(RegistryConstants::RULE_PARAM_URL_KEY);
$log = $this->getRule();
if ($logId) {
try {
if ($this->sender->sendByLogId($logId)) {
$this->messageManager->addSuccessMessage(__('Email successfully was send.'));
} else {
$this->messageManager->addErrorMessage(__('Something went wrong.'));
}
} catch (\Magento\Framework\Exception\LocalizedException $e) {
$this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(__(self::DEFAULT_ERROR_MESSAGE));
}
} else {
$this->messageManager->addErrorMessage(__('Unable to find the rule'));
}
return $this->_redirect($this->_redirect->getRefererUrl());
}
}
<?php
namespace Aitoc\Smtp\Controller\Adminhtml\Smtp;
use Exception;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Email\Model\Template\SenderResolver;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\LocalizedException;
use Psr\Log\LoggerInterface;
use Aitoc\Smtp\Controller\RegistryConstants;
class Test extends Action
{
/**
* Authorization level of a basic admin session
*
* @see _isAllowed()
*/
const ADMIN_RESOURCE = 'Aitoc_Smtp::main';
const TEST_EMAIL_TO_FIELD_NAME = 'test_email_to';
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var TransportBuilder
*/
private $_transportBuilder;
/**
* @var SenderResolver
*/
private $senderResolver;
/**
* @var \Magento\Framework\Json\EncoderInterface
*/
private $jsonEncoder;
/**
* @var Factory
*/
private $configFactory;
/**
* @var \Aitoc\Smtp\Model\Config
*/
private $config;
/**
* @var \Magento\Framework\Mail\TransportInterfaceFactory
*/
private $transportInterfaceFactory;
public function __construct(
Context $context,
LoggerInterface $logger,
SenderResolver $senderResolver,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
\Magento\Config\Model\Config\Factory $configFactory,
\Aitoc\Smtp\Model\Config $config,
\Magento\Framework\Mail\TransportInterfaceFactory $transportInterfaceFactory
) {
$this->logger = $logger;
$this->transportInterfaceFactory = $transportInterfaceFactory;
$this->senderResolver = $senderResolver;
$this->jsonEncoder = $jsonEncoder;
$this->configFactory = $configFactory;
$this->config = $config;
parent::__construct($context);
}
/**
* @return ResponseInterface|ResultInterface
* @throws LocalizedException
*/
public function execute()
{
$data = $this->getRequest()->getParams();
try {
if ($data && isset($data[self::TEST_EMAIL_TO_FIELD_NAME])) {
$config = $this->config->convertFromPostToSmtpParams($this->removeUnusedFields($data));
$config[RegistryConstants::IS_TEST_FIELD_ARRAY] = true;
unset($config[self::TEST_EMAIL_TO_FIELD_NAME]);
$transport = $this->transportInterfaceFactory->create($config);
$transport->testSend($data[self::TEST_EMAIL_TO_FIELD_NAME]);
$result = [
'status' => true,
'content' => __('Message is successfully send!')
];
} else {
$result = [
'status' => false,
'content' => __('Error. Something went wrong. Please, try again.')
];
}
} catch (LocalizedException $exception) {
$result = [
'status' => false,
'content' => $exception->getMessage()
];
} catch (\Exception $exception) {
$result = [
'status' => false,
'content' => $exception->getMessage()
];
}
return $this->getResponse()->representJson($this->jsonEncoder->encode($result));
}
/**
* @param $data
* @return mixed
*/
private function removeUnusedFields($data)
{
$fields = ['key', 'section', 'isAjax', 'form_key'];
foreach ($fields as $field) {
if (isset($data[$field]) && $data[$field]) {
unset($data[$field]);
}
}
return $data;
}
/**
* @return bool
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Aitoc_Smtp::main');
}
}
<?php
namespace Aitoc\Smtp\Controller;
class RegistryConstants
{
const RULE_PARAM_URL_KEY = 'log_id';
const CURRENT_RULE = 'aitoc_current_smtp_log';
const IS_TEST_FIELD_ARRAY = 'is_test';
const VERSION_COMPARISON_OLD_MAIL = '2.2.8';
const VERSION_COMPARISON_NEW_MAIL = '2.3.3';
const RESEND_EMAIL_TEMPLATE_ID = 'aitsmtp_resend_template';
}
<?php
namespace Aitoc\Smtp\Cron;
use Exception;
use Aitoc\Core\Model\Helpers\Date;
use Aitoc\Smtp\Model\Config;
use Aitoc\Smtp\Model\ResourceModel\Log\CollectionFactory;
use Psr\Log\LoggerInterface;
class Clear
{
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var Config
*/
private $config;
/**
* @var CollectionFactory
*/
private $collectionFactory;
/**
* @var Date
*/
private $date;
public function __construct(
LoggerInterface $logger,
CollectionFactory $collectionFactory,
Date $date,
Config $config
) {
$this->logger = $logger;
$this->date = $date;
$this->collectionFactory = $collectionFactory;
$this->config = $config;
}
/**
* @return $this
*/
public function execute()
{
if (!$this->config->enabled()) {
return $this;
}
$cleanDays = $this->config->cleanLogDays();
if ($cleanDays) {
$logCollection = $this->collectionFactory->create()
->addFieldToFilter('created_at', [
'lteq' => $this->date->getCurrentDateBeforeDays($cleanDays)
]);
foreach ($logCollection as $log) {
try {
$log->delete();
} catch (Exception $e) {
$this->logger->critical($e);
}
}
}
return $this;
}
}
<?php
namespace Aitoc\Smtp\Model;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\ScopeInterface;
use Magento\Email\Model\Template\SenderResolver;
use Magento\Framework\App\ProductMetadataInterface;
use Aitoc\Smtp\Controller\RegistryConstants;
/**
* Class Config
* @package Aitoc\Smtp\Model
*/
class Config
{
/**
* Constants
*/
const MODULE_NAME = 'aitsmtp';
const XML_PATH_TEMPLATE_ENABLED = 'aitsmtp/general/enabled';
const XML_PATH_TEMPLATE_SMTP = 'aitsmtp/smtp/';
const XML_PATH_TEMPLATE_EMAILS = 'aitsmtp/emails/';
const XML_PATH_TEMPLATE_DEBUG = 'aitsmtp/debug/';
const XML_PATH_TEMPLATE_LOG = 'aitsmtp/log/';
const ENCRYPTED_PASSWORD_VALUE = '******';
/**
* @var \Aitoc\Core\Helper\Config
*/
private $config;
/**
* @var \Magento\Framework\App\RequestInterface
*/
private $request;
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
private $storeManager;
/**
* @var Config\Options\Protocol
*/
private $protocol;
/**
* @var Config\Options\Auth
*/
private $auth;
/**
* @var \Magento\Framework\Encryption\EncryptorInterface
*/
private $encryptor;
/**
* @var SenderResolver
*/
private $senderResolver;
/**
* @var ProductMetadataInterface
*/
private $productMetadata;
/**
* @var array
*/
private $smtpFields = [
'name', 'host', 'port', 'protocol', 'login', 'password', 'auth_type'
];
public function __construct(
\Aitoc\Core\Helper\Config $config,
\Magento\Framework\App\RequestInterface $request,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Aitoc\Smtp\Model\Config\Options\Protocol $protocol,
\Aitoc\Smtp\Model\Config\Options\Auth $auth,
\Magento\Framework\Encryption\EncryptorInterface $encryptor,
SenderResolver $senderResolver,
ProductMetadataInterface $productMetadata
) {
$this->config = $config;
$this->request = $request;
$this->protocol = $protocol;
$this->storeManager = $storeManager;
$this->auth = $auth;
$this->encryptor = $encryptor;
$this->senderResolver = $senderResolver;
$this->productMetadata = $productMetadata;
}
/**
* @return bool
*/
public function enabled()
{
return (bool)$this->config->getModuleConfig(self::XML_PATH_TEMPLATE_ENABLED, $this->getCurrentStoreId());
}
/**
* @return int|mixed
*/
public function getCurrentStoreId()
{
$storeId = 0;
$requestStore = $this->request->getParam(ScopeInterface::SCOPE_STORE);
$requestWebsite = $this->request->getParam(ScopeInterface::SCOPE_WEBSITE);
try {
if ($requestStore) {
$storeId = $requestStore;
} elseif ($requestWebsite) {
$storeId = $this->storeManager->getWebsite($requestWebsite)->getDefaultStore()->getId();
} else {
$storeId = $this->storeManager->getStore()->getId();
}
} catch (LocalizedException $exception) {
} catch (NoSuchEntityException $e) {
}
return $storeId;
}
/**
* @return array
*/
public function getCcEmails()
{
$allowedEmailsString = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_EMAILS . 'cc',
$this->getCurrentStoreId()
);
return $this->getDataFromString($allowedEmailsString);;
}
/**
* @return array|bool
*/
public function getBccEmails()
{
$allowedEmailsString = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_EMAILS . 'bcc',
$this->getCurrentStoreId()
);
return $this->getDataFromString($allowedEmailsString);
}
/**
* @return array|bool
*/
protected function _getExceptionalEmails()
{
$allowedEmailsString = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_DEBUG . 'exeptional_email_addresses',
$this->getCurrentStoreId()
);
$arrayFromString = explode(',', $allowedEmailsString);
array_walk($arrayFromString, [$this, 'trimAllowedValues']);
return $arrayFromString;
}
/**
* @return array|bool
*/
protected function _getExceptionalDomains()
{
$allowedDomainsString = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_DEBUG . 'exeptional_domains',
$this->getCurrentStoreId()
);
$arrayFromString = explode(',', $allowedDomainsString);
array_walk($arrayFromString, [$this, 'trimAllowedValues']);
return $arrayFromString;
}
/**
* @param $string
* @return array|bool
*/
private function getDataFromString($string)
{
if ($string) {
$result = [];
$arrayFromString = explode(',', $string);
array_walk($arrayFromString, [$this, 'trimAllowedValues']);
foreach ($arrayFromString as $item) {
$result[] = [
'email' => $item,
'name' => null
];
}
return $result;
}
return false;
}
/**
* @param $allowedValuesArray
*/
public function trimAllowedValues(&$allowedValuesArray)
{
$allowedValuesArray = trim($allowedValuesArray);
}
/**
* @param $email
* @return bool
*/
public function needToBlockEmail($email)
{
if ($this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_DEBUG . 'delivery',
$this->getCurrentStoreId()
)) {
$domains = $this->_getExceptionalDomains();
$emails = $this->_getExceptionalEmails();
if (($emails && in_array($email, $emails)) || (is_array($domains) && $this->validDomain($email, $domains)))
{
return false;
}
return true;
}
return false;
}
/**
* @param $postParams
* @return array
*/
public function convertFromPostToSmtpParams($postParams)
{
$result = [];
foreach ($postParams as $key => $item) {
switch ($key) {
case 'protocol':
$key = 'ssl';
$item = $this->protocol->getOptionById($item);
break;
case 'auth_type':
$key = 'auth';
$item = $this->auth->getOptionById($item);
break;
case 'login':
$key = 'username';
break;
case 'password':
if ($item == self::ENCRYPTED_PASSWORD_VALUE) {
$configValue = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_SMTP . 'password',
$this->getCurrentStoreId()
);
$item = $this->encryptor->decrypt($configValue);
}
break;
default:
break;
}
$result[$key] = $item;
}
return $result;
}
/**
* @return bool
*/
public function isBlockedDelivery()
{
return (bool)$this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_DEBUG . 'delivery',
$this->getCurrentStoreId()
);
}
/**
* @return bool
*/
public function logEnabled()
{
return (bool)$this->config->getModuleConfig(self::XML_PATH_TEMPLATE_LOG . 'log', $this->getCurrentStoreId());
}
/**
* @return bool
*/
public function plainEnabled()
{
return (bool)$this->config->getModuleConfig(self::XML_PATH_TEMPLATE_EMAILS . 'plain', $this->getCurrentStoreId());
}
/**
* @return int
*/
public function cleanLogDays()
{
return (int)$this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_LOG . 'log_clean',
$this->getCurrentStoreId()
);
}
/**
* @param $message
* @param bool $isTest
* @return mixed
*/
public function prepareMessageToSend($message, $isTest = false)
{
$senderData = $this->getAnotherEmailSenderData();
if (!$senderData && $isTest) {
$senderData = $this->getSenderData();
}
if ($senderData) {
$message->setFrom($this->getNewAddress($senderData));
}
if (!$isTest) {
$ccEmails = $this->getCcEmails();
$bccEmails = $this->getBccEmails();
if ($ccEmails) {
$message->addCc($this->getAddressList($ccEmails));
}
if ($bccEmails) {
$message->addBcc($this->getAddressList($bccEmails));
}
}
return $message;
}
/**
* @param $emailData
* @return \Zend\Mail\Address
*/
public function getNewAddress($emailData){
return new \Zend\Mail\Address($emailData['email'], $emailData['name']);
}
/**
* @param $emailsData
* @return \Zend\Mail\AddressList
*/
public function getAddressList($emailsData) {
$addressList = new \Zend\Mail\AddressList();
foreach ($emailsData as $data) {
$addressList->add($data['email'], isset($data['name']) ? $data['name'] : null);
}
return $addressList;
}
/**
* @param string $type
* @return array|string
* @throws \Magento\Framework\Exception\MailException
*/
public function getSenderData($type = 'general')
{
return $this->senderResolver->resolve($type, $this->getCurrentStoreId());
}
/**
* @param $email
* @param $domains
* @return bool
*/
protected function validDomain($email, $domains)
{
$regExp = '/^(http:\/\/|https:\/\/){0,1}(www\.){0,1}([0-9a-z_\-\.]*[0-9a-z]*\.[a-z]{2,5})$/iu';
$emailDomain = substr(strrchr($email, "@"), 1);
foreach ($domains as $domain) {
if (preg_match($regExp, $domain, $parts) && $emailDomain == $parts[3]) {
return true;
}
}
return false;
}
/**
* @return array
*/
public function getAnotherEmailSenderData()
{
$result = [];
if ($this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_EMAILS . 'sender_enable',
$this->getCurrentStoreId()
)) {
$senderEmail = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_EMAILS . 'sender_email',
$this->getCurrentStoreId()
);
$senderName = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_EMAILS . 'sender_name',
$this->getCurrentStoreId()
);
if ($senderEmail) {
$senderName = $senderName ?: null;
$result = [
'name' => $senderName,
'email' => $senderEmail
];
}
}
return $result;
}
/**
* @return array
*/
public function getFullConfig()
{
$result = [];
foreach ($this->smtpFields as $field) {
$configValue = $this->config->getModuleConfig(
self::XML_PATH_TEMPLATE_SMTP . $field,
$this->getCurrentStoreId()
);
if ($configValue === null) {
continue;
}
if ($field == 'protocol') {
if (!$configValue) {
continue;
}
$field = 'ssl';
$configValue = $this->protocol->getOptionById($configValue);
}
if ($field == 'login') {
$field = 'username';
}
if ($field == 'password') {
$configValue = $this->encryptor->decrypt($configValue);
}
if ($field == 'auth_type') {
$field = 'auth';
$configValue = $this->auth->getOptionById($configValue);
}
$result[$field] = $configValue;
}
return $result;
}
/**
* @param $version
* @return mixed
*/
public function isNewSender($version)
{
$currentVersion = $this->productMetadata->getVersion();
return version_compare($currentVersion, $version, '>=');
}
}
<?php
namespace Aitoc\Smtp\Model\Config\Options;
use Magento\Framework\Option\ArrayInterface;
class Auth implements ArrayInterface
{
const AUTH_NONE = 0;
const AUTH_LOGIN = 1;
const AUTH_MD = 2;
const AUTH_LOGIN_VALUE = 'login';
const AUTH_MD_VALUE = 'md';
/**
* @return array
*/
public function toOptionArray()
{
return [
[
'value' => self::AUTH_NONE,
'label' => __('Not Required')
],
[
'value' => self::AUTH_LOGIN,
'label' => __('Login/Password')
],
[
'value' => self::AUTH_MD,
'label' => __('CRAM-MD5')
]
];
}
/**
* @param $id
* @return string
*/
public function getOptionById($id)
{
$value = '';
switch ($id) {
case 1:
$value = self::AUTH_LOGIN_VALUE;
break;
case 2:
$value = self::AUTH_MD_VALUE;
break;
}
return $value;
}
}
<?php
namespace Aitoc\Smtp\Model\Config\Options;
use Magento\Framework\Option\ArrayInterface;
class Protocol implements ArrayInterface
{
const PROTOCOL_NONE = 0;
const PROTOCOL_SSL = 1;
const PROTOCOL_TLS = 2;
const PROTOCOL_SSL_VALUE = 'ssl';
const PROTOCOL_TLS_VALUE = 'tls';
/**
* @return array
*/
public function toOptionArray()
{
return [
[
'value' => self::PROTOCOL_NONE,
'label' => __('None')
],
[
'value' => self::PROTOCOL_SSL,
'label' => __('SSL')
],
[
'value' => self::PROTOCOL_TLS,
'label' => __('TLS')
]
];
}
/**
* @param $id
* @return string
*/
public function getOptionById($id)
{
$value = '';
switch ($id) {
case 1:
$value = self::PROTOCOL_SSL_VALUE;
break;
case 2:
$value = self::PROTOCOL_TLS_VALUE;
break;
}
return $value;
}
}
<?php
namespace Aitoc\Smtp\Model\Config\Options;
use Magento\Framework\Option\ArrayInterface;
class Provider implements ArrayInterface
{
/**
* @var \Aitoc\Smtp\Model\Providers
*/
private $providers;
public function __construct(
\Aitoc\Smtp\Model\Providers $providers
) {
$this->providers = $providers;
}
public function toOptionArray()
{
$result = [];
$result[] = [
'value' => 'none',
'label' => 'None'
];
foreach ($this->providers->getAllProviders() as $key => $providerData) {
$result[] = [
'value' => $key,
'label' => $providerData['label']
];
}
return $result;
}
}
<?php
namespace Aitoc\Smtp\Model\Config\Options;
use Magento\Framework\Option\ArrayInterface;
class Status implements ArrayInterface
{
const STATUS_SUCCESS = 0;
const STATUS_FAILED = 1;
const STATUS_BLOCKED = 2;
/**
* @return array
*/
public function toOptionArray()
{
return [
[
'value' => self::STATUS_SUCCESS,
'label' => __('Success')
],
[
'value' => self::STATUS_FAILED,
'label' => __('Failed')
],
[
'value' => self::STATUS_BLOCKED,
'label' => __('Blocked')
]
];
}
/**
* @param $status
* @return mixed|string
*/
public function getLabelByStatus($status) {
foreach ($this->toOptionArray() as $item) {
if ($item['value'] == $status) {
return $item['label'];
}
}
return '';
}
}
<?php
namespace Aitoc\Smtp\Model\Framework\Mail;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Phrase;
use Zend\Mail\Message as ZendMessage;
use Zend\Mail\Address;
use Zend\Mail\Transport\Smtp;
use Magento\Framework\Mail\MessageInterface;
use Magento\Email\Model\Template\SenderResolver;
use Aitoc\Smtp\Model\Config;
use Zend\Mail\Transport\SmtpOptions;
use Aitoc\Smtp\Model\Resolver\From;
use Aitoc\Smtp\Model\LogFactory;
use Aitoc\Smtp\Model\Config\Options\Status;
use Magento\Framework\Registry;
use Aitoc\Smtp\Controller\RegistryConstants;
use Zend_Mail_Transport_Smtp;
class Transport implements \Magento\Framework\Mail\TransportInterface
{
const DEFAULT_LOCAL_CLIENT_HOSTNAME = 'localhost';
const TEST_MESSAGE_SUBJECT = 'Aitoc SMTP Test';
const TEST_MESSAGE_BODY =
"Now, your store uses an Aitoc SMTP. Please, hit ‘Save Config’ to use this connection.";
/**
* @var Sendmail
*/
private $zendTransport;
/**
* @var MessageInterface
*/
private $message;
/**
* @var array
*/
private $config;
/**
* @var Config
*/
private $aitConfig;
/**
* @var SenderResolver
*/
private $senderResolver;
/**
* @var From
*/
private $fromResolver;
/**
* @var LogFactory
*/
private $logFactory;
/**
* @var Registry
*/
private $registry;
public function __construct(
$message,
SenderResolver $senderResolver,
Config $aitConfig,
From $from,
LogFactory $logFactory,
Registry $registry,
array $config = []
) {
$this->config = $config;
$this->aitConfig = $aitConfig;
$this->senderResolver = $senderResolver;
$this->fromResolver = $from;
$this->logFactory = $logFactory;
$this->registry = $registry;
if ($this->aitConfig->isNewSender(RegistryConstants::VERSION_COMPARISON_OLD_MAIL)) {
$this->zendTransport = new Smtp($this->prepareOptions($config));
} else {
$this->zendTransport = new Zend_Mail_Transport_Smtp($config['host'], $config);
}
$this->message = $message;
$this->setFrom();
}
/**
* @return $this
*/
public function setFrom()
{
$fromData = $this->fromResolver->getFrom();
$message = ZendMessage::fromString($this->message->getRawMessage());
if ($fromData) {
if (($message instanceof ZendMessage && !$message->getFrom()->count())
|| ((is_array($message->getHeaders()) && !array_key_exists("From", $message->getHeaders())))
) {
$this->message->setFrom($this->aitConfig->getNewAddress($fromData));
}
}
return $this;
}
/**
* @param $config
* @return SmtpOptions
*/
private function prepareOptions($config)
{
if (!isset($config['name']) || !$config['name']) {
$config['name'] = self::DEFAULT_LOCAL_CLIENT_HOSTNAME;
}
$options = new SmtpOptions([
'name' => isset($config['name']) ? $config['name'] : '',
'host' => isset($config['host']) ? $config['host'] : '',
'port' => isset($config['port']) ? $config['port'] : 465,
]);
$connectionConfig = [];
if (isset($config['auth']) && $config['auth'] != '') {
$options->setConnectionClass($config['auth']);
$connectionConfig = [
'username' => isset($config['username']) ? $config['username'] : '',
'password' => isset($config['password']) ? $config['password'] : ''
];
}
if (isset($config['ssl']) && $config['ssl']) {
$connectionConfig['ssl'] = $config['ssl'];
}
if (!empty($connectionConfig)) {
$options->setConnectionConfig($connectionConfig);
}
return $options;
}
/**
* @inheritdoc
*/
public function sendMessage()
{
$logDisabled = $this->registry->registry(RegistryConstants::CURRENT_RULE) ? true : false;
try {
$this->message = $this->aitConfig->prepareMessageToSend($this->getMessage());
if ($this->aitConfig->isNewSender(RegistryConstants::VERSION_COMPARISON_OLD_MAIL)) {
$message = ZendMessage::fromString($this->message->getRawMessage())->setEncoding('utf-8');
} else {
$message = $this->message;
}
if ($this->aitConfig->isBlockedDelivery()) {
$modifiedRecipient = $this->modifyTo();
if (!$modifiedRecipient) {
$errorData = [
'status' => Status::STATUS_BLOCKED,
'status_message' => 'Debug mode'
];
if (!$logDisabled) {
$this->getLoger()->log($message, $errorData);
return;
}
}
$message = $modifiedRecipient;
}
$this->zendTransport->send($message);
if (!$logDisabled) {
$this->getLoger()->log($message);
}
} catch (\Exception $e) {
$errorData = [
'status' => Status::STATUS_FAILED,
'status_message' => $e->getMessage()
];
if (!$logDisabled) {
$this->getLoger()->log($message, $errorData);
}
throw new MailException(new Phrase($e->getMessage()), $e);
}
}
/**
* @return \Aitoc\Smtp\Model\Log
*/
private function getLoger()
{
return $this->logFactory->create();
}
/**
* @return bool|ZendMessage
*/
public function modifyTo()
{
if ($this->aitConfig->isNewSender(RegistryConstants::VERSION_COMPARISON_OLD_MAIL)) {
$message = ZendMessage::fromString($this->message->getRawMessage())->setEncoding('utf-8');
} else {
$message = $this->message;
}
$toEmails = $message->getTo();
$newEmails = [];
if ($toEmails) {
foreach ($toEmails as $email) {
$name = '';
if ($email instanceof Address) {
$name = $email->getName();
$email = $email->getEmail();
}
if ($this->aitConfig->needToBlockEmail($email)) {
continue;
}
$newEmails[] = [
'email' => $email,
'name' => $name
];
}
}
if (!$newEmails) {
return false;
}
$addressList = $this->aitConfig->getAddressList($newEmails);
$message->setTo($addressList);
return $message;
}
/**
* @param $to
* @return bool
* @throws MailException
*/
public function testSend($to)
{
try {
$result = false;
$this->message = $this->aitConfig->prepareMessageToSend($this->getMessage(), true);
$this->message
->addTo($to)
->setSubject(self::TEST_MESSAGE_SUBJECT)
->setBodyText(__(self::TEST_MESSAGE_BODY));
if ($this->aitConfig->isNewSender(RegistryConstants::VERSION_COMPARISON_OLD_MAIL)) {
$message = ZendMessage::fromString($this->message->getRawMessage())->setEncoding('utf-8');
} else {
$message = $this->message;
}
$this->zendTransport->send($message);
return $result;
} catch (\Exception $e) {
throw new MailException(new Phrase($e->getMessage()), $e);
}
}
/**
* @inheritdoc
*/
public function getMessage()
{
return $this->message;
}
}
<?php
namespace Aitoc\Smtp\Model;
/**
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link https://github.com/soundasleep/html2text
*/
class Html2Text {
public static function defaultOptions() {
return [
'ignore_errors' => false,
'drop_links' => false
];
}
/**
* Tries to convert the given HTML into a plain text format - best suited for
* e-mail display, etc.
*
* <p>In particular, it tries to maintain the following features:
* <ul>
* <li>Links are maintained, with the 'href' copied over
* <li>Information in the &lt;head&gt; is lost
* </ul>
*
* @param string $html the input HTML
* @param array $options Ignore xml parsing errors
* @return string the HTML converted, as best as possible, to text
* @throws \Exception if the HTML could not be loaded as a {@link \DOMDocument}
*/
public static function convert($html, $options = []) {
$options = array_merge(static::defaultOptions(), $options);
// check all options are valid
foreach ($options as $key => $value) {
if (!in_array($key, array_keys(static::defaultOptions()))) {
throw new \InvalidArgumentException("Unknown html2text option '$key'");
}
}
$is_office_document = static::isOfficeDocument($html);
if ($is_office_document) {
// remove office namespace
$html = str_replace(["<o:p>", "</o:p>"], "", $html);
}
$html = static::fixNewlines($html);
if (mb_detect_encoding($html, "UTF-8", true)) {
$html = mb_convert_encoding($html, "HTML-ENTITIES", "UTF-8");
}
$doc = static::getDocument($html, $options['ignore_errors']);
$output = static::iterateOverNode($doc, null, false, $is_office_document, $options);
// process output for whitespace/newlines
$output = static::processWhitespaceNewlines($output);
return $output;
}
/**
* Unify newlines; in particular, \r\n becomes \n, and
* then \r becomes \n. This means that all newlines (Unix, Windows, Mac)
* all become \ns.
*
* @param string $text text with any number of \r, \r\n and \n combinations
* @return string the fixed text
*/
static function fixNewlines($text) {
// replace \r\n to \n
$text = str_replace("\r\n", "\n", $text);
// remove \rs
$text = str_replace("\r", "\n", $text);
return $text;
}
static function nbspCodes() {
return [
"\xc2\xa0",
"\u00a0"
];
}
static function zwnjCodes() {
return [
"\xe2\x80\x8c",
"\u200c"
];
}
/**
* Remove leading or trailing spaces and excess empty lines from provided multiline text
*
* @param string $text multiline text any number of leading or trailing spaces or excess lines
* @return string the fixed text
*/
static function processWhitespaceNewlines($text) {
// remove excess spaces around tabs
$text = preg_replace("/ *\t */im", "\t", $text);
// remove leading whitespace
$text = ltrim($text);
// remove leading spaces on each line
$text = preg_replace("/\n[ \t]*/im", "\n", $text);
// convert non-breaking spaces to regular spaces to prevent output issues,
// do it here so they do NOT get removed with other leading spaces, as they
// are sometimes used for indentation
$text = static::renderText($text);
// remove trailing whitespace
$text = rtrim($text);
// remove trailing spaces on each line
$text = preg_replace("/[ \t]*\n/im", "\n", $text);
// unarmor pre blocks
$text = static::fixNewLines($text);
// remove unnecessary empty lines
$text = preg_replace("/\n\n\n*/im", "\n\n", $text);
return $text;
}
/**
* Parse HTML into a DOMDocument
*
* @param string $html the input HTML
* @param boolean $ignoreError Ignore xml parsing errors
* @return \DOMDocument the parsed document tree
*/
static function getDocument($html, $ignoreError = false) {
$doc = new \DOMDocument();
$html = trim($html);
if (!$html) {
// DOMDocument doesn't support empty value and throws an error
// Return empty document instead
return $doc;
}
if ($html[0] !== '<') {
// If HTML does not begin with a tag, we put a body tag around it.
// If we do not do this, PHP will insert a paragraph tag around
// the first block of text for some reason which can mess up
// the newlines. See pre.html test for an example.
$html = '<body>' . $html . '</body>';
}
if ($ignoreError) {
$doc->strictErrorChecking = false;
$doc->recover = true;
$doc->xmlStandalone = true;
$old_internal_errors = libxml_use_internal_errors(true);
$load_result = $doc->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NONET | LIBXML_PARSEHUGE);
libxml_use_internal_errors($old_internal_errors);
}
else {
$load_result = $doc->loadHTML($html);
}
if (!$load_result) {
throw new \Exception("Could not load HTML - badly formed?\n", $html);
}
return $doc;
}
/**
* Can we guess that this HTML is generated by Microsoft Office?
*/
static function isOfficeDocument($html) {
return strpos($html, "urn:schemas-microsoft-com:office") !== false;
}
/**
* Replace any special characters with simple text versions, to prevent output issues:
* - Convert non-breaking spaces to regular spaces; and
* - Convert zero-width non-joiners to '' (nothing).
*
* This is to match our goal of rendering documents as they would be rendered
* by a browser.
*/
static function renderText($text) {
$text = str_replace(static::nbspCodes(), " ", $text);
$text = str_replace(static::zwnjCodes(), "", $text);
return $text;
}
static function isWhitespace($text) {
return strlen(trim(static::renderText($text), "\n\r\t ")) === 0;
}
static function nextChildName($node) {
// get the next child
$nextNode = $node->nextSibling;
while ($nextNode != null) {
if ($nextNode instanceof \DOMText) {
if (!static::isWhitespace($nextNode->wholeText)) {
break;
}
}
if ($nextNode instanceof \DOMElement) {
break;
}
$nextNode = $nextNode->nextSibling;
}
$nextName = null;
if (($nextNode instanceof \DOMElement || $nextNode instanceof \DOMText) && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
return $nextName;
}
static function iterateOverNode($node, $prevName = null, $in_pre = false, $is_office_document = false, $options) {
if ($node instanceof \DOMText) {
// Replace whitespace characters with a space (equivilant to \s)
if ($in_pre) {
$text = "\n" . trim(static::renderText($node->wholeText), "\n\r\t ") . "\n";
// Remove trailing whitespace only
$text = preg_replace("/[ \t]*\n/im", "\n", $text);
// armor newlines with \r.
return str_replace("\n", "\r", $text);
} else {
$text = static::renderText($node->wholeText);
$text = preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $text);
if (!static::isWhitespace($text) && ($prevName == 'p' || $prevName == 'div')) {
return "\n" . $text;
}
return $text;
}
}
if ($node instanceof \DOMDocumentType || $node instanceof \DOMProcessingInstruction) {
// ignore
return "";
}
$name = strtolower($node->nodeName);
$nextName = static::nextChildName($node);
// start whitespace
switch ($name) {
case "hr":
$prefix = '';
if ($prevName != null) {
$prefix = "\n";
}
return $prefix . "---------------------------------------------------------------\n";
case "style":
case "head":
case "title":
case "meta":
case "script":
// ignore these tags
return "";
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "ol":
case "ul":
case "pre":
// add two newlines
$output = "\n\n";
break;
case "td":
case "th":
// add tab char to separate table fields
$output = "\t";
break;
case "p":
// Microsoft exchange emails often include HTML which, when passed through
// html2text, results in lots of double line returns everywhere.
//
// To fix this, for any p element with a className of `MsoNormal` (the standard
// classname in any Microsoft export or outlook for a paragraph that behaves
// like a line return) we skip the first line returns and set the name to br.
if ($is_office_document && $node->getAttribute('class') == 'MsoNormal') {
$output = "";
$name = 'br';
break;
}
// add two lines
$output = "\n\n";
break;
case "tr":
// add one line
$output = "\n";
break;
case "div":
$output = "";
if ($prevName !== null) {
// add one line
$output .= "\n";
}
break;
case "li":
$output = "- ";
break;
default:
// print out contents of unknown tags
$output = "";
break;
}
// debug
//$output .= "[$name,$nextName]";
if (isset($node->childNodes)) {
$n = $node->childNodes->item(0);
$previousSiblingNames = [];
$previousSiblingName = null;
$parts = [];
$trailing_whitespace = 0;
while ($n != null) {
$text = static::iterateOverNode($n, $previousSiblingName, $in_pre || $name == 'pre', $is_office_document, $options);
// Pass current node name to next child, as previousSibling does not appear to get populated
if ($n instanceof \DOMDocumentType
|| $n instanceof \DOMProcessingInstruction
|| ($n instanceof \DOMText && static::isWhitespace($text))) {
// Keep current previousSiblingName, these are invisible
$trailing_whitespace++;
}
else {
$previousSiblingName = strtolower($n->nodeName);
$previousSiblingNames[] = $previousSiblingName;
$trailing_whitespace = 0;
}
$node->removeChild($n);
$n = $node->childNodes->item(0);
$parts[] = $text;
}
// Remove trailing whitespace, important for the br check below
while ($trailing_whitespace-- > 0) {
array_pop($parts);
}
// suppress last br tag inside a node list if follows text
$last_name = array_pop($previousSiblingNames);
if ($last_name === 'br') {
$last_name = array_pop($previousSiblingNames);
if ($last_name === '#text') {
array_pop($parts);
}
}
$output .= implode('', $parts);
}
// end whitespace
switch ($name) {
case "h1":
case "h2":
case "h3":
$output = mb_strtoupper($output);
case "h4":
case "h5":
case "h6":
case "pre":
case "p":
// add two lines
$output .= "\n\n";
break;
case "b":
case "strong":
$output = "*$output*";
break;
case "i":
case "em":
$output = "/$output/";
break;
case "br":
// add one line
$output .= "\n";
break;
case "div":
break;
case "a":
// links are returned in text <link> format
$href = $node->getAttribute("href");
$output = trim($output);
// if there is no link text, but a title attr
if (!$output && $node->getAttribute("title")) {
$output = $node->getAttribute("title");
}
if ($href == null) {
// it doesn't link anywhere
if ($node->getAttribute("name") != null) {
if ($options['drop_links']) {
$output = "$output";
} else {
$output = "$output";
}
}
} else {
if ($href == $output || $href == "http://$output" || $href == "https://$output") {
// link to the same address: just use link
$output = "$output";
} else {
// replace it
if ($output) {
if ($options['drop_links']) {
$output = "$output";
} else {
$output = "$output <$href>";
}
} else {
// empty string
$output = "$href";
}
}
}
// does the next node require additional whitespace?
switch ($nextName) {
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
$output .= "\n";
break;
}
break;
case "img":
case "img":
if ($node->getAttribute("title")) {
$output = $node->getAttribute("title");
} elseif ($node->getAttribute("alt")) {
$output = $node->getAttribute("alt");
} else {
$output = "";
}
break;
case "li":
$output .= "\n";
break;
case "blockquote":
// process quoted text for whitespace/newlines
$output = static::processWhitespaceNewlines($output);
// add leading newline
$output = "\n" . $output;
// prepend '> ' at the beginning of all lines
$output = preg_replace("/\n/im", "\n> ", $output);
// replace leading '> >' with '>>'
$output = preg_replace("/\n> >/im", "\n>>", $output);
// add another leading newline and trailing newlines
$output = "\n" . $output . "\n\n";
break;
default:
// do nothing
}
return $output;
}
}
\ No newline at end of file
<?php
namespace Aitoc\Smtp\Model;
use Aitoc\Smtp\Api\Data\LogInterface;
use Aitoc\Smtp\Controller\RegistryConstants;
class Log extends \Magento\Framework\Model\AbstractModel implements LogInterface
{
const LOG_ACTIVE = '1';
const LOG_INACTIVE = '0';
const LOG_ID_TYPE_FIELD = 'log_id';
/**
* @var Config
*/
private $config;
/**
* @var \Aitoc\Core\Model\Helpers\Date
*/
private $date;
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Registry $registry,
\Aitoc\Smtp\Model\ResourceModel\Log $resource,
\Aitoc\Smtp\Model\ResourceModel\Log\Collection $resourceCollection,
\Aitoc\Smtp\Model\Config $config,
\Aitoc\Core\Model\Helpers\Date $date,
array $data = []
)
{
$this->config = $config;
$this->date = $date;
parent::__construct($context, $registry, $resource, $resourceCollection);
}
/**
* {@inheritdoc}
*/
public function _construct()
{
$this->_init(\Aitoc\Smtp\Model\ResourceModel\Log::class);
}
/**
* @return int|mixed
*/
public function getLogId()
{
return $this->getData(LogInterface::LOG_ID);
}
/**
* @param int $logId
* @return $this|LogInterface
*/
public function setLogId($logId)
{
$this->setData(LogInterface::LOG_ID, $logId);
return $this;
}
/**
* @param $ruleId
*
* @return $this
*/
public function getLogById($logId)
{
$resource = $this->getResource();
$resource->load($this, $logId);
return $this;
}
/**
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function beforeSave()
{
parent::beforeSave();
return $this->updateDate();
}
/**
* @return $this
*/
private function updateDate()
{
return $this;
}
/**
* @param $message
* @param array $errorData
* @return $this
* @throws \Magento\Framework\Exception\AlreadyExistsException
*/
public function log($message, $errorData = [])
{
if ($this->config->logEnabled()) {
$logData = $this->getLogData($message);
$logData[LogInterface::CREATED_AT] = $this->date->getCurrentDate();
$logData[LogInterface::STATUS] = $errorData ? $errorData[LogInterface::STATUS] : 0;
$logData[LogInterface::STATUS_MESSAGE] = $errorData ? $errorData[LogInterface::STATUS_MESSAGE] : '';
$this->setData($logData);
$this->_resource->save($this);
}
return $this;
}
/**
* @param $message
* @return array
*/
private function getLogData($message)
{
$result = [];
if ($this->config->isNewSender(RegistryConstants::VERSION_COMPARISON_OLD_MAIL)) {
$result[LogInterface::SUBJECT] = $message->getSubject() ?: '';
$result[LogInterface::SENDER_EMAIL] = $this->getEmailsFromAddressList($message->getFrom());
$result[LogInterface::RECIPIENT_EMAIL] = $this->getEmailsFromAddressList($message->getTo());
$result[LogInterface::BCC] = $this->getEmailsFromAddressList($message->getBcc());
$result[LogInterface::CC] = $this->getEmailsFromAddressList($message->getCc());
$result[LogInterface::EMAIL_BODY] = htmlspecialchars($message->getBodyText());
} else {
$headers = $message->getHeaders();
$result[LogInterface::SUBJECT] = isset($headers['Subject'][0]) ? $headers['Subject'][0] : '';
$result[LogInterface::SENDER_EMAIL] = isset($headers['From'][0]) ? $headers['From'][0] : '';
if (isset($headers['To'])) {
$recipient = $headers['To'];
if (isset($recipient['append'])) {
unset($recipient['append']);
}
$result[LogInterface::RECIPIENT_EMAIL] = $this->getEmailsFromAddressList($recipient);
}
if (isset($headers['Cc'])) {
$cc = $headers['Cc'];
if (isset($cc['append'])) {
unset($cc['append']);
}
$result[LogInterface::CC] = $this->getEmailsFromAddressList($cc);
}
if (isset($headers['Bcc'])) {
$bcc = $headers['Bcc'];
if (isset($bcc['append'])) {
unset($bcc['append']);
}
$result[LogInterface::BCC] = $this->getEmailsFromAddressList($bcc);
}
$emailBody = $message->getBodyHtml();
if (is_object($emailBody)) {
$result[LogInterface::EMAIL_BODY] = htmlspecialchars($emailBody->getRawContent());
} else {
$result[LogInterface::EMAIL_BODY] = htmlspecialchars($message->getBody()->getRawContent());
}
}
return $result;
}
/**
* @param $emails
* @return string
*/
private function getEmailsFromAddressList($emails)
{
$result = [];
if (count($emails)) {
foreach ($emails as $email) {
$name = 'Unknown';
if ($email->getName()) {
$name = $email->getName();
}
$result[] = "<" . $name . ">" . $email->getEmail();
}
}
return implode(',', $result);
}
}
<?php
namespace Aitoc\Smtp\Model;
/**
* Class Providers
* @package Aitoc\Smtp\Model
*/
class Providers
{
/**
* @return array
*/
public function getAllProviders()
{
return [
'gmail' => [
'label' => __('Gmail, GSuite'),
'info' => [
'host' => 'smtp.gmail.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'yandex' => [
'label' => __('Yandex'),
'info' => [
'host' => 'smtp.yandex.ru',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'amazon-us-east-virginia' => [
'label' => __('Amazon SES: US East (N. Virginia)'),
'info' => [
'host' => 'email-smtp.us-east-1.amazonaws.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'amazon-us-east-oregon' => [
'label' => __('Amazon SES: US West (Oregon)'),
'info' => [
'host' => 'email-smtp.us-west-2.amazonaws.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'amazon-eu-ireland' => [
'label' => __('Amazon SES: EU (Ireland)'),
'info' => [
'host' => 'email-smtp.eu-west-1.amazonaws.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'mailgun' => [
'label' => __('Mailgun'),
'info' => [
'host' => 'smtp.mailgun.org',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'mandrill' => [
'label' => __('Mandrill'),
'info' => [
'host' => 'smtp.mandrillapp.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'sendinblue' => [
'label' => __('Sendinblue'),
'info' => [
'host' => 'smtp-relay.sendinblue.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'sendgrid' => [
'label' => __('Sendgrid'),
'info' => [
'host' => 'smtp.sendgrid.net',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'elastic' => [
'label' => __('Elastic Email'),
'info' => [
'host' => 'smtp.elasticemail.com',
'port' => '2525',
'encryption' => '',
'auth_type' => 'login'
]
],
'sparkpost' => [
'label' => __('SparkPost'),
'info' => [
'host' => 'smtp.sparkpostmail.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'mailjet' => [
'label' => __('Mailjet'),
'info' => [
'host' => 'in-v3.mailjet.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'postmark' => [
'label' => __('Postmark'),
'info' => [
'host' => 'smtp.postmarkapp.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'aol' => [
'label' => __('AOL Mail'),
'info' => [
'host' => 'smtp.aol.com',
'port' => '587',
'encryption' => '',
'auth_type' => 'login'
]
],
'comcast' => [
'label' => __('Comcast'),
'info' => [
'host' => 'smtp.comcast.net',
'port' => '587',
'encryption' => '',
'auth_type' => 'login'
]
],
'gmx' => [
'label' => __('GMX'),
'info' => [
'host' => 'mail.gmx.net',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'hotmail' => [
'label' => __('Hotmail'),
'info' => [
'host' => 'smtp-mail.outlook.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'mailcom' => [
'label' => __('Mail.com'),
'info' => [
'host' => 'smtp.mail.com',
'port' => '587',
'encryption' => '',
'auth_type' => 'login'
]
],
'02mail' => [
'label' => __('O2 Mail'),
'info' => [
'host' => 'smtp.o2.ie',
'port' => '25',
'encryption' => '',
'auth_type' => 'login'
]
],
'office365' => [
'label' => __('Office365'),
'info' => [
'host' => 'smtp.office365.com',
'port' => '587',
'encryption' => '',
'auth_type' => 'login'
]
],
'orange' => [
'label' => __('Orange'),
'info' => [
'host' => 'smtp.orange.net',
'port' => '25',
'encryption' => '',
'auth_type' => 'login'
]
],
'outlook' => [
'label' => __('Outlook'),
'info' => [
'host' => 'smtp-mail.outlook.com',
'port' => '587',
'encryption' => 'tls',
'auth_type' => 'login'
]
],
'yahoo' => [
'label' => __('Yahoo Mail'),
'info' => [
'host' => 'smtp.mail.yahoo.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'yahooplus' => [
'label' => __('Yahoo Mail Plus'),
'info' => [
'host' => 'plus.smtp.mail.yahoo.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'yahooau' => [
'label' => __('Yahoo AU/NZ'),
'info' => [
'host' => 'smtp.mail.yahoo.com.au',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'at&t' => [
'label' => __('AT&T'),
'info' => [
'host' => 'smtp.att.yahoo.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'ntlworld' => [
'label' => __('NTL @ntlworld.com'),
'info' => [
'host' => 'smtp.ntlworld.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'btconnect' => [
'label' => __('BT Connect'),
'info' => [
'host' => 'pop3.btconnect.com',
'port' => '25',
'encryption' => '',
'auth_type' => 'login'
]
],
'zoho' => [
'label' => __('Zoho Mail'),
'info' => [
'host' => 'smtp.zoho.com',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'verizon' => [
'label' => __('Verizon'),
'info' => [
'host' => 'outgoing.verizon.net',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'btopenworld' => [
'label' => __('BT Openworld'),
'info' => [
'host' => 'mail.btopenworld.com',
'port' => '25',
'encryption' => '',
'auth_type' => 'login'
]
],
'o2online' => [
'label' => __('O2 Online Deutschland'),
'info' => [
'host' => 'mail.o2online.de',
'port' => '25',
'encryption' => '',
'auth_type' => 'login'
]
],
'1&1webmail' => [
'label' => __('1&1 Webmail'),
'info' => [
'host' => 'smtp.1and1.com',
'port' => '587',
'encryption' => '',
'auth_type' => 'login'
]
],
'ovh' => [
'label' => __('OVH'),
'info' => [
'host' => 'ssl0.ovh.net',
'port' => '465',
'encryption' => 'ssl',
'auth_type' => 'login'
]
],
'smtp2go' => [
'label' => __('SMTP2GO'),
'info' => [
'host' => 'mail.smtp2go.com',
'port' => '2525',
'encryption' => 'tls',
'auth_type' => 'login'
]
]
];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Model;
namespace Aitoc\Smtp\Model\Resolver;
class Store
/**
* Class From
* @package Aitoc\Smtp\Model\Resolver
*/
class From
{
/** @var int/null */
protected $store_id = null;
/**
* @var null
*/
protected $from = null;
/**
* @return int|null
* @return null
*/
public function getStoreId()
public function getFrom()
{
return $this->store_id;
return $this->from;
}
/**
* @param $store_id
* @param $from
* @return $this
*/
public function setStoreId($store_id)
public function setFrom($from)
{
$this->store_id = $store_id;
return $this;
}
$this->from = $from;
/**
* @return string|array
*/
public function getFrom()
{
return $this->from;
return $this;
}
/**
* @param string|array $from
* @return $this
*/
public function setFrom($from)
public function reset()
{
$this->from = $from;
$this->from = null;
return $this;
}
}
<?php
namespace Aitoc\Smtp\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\VersionControl\AbstractDb;
class Log extends AbstractDb
{
/**
* {@inheritdoc}
*/
protected function _construct()
{
$this->_init(
\Aitoc\Smtp\Setup\InstallSchema::AITOC_SMTP_LOG_TABLE_NAME,
\Aitoc\Smtp\Model\Log::LOG_ID_TYPE_FIELD
);
}
}
\ No newline at end of file
<?php
namespace Aitoc\Smtp\Model\ResourceModel\Log;
use Aitoc\Smtp\Model\Log;
class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
{
/**
* @var \Aitoc\ShippingRules\Model\Date
*/
private $date;
public function __construct(
\Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory, \Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Aitoc\Core\Model\Helpers\Date $date,
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
$this->date = $date;
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$connection,
$resource
);
}
/**
* _construct
*/
protected function _construct()
{
parent::_construct();
$this->_init(\Aitoc\Smtp\Model\Log::class,
\Aitoc\Smtp\Model\ResourceModel\Log::class
);
$this->_setIdFieldName($this->getResource()->getIdFieldName());
}
/**
* @param $logId
* @return $this
*/
public function addLogIdFilter($logId)
{
$this->addFieldToFilter(Log::LOG_ID_TYPE_FIELD,
[
'eq' => $logId
]
);
return $this;
}
}
<?php
namespace Aitoc\Smtp\Model\ResourceModel\Log\Grid;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Api\Search\AggregationInterface;
use Aitoc\Smtp\Model\ResourceModel\Log\Collection as LogCollection;
/**
* Class Collection
*
* @package Aitoc\ShippingRules\Model\ResourceModel\Rules\Grid
*/
class Collection extends LogCollection implements SearchResultInterface
{
/**
* @var AggregationInterface
*/
public $aggregations;
/**
* Collection constructor.
*
* @param \Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory
* @param \Psr\Log\LoggerInterface $logger
* @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param \Magento\Framework\DB\Adapter\AdapterInterface $mainTable
* @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $eventPrefix
* @param $eventObject
* @param $resourceModel
* @param string $model
* @param null $connection
* @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb|null $resource
*/
public function __construct(
\Magento\Framework\Data\Collection\EntityFactoryInterface $entityFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
\Aitoc\Core\Model\Helpers\Date $date,
$mainTable,
$eventPrefix,
$eventObject,
$resourceModel,
$model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
$connection = null,
\Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource = null
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$date,
$connection,
$resource
);
$this->_eventPrefix = $eventPrefix;
$this->_eventObject = $eventObject;
$this->_init($model, $resourceModel);
$this->setMainTable($mainTable);
}
/**
* @return AggregationInterface
*/
public function getAggregations()
{
return $this->aggregations;
}
/**
* @param AggregationInterface $aggregations
* @return $this
*/
public function setAggregations($aggregations)
{
$this->aggregations = $aggregations;
}
/**
* Get search criteria.
*
* @return \Magento\Framework\Api\SearchCriteriaInterface|null
*/
public function getSearchCriteria()
{
return null;
}
/**
* Set search criteria.
*
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setSearchCriteria(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria = null)
{
return $this;
}
/**
* Get total count.
*
* @return int
*/
public function getTotalCount()
{
return $this->getSize();
}
/**
* Set total count.
*
* @param int $totalCount
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setTotalCount($totalCount)
{
return $this;
}
/**
* Set items list.
*
* @param \Magento\Framework\Api\ExtensibleDataInterface[] $items
* @return $this
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function setItems(array $items = null)
{
return $this;
}
}
<?php
namespace Aitoc\Smtp\Model;
use Aitoc\Smtp\Api\Data\LogInterface;
use Aitoc\Smtp\Controller\RegistryConstants;
use Aitoc\Smtp\Model\Config\Options\Status;
use Magento\Framework\App\Area;
use Magento\Framework\Exception\MailException;
use Magento\Store\Model\Store;
class Sender
{
const ADDRESS_SCOPE_FROM = 'from';
const ADDRESS_SCOPE_TO = 'to';
/**
* @var LogFactory
*/
private $logFactory;
/**
* @var \Magento\Framework\Mail\Template\TransportBuilder
*/
private $transportBuilder;
/**
* @var Config
*/
private $config;
/**
* @param LogFactory $logFactory
* @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
* @param Config $config
*/
public function __construct(
\Aitoc\Smtp\Model\LogFactory $logFactory,
\Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
\Aitoc\Smtp\Model\Config $config
) {
$this->logFactory = $logFactory;
$this->transportBuilder = $transportBuilder;
$this->config = $config;
}
/**
* Send the Email using log Id
*
* @param int $logId
* @return bool
* @throws MailException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function sendByLogId($logId)
{
$log = $this->getCurrentLog($logId);
if (!$log->getId()) {
return false;
}
$data = $log->getData();
$data[LogInterface::EMAIL_BODY] = htmlspecialchars_decode($data[LogInterface::EMAIL_BODY]);
$vars = [];
if (!$data[LogInterface::EMAIL_BODY]
|| !$data[LogInterface::RECIPIENT_EMAIL]
|| !$data[LogInterface::SENDER_EMAIL]
|| !$data[LogInterface::SUBJECT]
) {
return false;
}
$vars[LogInterface::EMAIL_BODY] = quoted_printable_decode($data[LogInterface::EMAIL_BODY]);
$vars[LogInterface::SUBJECT] = $data[LogInterface::SUBJECT];
$this->transportBuilder
->addTo($this->prepareEmailsData($data[LogInterface::RECIPIENT_EMAIL], self::ADDRESS_SCOPE_TO))
->setFromByScope($this->prepareEmailsData($data[LogInterface::SENDER_EMAIL], self::ADDRESS_SCOPE_FROM));
if ($data[LogInterface::BCC]) {
$this->transportBuilder->addBcc($this->prepareEmailsData($data[LogInterface::BCC]));
}
if ($data[LogInterface::CC]) {
$this->transportBuilder->addCc($this->prepareEmailsData($data[LogInterface::CC]));
}
try {
$this->transportBuilder
->setTemplateIdentifier(RegistryConstants::RESEND_EMAIL_TEMPLATE_ID)
->setTemplateOptions(['store' => Store::DEFAULT_STORE_ID, 'area' => Area::AREA_FRONTEND])
->setTemplateVars($vars);
$this->transportBuilder->getTransport()->sendMessage();
$log->setData(LogInterface::STATUS, Status::STATUS_SUCCESS)
->setData(LogInterface::STATUS_MESSAGE, '')
->save();
} catch (MailException $e) {
$log->setData(LogInterface::STATUS, Status::STATUS_FAILED)
->setData(LogInterface::STATUS_MESSAGE, $e->getMessage())
->save();
return false;
}
return true;
}
/**
* Prepare the Data for Send Email
*
* @param array $emails
* @param string $scope
* @return array|mixed|string|\Zend\Mail\AddressList
*/
private function prepareEmailsData($emails, $scope = '')
{
$emailsConverted = [];
$emails = explode(',', $emails);
foreach ($emails as $email) {
$emailData = explode('>', substr($email, 1));
switch ($scope) {
case self::ADDRESS_SCOPE_TO:
return $emailData[1];
break;
case self::ADDRESS_SCOPE_FROM:
return [
'name' => ($emailData[0] == 'Unknown' ? '' : $emailData[0]),
'email' => $emailData[1],
];
break;
}
$emailsConverted[] = [
'name' => ($emailData[0] == 'Unknown' ? '' : $emailData[0]),
'email' => $emailData[1],
];
}
return $this->config->getAddressList($emailsConverted);
}
/**
* Get the Current Log Details using Log Id
*
* @param int $logId
* @return Log
*/
public function getCurrentLog($logId)
{
return $this->logFactory->create()->getLogById($logId);
}
}
<?php
namespace Aitoc\Smtp\Plugin\Framework\Mail;
use Aitoc\Smtp\Model\Html2Text;
use Magento\Framework\Mail\Message as MailMessage;
class Message
{
/**
* @var \Aitoc\Smtp\Model\Config
*/
private $config;
public function __construct(
\Aitoc\Smtp\Model\Config $config
) {
$this->config = $config;
}
/**
* @param MailMessage $subject
* @return MailMessage
*/
public function afterSetBody(MailMessage $subject)
{
if (!$this->config->plainEnabled()) {
return $subject;
}
try {
$body = $subject->getBody();
if ($body instanceof \Zend\Mime\Message && !$body->isMultiPart()) {
$reflection = new \ReflectionProperty(MailMessage::class, 'zendMessage');
$reflection->setAccessible(true);
/** @var \Zend\Mail\Message $zendMessage */
$zendMessage = $reflection->getValue($subject);
$plainContent = '';
try {
$plainContent = Html2Text::convert($zendMessage->getBodyText());
} catch (\Exception $e) {
}
$textPart = new \Zend\Mime\Part($plainContent);
$textPart->setCharset($zendMessage->getEncoding());
$textPart->setType(\Zend\Mime\Mime::TYPE_TEXT);
$body->setParts(array_merge([$textPart], $body->getParts()));
$zendMessage->setBody($body);
$zendMessage->getHeaders()->get('content-type')->setType('multipart/alternative');
}
} catch (\Exception $e) {
}
return $subject;
}
}
<?php
namespace Aitoc\Smtp\Plugin\Framework\Mail\Template;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Aitoc\Smtp\Model\Resolver\From;
class TransportBuilder
{
/**
* @var From
*/
private $fromResolver;
/**
* @var SenderResolverInterface
*/
private $senderResolver;
public function __construct(
From $fromResolver,
SenderResolverInterface $SenderResolver
) {
$this->fromResolver = $fromResolver;
$this->senderResolver = $SenderResolver;
}
/**
* @param \Magento\Framework\Mail\Template\TransportBuilder $subject
* @param $from
* @return array
* @throws MailException
*/
public function beforeSetFrom(
\Magento\Framework\Mail\Template\TransportBuilder $subject,
$from
) {
$this->fromResolver->reset();
$senderData = $from;
if (is_string($from)) {
$senderData = $this->senderResolver->resolve($from);
}
if (is_array($from)) {
$senderData = $from;
}
$this->fromResolver->setFrom($senderData);
return [$from];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Plugin\Mail\Template;
namespace Aitoc\Smtp\Plugin\Framework\Mail\Template;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\Template\TransportBuilderByStore;
use MagePal\GmailSmtpApp\Model\Store;
use Magento\Framework\Mail\Template\TransportBuilderByStore as TransportBuilderByStoreOriginal;
use Aitoc\Smtp\Model\Resolver\From;
class TransportBuilderByStorePlugin
class TransportBuilderByStore
{
/**
* @var Store
* @var From
*/
protected $storeModel;
private $fromResolver;
/**
* Sender resolver.
*
* @var SenderResolverInterface
*/
private $senderResolver;
/**
* @param Store $storeModel
* @param SenderResolverInterface $senderResolver
*/
public function __construct(
Store $storeModel,
From $fromResolver,
SenderResolverInterface $senderResolver
) {
$this->storeModel = $storeModel;
$this->fromResolver = $fromResolver;
$this->senderResolver = $senderResolver;
}
......@@ -45,16 +36,12 @@ class TransportBuilderByStorePlugin
* @throws MailException
*/
public function beforeSetFromByStore(
TransportBuilderByStore $subject,
TransportBuilderByStoreOriginal $subject,
$from,
$store
) {
if (!$this->storeModel->getStoreId()) {
$this->storeModel->setStoreId($store);
}
$email = $this->senderResolver->resolve($from, $store);
$this->storeModel->setFrom($email);
$this->fromResolver->reset();
$this->fromResolver->setFrom($this->senderResolver->resolve($from, $store));
return [$from, $store];
}
......
<?php
namespace Aitoc\Smtp\Plugin\Framework\Mail;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Mail\TransportInterfaceFactory as TransportInterfaceFactoryCore;
use Aitoc\Smtp\Model\Config;
use Aitoc\Smtp\Controller\RegistryConstants;
/**
* TransportInterface Plugin
*/
class TransportInterfaceFactory
{
/**
* Object Manager
*
* @var \Magento\Framework\ObjectManagerInterface
*/
private $objectManager;
/**
* @var \Aitoc\Smtp\Model\Config
*/
private $config;
public function __construct(
ObjectManagerInterface $objectManager,
Config $config
) {
$this->objectManager = $objectManager;
$this->config = $config;
}
/**
* Create Class instance with Specified Parameters
*
* @param $subject TransportInterfaceFactoryCore
* @param $proceed \Callable
* @param array $data
* @return \Magento\Framework\Mail\TransportInterface
*/
public function aroundCreate($subject, $proceed, array $data = [])
{
if ($this->config->enabled()) {
if ($data && isset($data[\Aitoc\Smtp\Controller\RegistryConstants::IS_TEST_FIELD_ARRAY])) {
unset($data[RegistryConstants::IS_TEST_FIELD_ARRAY]);
$config = $data;
$data['message'] = $this->objectManager->create(\Magento\Framework\Mail\Message::class);
} else {
$config = $this->config->getFullConfig();
}
$data = array_merge($data, ['config' => $config]);
return $this->objectManager
->create(
\Aitoc\Smtp\Model\Framework\Mail\Transport::class,
$data
);
}
return $proceed($data);
}
}
<?php
namespace Aitoc\Smtp\Setup;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
class InstallSchema implements InstallSchemaInterface
{
const AITOC_SMTP_LOG_TABLE_NAME = 'aitoc_smtp_log';
/**
* @inheritDoc
*/
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$installer = $setup;
$installer->startSetup();
if (!$installer->tableExists(self::AITOC_SMTP_LOG_TABLE_NAME)) {
$table = $installer->getConnection()
->newTable($installer->getTable(self::AITOC_SMTP_LOG_TABLE_NAME))
->addColumn(
'log_id',
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
'Log Id'
)->addColumn(
'created_at',
\Magento\Framework\DB\Ddl\Table::TYPE_DATETIME,
null,
['nullable' => true, 'default' => null],
'Created At'
)
->addColumn(
'subject',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
['nullable' => false]
)
->addColumn(
'email_body',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
null,
['nullable' => false]
)
->addColumn(
'sender_email',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
120,
['nullable' => false]
)
->addColumn(
'recipient_email',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
120,
['nullable' => false]
)
->addColumn(
'cc',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
120,
['nullable' => true]
)
->addColumn(
'bcc',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
120,
['nullable' => true]
)
->addColumn(
'status',
\Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
null,
['nullable' => false]
)->addColumn(
'status_message',
\Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
255,
['nullable' => true]
)->setComment('Aitoc SMTP Log');
$installer->getConnection()->createTable($table);
}
$installer->endSetup();
}
}
<?php
namespace Aitoc\Smtp\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Aitoc\Smtp\Model\Config\Options\Status as StatusOptions;
/**
* Class Status
*/
class Status extends Column
{
/**
* @var StatusOptions
*/
private $statusOptions;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
StatusOptions $statusOptions,
array $components = [],
array $data = []
) {
$this->statusOptions = $statusOptions;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
$item[$this->getData('name')] = $this->getLabelByStatus($item[$this->getData('name')]);
}
}
return $dataSource;
}
/**
* @param $status
* @return string
*/
private function getLabelByStatus($status)
{
$html = '';
$label = $this->statusOptions->getLabelByStatus($status);
switch ($status) {
case StatusOptions::STATUS_SUCCESS:
$html = '<span class="grid-severity-notice"><span>'
. $label . '</span></span>';
break;
case StatusOptions::STATUS_FAILED:
$html = '<span class="grid-severity-major"><span>'
. $label . '</span></span>';
break;
default:
$html = '<span class="grid-severity-minor"><span>'
. $label . '</span></span>';
break;
}
return $html;
}
}
<?php
namespace Aitoc\Smtp\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\UrlInterface;
/**
* Class ViewAction
*/
class ViewAction extends Column
{
const PREVIEW_ULR_PATH = 'aitoc_smtp/log/preview';
const RESEND_URL_PATH = 'aitoc_smtp/log/resend';
const DELETE_LOG_URL_PATH = 'aitoc_smtp/log/delete';
/**
* @var UrlInterface
*/
private $urlBuilder;
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item['log_id'])) {
$urlEntityParamName = $this->getData('config/urlEntityParamName') ?: 'log_id';
$item[$this->getData('name')] = [
'preview' => [
'href' => $this->context->getUrl(
self::PREVIEW_ULR_PATH,
[
$urlEntityParamName => $item['log_id']
]
),
'target' => '_blank',
'label' => __('Preview Email'),
'popup' => true,
],
'delete' => [
'href' => $this->urlBuilder->getUrl(
self::DELETE_LOG_URL_PATH,
['log_id' => $item['log_id']]
),
'label' => __('Delete Email Log'),
'confirm' => [
'title' => __('Delete'),
'message' => __('Are you sure you want to delete?')
]
],
'resend' => [
'href' => $this->urlBuilder->getUrl(
self::RESEND_URL_PATH,
['log_id' => $item['log_id']]
),
'label' => __('Resend Email'),
'confirm' => [
'title' => __('Resend Email'),
'message' => __('Are you sure you want to resend the selected email?')
]
],
];
}
}
}
return $dataSource;
}
}
{
"name": "aitoc/smtp",
"description": "SMTP",
"require": {
"php": "~5.6.5|7.0.2|7.0.4|~7.0.6|~7.0.13|~7.1.0|~7.2.0|~7.3.0|~7.4.0",
"aitoc/core": ">=1.0.8"
},
"type": "magento2-module",
"version": "1.0.3",
"license": [
"Commercial"
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Aitoc\\Smtp\\": ""
}
}
}
<?xml version="1.0" ?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
-->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="MagePal_GmailSmtpApp::magepal_gmailsmtpapp" title="MagePal SMTP App"/>
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Reports::report">
<resource id="Aitoc_Smtp::main" title="Aitoc: SMTP" sortOrder="31">
<resource id="Aitoc_Smtp::log" title="SMTP Log" translate="title" sortOrder="10" />
</resource>
</resource>
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Aitoc_Smtp::config"
title="Aitoc SMTP" sortOrder="50"/>
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
\ No newline at end of file
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Aitoc\Smtp\Model\ResourceModel\Log\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">aitoc_smtp_log</argument>
<argument name="eventPrefix" xsi:type="string">aitsmtp_log_grid_collection</argument>
<argument name="eventObject" xsi:type="string">aitsmtp_log_grid_collection</argument>
<argument name="resourceModel" xsi:type="string">Aitoc\Smtp\Model\ResourceModel\Rule</argument>
</arguments>
</type>
</config>
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
<menu>
<add id="Aitoc_Smtp::main" title="SMTP" translate="title" module="Aitoc_Smtp" sortOrder="31" parent="Magento_Reports::report" resource="Aitoc_Smtp::main"/>
<add id="Aitoc_Smtp::log" action="aitoc_smtp/log/index" title="Emails Log" translate="title" module="Aitoc_Smtp" sortOrder="10" parent="Aitoc_Smtp::main" resource="Aitoc_Smtp::log"/>
</menu>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="magepal" frontName="magepal">
<module name="MagePal_Core" before="Magento_Backend" />
<route id="aitoc_smtp" frontName="aitoc_smtp">
<module name="Aitoc_Smtp" />
</route>
</router>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="aitsmtp" translate="label" type="text" sortOrder="061532" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SMTP</label>
<tab>aitoc_extensions</tab>
<resource>Aitoc_Smtp::config</resource>
<group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>General Settings</label>
<field id="enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enable SMTP</label>
<comment>When the option is disabled, the module will not impact on email sendings</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
<group id="smtp" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SMTP Settings</label>
<field id="use_default" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Use Pre-Defined SMTP Providers</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="provider" translate="label comment" type="select" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Provider</label>
<frontend_model>Aitoc\Smtp\Block\Adminhtml\Provider</frontend_model>
<source_model>Aitoc\Smtp\Model\Config\Options\Provider</source_model>
<depends>
<field id="use_default">1</field>
</depends>
</field>
<field id="name" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Local Client Hostname</label>
<comment><![CDATA[Local client hostname or IP. Default: <strong>localhost</strong>]]></comment>
</field>
<field id="host" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Host</label>
<comment>Host name or IP address of SMTP Server</comment>
</field>
<field id="port" translate="label comment" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Port</label>
<comment><![CDATA[Default ports: 25, 465, or 587. (<strong>SSL</strong> - 465, <strong>TLS</strong> - 587)]]></comment>
<validate>integer</validate>
</field>
<field id="protocol" translate="label comment" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Connection Security</label>
<source_model>Aitoc\Smtp\Model\Config\Options\Protocol</source_model>
</field>
<field id="auth_type" translate="label comment" type="select" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Authentication Method</label>
<source_model>Aitoc\Smtp\Model\Config\Options\Auth</source_model>
</field>
<field id="login" translate="label comment" type="text" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Username</label>
</field>
<field id="password" translate="label comment" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Password</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
</field>
<field id="test_email_to" translate="label comment" type="text" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Send Test To E-mail</label>
</field>
<field id="test_email_button" translate="label" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="1">
<label></label>
<frontend_model>Aitoc\Smtp\Block\Adminhtml\TestButton</frontend_model>
</field>
</group>
<group id="emails" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Emails Settings</label>
<field id="plain" translate="label comment" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Add Plain Text Part in Email</label>
<comment><![CDATA[Add Plain part in email recommended for <strong>increase Spam Score of email</strong>]]></comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="sender_enable" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Use Another Email Sender</label>
<comment><![CDATA[<strong>Please notice,</strong> the <strong>Sender domain</strong> must be the same as the domain where you send email. Other combinations can lead to a ban because of spam]]></comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="sender_email" translate="label comment" type="text" sortOrder="14" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Sender Email</label>
<depends>
<field id="sender_enable">1</field>
</depends>
</field>
<field id="sender_name" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Sender Name</label>
<depends>
<field id="sender_enable">1</field>
</depends>
</field>
<field id="cc" translate="label" type="textarea" sortOrder="20" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>CC Emails</label>
<comment><![CDATA[Comma-separated. Leave empty for disable <strong>CC emails.</strong>]]></comment>
</field>
<field id="bcc" translate="label" type="textarea" sortOrder="30" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>BCC Emails</label>
<comment><![CDATA[Comma-separated. Leave empty for disable <strong>BCC emails.</strong>]]></comment>
</field>
</group>
<group id="log" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Log Settings</label>
<field id="log" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enable Log Outgoing Emails</label>
<comment><![CDATA[See the all emails and their contents ever <strong>sent throughout Aitoc SMTP.</strong>]]></comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="log_clean" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Log Clean Every (days)</label>
<comment><![CDATA[<strong>Day(s).</strong> When empty or zero, the Email log will not be cleaned.]]></comment>
<validate>integer</validate>
<depends>
<field id="log">1</field>
</depends>
</field>
</group>
<group id="debug" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Debug</label>
<field id="delivery" translate="label comment" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Disable Actual Email Delivery (Debug Mode)</label>
<comment><![CDATA[<strong>Set the option to ‘Yes’ in order not to send emails actually.</strong> That can be used for debug or development purposes]]></comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="exeptional_email_addresses" translate="label" type="textarea" sortOrder="20" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>Exceptional Email Addresses</label>
<comment>Comma-separated. Leave empty for disable</comment>
<depends>
<field id="delivery">1</field>
</depends>
</field>
<field id="exeptional_domains" translate="label" type="textarea" sortOrder="30" showInDefault="1"
showInWebsite="1" showInStore="1">
<label>Exceptional Email Domains</label>
<comment><![CDATA[Comma-separated. Leave empty for disable. <strong>Example:</strong> http://example.com,qwewqe.com ]]></comment>
<depends>
<field id="delivery">1</field>
</depends>
</field>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Store/etc/config.xsd">
<default>
<aitsmtp>
<general>
<enabled>1</enabled>
</general>
<smtp>
<name>localhost</name>
<use_default>0</use_default>
<provider>none</provider>
<auth_type>1</auth_type>
</smtp>
<emails>
<plain>0</plain>
<cc></cc>
<bcc></bcc>
<sender_enable>0</sender_enable>
</emails>
<log>
<log>1</log>
<log_clean>30</log_clean>
</log>
<debug>
<delivery>0</delivery>
<exeptional_email_addresses>example@example.com,unknown@unknown.com</exeptional_email_addresses>
<exeptional_domains>http://example.com, https://hello.com</exeptional_domains>
</debug>
</aitsmtp>
</default>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Cron:etc/crontab.xsd">
<group id="default">
<job name="aitoc_smtp_log_clear" instance="Aitoc\Smtp\Cron\Clear" method="execute">
<schedule>0 0 * * *</schedule>
</job>
</group>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="aitoc_smtp_log_listing_data_source" xsi:type="string">Aitoc\Smtp\Model\ResourceModel\Log\Grid\Collection</item>
</argument>
</arguments>
</type>
<type name="Magento\Framework\Mail\TransportInterfaceFactory">
<plugin name="Aitoc_Smtp::transportBuider" type="Aitoc\Smtp\Plugin\Framework\Mail\TransportInterfaceFactory" />
</type>
<type name="Magento\Framework\Mail\Template\TransportBuilderByStore">
<plugin name="Aitoc_Smtp::transportBuilderByStore"
type="Aitoc\Smtp\Plugin\Framework\Mail\Template\TransportBuilderByStore"/>
</type>
<type name="Magento\Framework\Mail\Template\TransportBuilder">
<plugin name="Aitoc_Smtp::TransportBuilder" type="Aitoc\Smtp\Plugin\Framework\Mail\Template\TransportBuilder"/>
</type>
<type name="Magento\Framework\Mail\Message">
<plugin name="Aitoc_Smtp::PlainPart" type="Aitoc\Smtp\Plugin\Framework\Mail\Message"/>
</type>
<type name="Magento\Framework\Mail\EmailMessage">
<arguments>
<argument name="encoding" xsi:type="string">utf-8</argument>
</arguments>
</type>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd">
<template id="magepal_smtp_zend_email_test" label="Test Email using Zend Framework" file="zend_email_test.html" type="html" module="MagePal_GmailSmtpApp" area="adminhtml"/>
<template id="magepal_smtp_magento_email_test" label="Test Email using Magento Framework" file="magento_email_test.html" type="html" module="MagePal_GmailSmtpApp" area="adminhtml"/>
</config>
\ No newline at end of file
<template id="aitsmtp_resend_template" label="Aitoc SMTP Resend Template" file="resend_template.html" type="html" module="Aitoc_Smtp" area="frontend"/>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Aitoc_Smtp" setup_version="1.0.0">
<sequence>
<module name="Aitoc_Core"/>
</sequence>
</module>
</config>
"Send Test Email","Send Test Email"
Reports,Reports
"Log doesn't exist.","Log doesn't exist."
"A total of %1 email(s) have been deleted.","A total of %1 email(s) have been deleted."
"Email Log has been successfully deleted","Email Log has been successfully deleted"
"Unable to find the rule","Unable to find the rule"
"Emails Log","Emails Log"
"A total of %1 email(s) have been sent.","A total of %1 email(s) have been sent."
"Email Preview","Email Preview"
"An error occurred. The email template can not be opened for preview.","An error occurred. The email template can not be opened for preview."
"Email successfully was send.","Email successfully was send."
"Something went wrong.","Something went wrong."
"Message is successfully send!","Message is successfully send!"
"Error. Something went wrong. Please, try again.","Error. Something went wrong. Please, try again."
"Not Required","Not Required"
Login/Password,Login/Password
CRAM-MD5,CRAM-MD5
None,None
SSL,SSL
TLS,TLS
Success,Success
Failed,Failed
Blocked,Blocked
"Gmail, GSuite","Gmail, GSuite"
Yandex,Yandex
"Amazon SES: US East (N. Virginia)","Amazon SES: US East (N. Virginia)"
"Amazon SES: US West (Oregon)","Amazon SES: US West (Oregon)"
"Amazon SES: EU (Ireland)","Amazon SES: EU (Ireland)"
Mailgun,Mailgun
Mandrill,Mandrill
Sendinblue,Sendinblue
Sendgrid,Sendgrid
"Elastic Email","Elastic Email"
SparkPost,SparkPost
Mailjet,Mailjet
Postmark,Postmark
"AOL Mail","AOL Mail"
Comcast,Comcast
GMX,GMX
Hotmail,Hotmail
Mail.com,Mail.com
"O2 Mail","O2 Mail"
Office365,Office365
Orange,Orange
Outlook,Outlook
"Yahoo Mail","Yahoo Mail"
"Yahoo Mail Plus","Yahoo Mail Plus"
"Yahoo AU/NZ","Yahoo AU/NZ"
AT&T,AT&T
"NTL @ntlworld.com","NTL @ntlworld.com"
"BT Connect","BT Connect"
"Zoho Mail","Zoho Mail"
Verizon,Verizon
"BT Openworld","BT Openworld"
"O2 Online Deutschland","O2 Online Deutschland"
"1&1 Webmail","1&1 Webmail"
OVH,OVH
SMTP2GO,SMTP2GO
"Preview Email","Preview Email"
"Delete Email Log","Delete Email Log"
Delete,Delete
"Are you sure you want to delete?","Are you sure you want to delete?"
"Resend Email","Resend Email"
"Are you sure you want to resend the selected email?","Are you sure you want to resend the selected email?"
Fail,Fail
"Validation Error","Validation Error"
"Please check if the following fields are filled in: ","Please check if the following fields are filled in: "
"Send Test To E-mail","Send Test To E-mail"
"Connection Security","Connection Security"
"Authentication Method","Authentication Method"
"SMTP Log","SMTP Log"
SMTP,SMTP
"General Settings","General Settings"
"Enable SMTP","Enable SMTP"
"When the option is disabled, the module will not impact on email sendings","When the option is disabled, the module will not impact on email sendings"
"SMTP Settings","SMTP Settings"
"Use Pre-Defined SMTP Providers","Use Pre-Defined SMTP Providers"
Provider,Provider
"Local Client Hostname","Local Client Hostname"
"Local client hostname or IP. Default: <strong>localhost</strong>","Local client hostname or IP. Default: <strong>localhost</strong>"
Host,Host
"Host name or IP address of SMTP Server","Host name or IP address of SMTP Server"
Port,Port
"Default ports: 25, 465, or 587. (<strong>SSL</strong> - 465, <strong>TLS</strong> - 587)","Default ports: 25, 465, or 587. (<strong>SSL</strong> - 465, <strong>TLS</strong> - 587)"
Username,Username
Password,Password
"Emails Settings","Emails Settings"
"Add Plain Text Part in Email","Add Plain Text Part in Email"
"Add Plain part in email recommended for <strong>increase Spam Score of email</strong>","Add Plain part in email recommended for <strong>increase Spam Score of email</strong>"
"Use Another Email Sender","Use Another Email Sender"
"<strong>Please notice,</strong> the <strong>Sender domain</strong> must be the same as the domain where you send email. Other combinations can lead to a ban because of spam","<strong>Please notice,</strong> the <strong>Sender domain</strong> must be the same as the domain where you send email. Other combinations can lead to a ban because of spam"
"Sender Email","Sender Email"
"Sender Name","Sender Name"
"CC Emails","CC Emails"
"BCC Emails","BCC Emails"
"Log Settings","Log Settings"
"Enable Log Outgoing Emails","Enable Log Outgoing Emails"
"See the all emails and their contents ever <strong>sent throughout Aitoc SMTP.</strong>","See the all emails and their contents ever <strong>sent throughout Aitoc SMTP.</strong>"
"Log Clean Every (days)","Log Clean Every (days)"
"<strong>Day(s).</strong> When empty or zero, the Email log will not be cleaned.","<strong>Day(s).</strong> When empty or zero, the Email log will not be cleaned."
Debug,Debug
"Disable Actual Email Delivery (Debug Mode)","Disable Actual Email Delivery (Debug Mode)"
"<strong>Set the option to ‘Yes’ in order not to send emails actually.</strong> That can be used for debug or development purposes","<strong>Set the option to ‘Yes’ in order not to send emails actually.</strong> That can be used for debug or development purposes"
"Exceptional Email Addresses","Exceptional Email Addresses"
"Exceptional Email Domains","Exceptional Email Domains"
"Clear Emails Log","Clear Emails Log"
"Delete logs","Delete logs"
"Delete selected logs?","Delete selected logs?"
Resend,Resend
"Resend selected logs?","Resend selected logs?"
ID,ID
"Sender Data","Sender Data"
"Recipient Data","Recipient Data"
"Email Subject","Email Subject"
"Email Status","Email Status"
"Fail Message","Fail Message"
"Send Date","Send Date"
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'MagePal_Core',
'Aitoc_Smtp',
__DIR__
);
<?xml version="1.0"?>
<!--
/*
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="styles"/>
<head>
<css src="MagePal_Core::css/config.css" />
<title>Emails Log</title>
</head>
<body>
<referenceContainer name="content">
<block class="MagePal\Core\Block\Adminhtml\Badge" name="magepal_email_edit"
template="MagePal_Core::badge.phtml"/>
<uiComponent name="aitoc_smtp_log_listing"/>
</referenceContainer>
</body>
</page>
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="empty" />
<body>
<referenceContainer name="root">
<block name="preview.page.content" class="Magento\Framework\View\Element\Template" template="Aitoc_Smtp::template/preview.phtml">
<block class="Aitoc\Smtp\Block\Adminhtml\Log\Preview" name="content" as="content"/>
</block>
</referenceContainer>
</body>
</page>
<?php
/** @var \Aitoc\Smtp\Block\Adminhtml\TestButton $block */
?>
<?= /* @escapeNotVerified */ $block->getData('send_button') ?>
<script type="text/javascript">
require(['jquery', 'underscore', 'Magento_Ui/js/modal/alert', 'mage/translate'], function($, _, modalAlert, $t) {
var self = this,
buttonId = '<?= /* @escapeNotVerified */ $block->getData('button_id'); ?>',
params = ['name', 'host', 'port', 'protocol', 'auth_type', 'login', 'password', 'test_email_to'];
$('#' + buttonId).click(function (e) {
e.preventDefault();
showOrHideAjaxLoader('show');
call();
});
function call() {
var params = prepareSendParams(),
isValid = true,
validationFields = '';
_.each(params, function(data, key) {
if (data == '' || data === undefined) {
isValid = false;
validationFields = validationFields + '<strong>' + fieldModify(key) + '</strong>; ';
}
});
if (isValid) {
$.ajax({
url: '<?= /* @escapeNotVerified */ $block->getData('ajax_url'); ?>',
data: params,
dataType: 'json',
success: function (result) {
showOrHideAjaxLoader('hide');
modalAlert({
title: result.status ? $t('Success') : $t('Fail'),
content: result.content
});
}
});
} else {
showOrHideAjaxLoader('hide');
modalAlert({
title: $t('Validation Error'),
content: $t('Please check if the following fields are filled in: ') + validationFields
});
}
}
function fieldModify(field) {
if (field == 'test_email_to') {
field = $t('Send Test To E-mail');
return field;
}
if (field == 'protocol') {
field = $t('Connection Security');
return field;
}
if (field == 'auth_type') {
field = $t("Authentication Method");
return field;
}
if(field.length) {
field = field.charAt(0).toUpperCase() + field.slice(1);
}
return field;
}
function showOrHideAjaxLoader(action) {
$('body').loader(action);
return true;
}
/**
*
*/
function prepareSendParams() {
var selector = 'aitsmtp_smtp_',
result = {};
_.each(params, function(data){
result[data] = $('#' + selector + data).val();
});
return result;
}
})
</script>
<?php
/** @var \Aitoc\Smtp\Block\Adminhtml\Provider $block */
?>
<script type="text/javascript">
require(['jquery', 'underscore'], function($, _) {
var self = this,
providers = $.parseJSON('<?= /* @escapeNotVerified */ $block->getData('providers'); ?>'),
params = ['host', 'port', 'protocol', 'auth_type'],
selectId = '#aitsmtp_smtp_provider';
$(selectId).change(function (e) {
var val = $(this).val();
var selector = 'aitsmtp_smtp_',
result = {};
_.each(params, function(data){
result[data] = $('#' + selector + data).val();
});
_.each(providers, function(data, key) {
if (key == val) {
_.each(params, function(param) {
var item = data.info[param];
if (param == 'protocol') {
item = data.info['encryption'];
switch (item) {
case 'ssl':
item = 1;
break;
case 'tls':
item = 2;
break;
default:
item = 0;
break;
}
}
if (param == 'auth_type') {
switch (item){
case 'login':
item = 1;
break;
default:
item = 0;
break;
}
}
$('#' + selector + param).val(item);
});
}
});
});
})
</script>
\ No newline at end of file
<?php
/* @var $block \Aitoc\Smtp\Block\Adminhtml\Log\Preview */
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title><?= $block->escapeHtml(__('Email Preview')) ?></title>
</head>
<body>
<?= $block->getChildHtml('content') ?>
</body>
</html>
<script type="text/javascript">
function resizeIframe(obj){
obj.style.height = 0;
obj.style.height = obj.contentWindow.document.body.scrollHeight + 'px';
}
</script>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing_data_source</item>
<item name="deps" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">aitoc_smtp_log_columns</item>
<item name="buttons" xsi:type="array">
<item name="clear_log" xsi:type="array">
<item name="name" xsi:type="string">clear</item>
<item name="class" xsi:type="string">primary</item>
<item name="label" xsi:type="string" translate="true">Clear Emails Log</item>
<item name="url" xsi:type="string">aitoc_smtp/log/clear</item>
</item>
</item>
</argument>
<dataSource name="aitoc_smtp_log_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider</argument>
<argument name="name" xsi:type="string">aitoc_smtp_log_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">log_id</argument>
<argument name="requestFieldName" xsi:type="string">log_id</argument>
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
<item name="update_url" xsi:type="url" path="mui/index/render"/>
<item name="storageConfig" xsi:type="array">
<item name="indexField" xsi:type="string">log_id</item>
</item>
</item>
</argument>
</argument>
</dataSource>
<container name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
</item>
</argument>
<bookmark name="bookmarks">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="namespace" xsi:type="string">aitoc_smtp_log_listing</item>
</item>
</item>
</argument>
</bookmark>
<component name="columns_controls">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsData" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns</item>
</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
<item name="displayArea" xsi:type="string">dataGridActions</item>
</item>
</argument>
</component>
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns.${ $.index }:visible</item>
</item>
</item>
</item>
</argument>
</filters>
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns.ids</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/tree-massactions</item>
<item name="indexField" xsi:type="string">log_id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete logs</item>
<item name="message" xsi:type="string" translate="true">Delete selected logs?</item>
</item>
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="aitoc_smtp/log/massDelete"/>
</item>
</argument>
</action>
<action name="resend">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Resend</item>
<item name="message" xsi:type="string" translate="true">Resend selected logs?</item>
</item>
<item name="type" xsi:type="string">resend</item>
<item name="label" xsi:type="string" translate="true">Resend</item>
<item name="url" xsi:type="url" path="aitoc_smtp/log/massResend"/>
</item>
</argument>
</action>
</massaction>
<paging name="listing_paging">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns.ids</item>
</item>
</argument>
</paging>
</container>
<columns name="aitoc_smtp_log_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.aitoc_smtp_log_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">view</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">aitoc_smtp_log_listing.aitoc_smtp_log_listing.listing_top.bookmarks</item>
<item name="root" xsi:type="string">columns.${ $.index }</item>
<item name="namespace" xsi:type="string">current.${ $.storageConfig.root}</item>
</item>
</item>
</item>
</argument>
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">log_id</item>
</item>
</argument>
</selectionsColumn>
<column name="log_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="sorting" xsi:type="string">desc</item>
<item name="resizeEnabled" xsi:type="boolean">true</item>
<item name="resizeDefaultWidth" xsi:type="string">40</item>
<item name="label" xsi:type="string" translate="true">ID</item>
</item>
</argument>
</column>
<column name="sender_email">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Sender Data</item>
</item>
</argument>
</column>
<column name="recipient_email">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Recipient Data</item>
</item>
</argument>
</column>
<column name="subject">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Email Subject</item>
</item>
</argument>
</column>
<column name="bcc">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">BCC Emails</item>
</item>
</argument>
</column>
<column name="cc">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">CC Emails</item>
</item>
</argument>
</column>
<column name="status" class="Aitoc\Smtp\Ui\Component\Listing\Column\Status">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="bodyTmpl" xsi:type="string">ui/grid/cells/html</item>
<item name="sortable" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true">Email Status</item>
<item name="resizeEnabled" xsi:type="boolean">true</item>
<item name="resizeDefaultWidth" xsi:type="string">100</item>
</item>
</argument>
</column>
<column name="status_message">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Fail Message</item>
</item>
</argument>
</column>
<column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Send Date</item>
</item>
</argument>
</column>
<actionsColumn name="actions" class="Aitoc\Smtp\Ui\Component\Listing\Column\ViewAction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">log_id</item>
<item name="viewUrlPath" xsi:type="string">aitoc_smtp/log/edit</item>
</item>
</argument>
</actionsColumn>
</columns>
</listing>
<!--@subject {{var subject}} @-->
{{var email_body|raw}}
......@@ -1555,6 +1555,19 @@ button.action.submit.primary {
box-shadow: 0 0 0 0px;
}
//处理选中色块方形改为圆形
.swatch-option::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 40px;
height: 40px;
border-radius: 50%;
}
//详情页mobile修改
@media (max-width: 780px){
.product-info-stock-sku{
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d0e90611d229c2dda065c2f56891ac7d",
"content-hash": "1f48ba899f2919a182ef06ff43283768",
"packages": [
{
"name": "aws/aws-crt-php",
......@@ -4436,139 +4436,6 @@
"time": "2020-12-02T21:12:59+00:00"
},
{
"name": "magepal/magento2-core",
"version": "1.1.12",
"source": {
"type": "git",
"url": "https://github.com/magepal/magento2-core.git",
"reference": "83fbffc0d540f82f55e0623719e08ecacc9c7e18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/magepal/magento2-core/zipball/83fbffc0d540f82f55e0623719e08ecacc9c7e18",
"reference": "83fbffc0d540f82f55e0623719e08ecacc9c7e18",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"magento/framework": "*"
},
"type": "magento2-module",
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\Core\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"proprietary"
],
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"description": "MagePal core extension",
"homepage": "https://www.magepal.com/",
"keywords": [
"magento 2",
"magepal core extension"
],
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-core/issues/",
"source": "https://github.com/magepal/magento2-core/tree/1.1.12"
},
"time": "2021-05-29T19:47:51+00:00"
},
{
"name": "magepal/magento2-gmailsmtpapp",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/magepal/magento2-gmail-smtp-app.git",
"reference": "e6e17cf275e4cee5db603ba3aea6aae8ec8c4547"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/magepal/magento2-gmail-smtp-app/zipball/e6e17cf275e4cee5db603ba3aea6aae8ec8c4547",
"reference": "e6e17cf275e4cee5db603ba3aea6aae8ec8c4547",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"magento/framework": "103.0.*",
"magento/module-backend": "102.0.*",
"magepal/magento2-core": ">1.1.0",
"php": "~7.4.0||~8.1.0"
},
"type": "magento2-module",
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\GmailSmtpApp\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"proprietary"
],
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"description": "Magento 2 SMTP Extension - Configure Magento 2 to send all transactional email using Gmail, G Suite, Amazon SES, Office360, Mailgun, SendGrid, Mandrill or any other SMTP servers",
"homepage": "https://www.magepal.com/",
"keywords": [
"Amazon SES",
"Amazon Simple Email Service",
"g suite",
"gmail smtp",
"google app",
"how to configure magento email",
"how to setup email magento2",
"magento 2",
"magento2 email",
"magento2 email setup",
"magento2 smtp",
"send email magento2"
],
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-gmail-smtp-app/issues/",
"source": "https://github.com/magepal/magento2-gmail-smtp-app/tree/2.9.0"
},
"funding": [
{
"url": "https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=github%20sponsor",
"type": "custom"
}
],
"time": "2022-04-12T22:17:11+00:00"
},
{
"name": "monolog/monolog",
"version": "1.27.1",
"source": {
......
......@@ -124,14 +124,12 @@ return array(
'8a9dc1de0ca7e01f3e08231539562f61' => $vendorDir . '/aws/aws-sdk-php/src/functions.php',
'a4ecaeafb8cfb009ad0e052c90355e98' => $vendorDir . '/beberlei/assert/lib/Assert/functions.php',
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
'b5843d9aa73dbbe9d709b76cbd3ff36f' => $vendorDir . '/magepal/magento2-core/registration.php',
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'103e25fab6e1520b3d5716d51befc8a8' => $vendorDir . '/swissup/module-breeze-integrations/registration.php',
'cbb1b03512ba520311d1592006f0c299' => $vendorDir . '/swissup/theme-frontend-breeze-blank/registration.php',
'940abd8fb01ee76a36b44f35dcf9783b' => $vendorDir . '/weew/helpers-array/src/array.php',
'8592c7b0947d8a0965a9e8c3d16f9c24' => $vendorDir . '/elasticsearch/elasticsearch/src/autoload.php',
'eda65932675b68b5aee4503e0762d64d' => $vendorDir . '/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/_bootstrap.php',
'0ba5079bc0aa31645404935d16e5e118' => $vendorDir . '/magepal/magento2-gmailsmtpapp/registration.php',
'3109cb1a231dcd04bee1f9f620d46975' => $vendorDir . '/paragonie/sodium_compat/autoload.php',
'aa75ea0761a2f40c1f3b32ad314f86c4' => $vendorDir . '/phpseclib/mcrypt_compat/lib/mcrypt.php',
'9b38cf48e83f5d8f60375221cd213eee' => $vendorDir . '/phpstan/phpstan/bootstrap.php',
......
......@@ -79,8 +79,6 @@ return array(
'Magento\\Composer\\' => array($vendorDir . '/magento/composer/src'),
'Magento\\' => array($baseDir . '/app/code/Magento'),
'Magento2\\' => array($vendorDir . '/magento/magento-coding-standard/Magento2'),
'MagePal\\GmailSmtpApp\\' => array($vendorDir . '/magepal/magento2-gmailsmtpapp'),
'MagePal\\Core\\' => array($vendorDir . '/magepal/magento2-core'),
'MFTF\\' => array($vendorDir . '/magento/magento2-functional-testing-framework/dev/tests/functional/tests/MFTF'),
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
'League\\Flysystem\\AwsS3V3\\' => array($vendorDir . '/league/flysystem-aws-s3-v3'),
......
......@@ -125,14 +125,12 @@ class ComposerStaticInitb71ce7c407b65980cf51508f463c8dcf
'8a9dc1de0ca7e01f3e08231539562f61' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/functions.php',
'a4ecaeafb8cfb009ad0e052c90355e98' => __DIR__ . '/..' . '/beberlei/assert/lib/Assert/functions.php',
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
'b5843d9aa73dbbe9d709b76cbd3ff36f' => __DIR__ . '/..' . '/magepal/magento2-core/registration.php',
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'103e25fab6e1520b3d5716d51befc8a8' => __DIR__ . '/..' . '/swissup/module-breeze-integrations/registration.php',
'cbb1b03512ba520311d1592006f0c299' => __DIR__ . '/..' . '/swissup/theme-frontend-breeze-blank/registration.php',
'940abd8fb01ee76a36b44f35dcf9783b' => __DIR__ . '/..' . '/weew/helpers-array/src/array.php',
'8592c7b0947d8a0965a9e8c3d16f9c24' => __DIR__ . '/..' . '/elasticsearch/elasticsearch/src/autoload.php',
'eda65932675b68b5aee4503e0762d64d' => __DIR__ . '/..' . '/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/_bootstrap.php',
'0ba5079bc0aa31645404935d16e5e118' => __DIR__ . '/..' . '/magepal/magento2-gmailsmtpapp/registration.php',
'3109cb1a231dcd04bee1f9f620d46975' => __DIR__ . '/..' . '/paragonie/sodium_compat/autoload.php',
'aa75ea0761a2f40c1f3b32ad314f86c4' => __DIR__ . '/..' . '/phpseclib/mcrypt_compat/lib/mcrypt.php',
'9b38cf48e83f5d8f60375221cd213eee' => __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
......@@ -239,8 +237,6 @@ class ComposerStaticInitb71ce7c407b65980cf51508f463c8dcf
'Magento\\Composer\\' => 17,
'Magento\\' => 8,
'Magento2\\' => 9,
'MagePal\\GmailSmtpApp\\' => 21,
'MagePal\\Core\\' => 13,
'MFTF\\' => 5,
),
'L' =>
......@@ -655,14 +651,6 @@ class ComposerStaticInitb71ce7c407b65980cf51508f463c8dcf
array (
0 => __DIR__ . '/..' . '/magento/magento-coding-standard/Magento2',
),
'MagePal\\GmailSmtpApp\\' =>
array (
0 => __DIR__ . '/..' . '/magepal/magento2-gmailsmtpapp',
),
'MagePal\\Core\\' =>
array (
0 => __DIR__ . '/..' . '/magepal/magento2-core',
),
'MFTF\\' =>
array (
0 => __DIR__ . '/..' . '/magento/magento2-functional-testing-framework/dev/tests/functional/tests/MFTF',
......
......@@ -6594,145 +6594,6 @@
"install-path": "../magento/zendframework1"
},
{
"name": "magepal/magento2-core",
"version": "1.1.12",
"version_normalized": "1.1.12.0",
"source": {
"type": "git",
"url": "https://github.com/magepal/magento2-core.git",
"reference": "83fbffc0d540f82f55e0623719e08ecacc9c7e18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/magepal/magento2-core/zipball/83fbffc0d540f82f55e0623719e08ecacc9c7e18",
"reference": "83fbffc0d540f82f55e0623719e08ecacc9c7e18",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"magento/framework": "*"
},
"time": "2021-05-29T19:47:51+00:00",
"type": "magento2-module",
"installation-source": "dist",
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\Core\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"proprietary"
],
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"description": "MagePal core extension",
"homepage": "https://www.magepal.com/",
"keywords": [
"magento 2",
"magepal core extension"
],
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-core/issues/",
"source": "https://github.com/magepal/magento2-core/tree/1.1.12"
},
"install-path": "../magepal/magento2-core"
},
{
"name": "magepal/magento2-gmailsmtpapp",
"version": "2.9.0",
"version_normalized": "2.9.0.0",
"source": {
"type": "git",
"url": "https://github.com/magepal/magento2-gmail-smtp-app.git",
"reference": "e6e17cf275e4cee5db603ba3aea6aae8ec8c4547"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/magepal/magento2-gmail-smtp-app/zipball/e6e17cf275e4cee5db603ba3aea6aae8ec8c4547",
"reference": "e6e17cf275e4cee5db603ba3aea6aae8ec8c4547",
"shasum": "",
"mirrors": [
{
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
"preferred": true
}
]
},
"require": {
"magento/framework": "103.0.*",
"magento/module-backend": "102.0.*",
"magepal/magento2-core": ">1.1.0",
"php": "~7.4.0||~8.1.0"
},
"time": "2022-04-12T22:17:11+00:00",
"type": "magento2-module",
"installation-source": "dist",
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\GmailSmtpApp\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"proprietary"
],
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"description": "Magento 2 SMTP Extension - Configure Magento 2 to send all transactional email using Gmail, G Suite, Amazon SES, Office360, Mailgun, SendGrid, Mandrill or any other SMTP servers",
"homepage": "https://www.magepal.com/",
"keywords": [
"Amazon SES",
"Amazon Simple Email Service",
"g suite",
"gmail smtp",
"google app",
"how to configure magento email",
"how to setup email magento2",
"magento 2",
"magento2 email",
"magento2 email setup",
"magento2 smtp",
"send email magento2"
],
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-gmail-smtp-app/issues/",
"source": "https://github.com/magepal/magento2-gmail-smtp-app/tree/2.9.0"
},
"funding": [
{
"url": "https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=github%20sponsor",
"type": "custom"
}
],
"install-path": "../magepal/magento2-gmailsmtpapp"
},
{
"name": "monolog/monolog",
"version": "1.27.1",
"version_normalized": "1.27.1.0",
......
......@@ -3,7 +3,7 @@
'name' => 'magento/magento2ce',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'c83cfc8de2807a1d60faa3507e08df0346299121',
'reference' => '5132e606a8b098df9263e3fc8475f2aeed786e52',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
......@@ -940,7 +940,7 @@
'magento/magento2ce' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => 'c83cfc8de2807a1d60faa3507e08df0346299121',
'reference' => '5132e606a8b098df9263e3fc8475f2aeed786e52',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
......@@ -2215,24 +2215,6 @@
'aliases' => array(),
'dev_requirement' => false,
),
'magepal/magento2-core' => array(
'pretty_version' => '1.1.12',
'version' => '1.1.12.0',
'reference' => '83fbffc0d540f82f55e0623719e08ecacc9c7e18',
'type' => 'magento2-module',
'install_path' => __DIR__ . '/../magepal/magento2-core',
'aliases' => array(),
'dev_requirement' => false,
),
'magepal/magento2-gmailsmtpapp' => array(
'pretty_version' => '2.9.0',
'version' => '2.9.0.0',
'reference' => 'e6e17cf275e4cee5db603ba3aea6aae8ec8c4547',
'type' => 'magento2-module',
'install_path' => __DIR__ . '/../magepal/magento2-gmailsmtpapp',
'aliases' => array(),
'dev_requirement' => false,
),
'monolog/monolog' => array(
'pretty_version' => '1.27.1',
'version' => '1.27.1.0',
......
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Block\Adminhtml;
use Magento\Backend\Block\Template;
use MagePal\Core\Controller\Adminhtml\Version\Index;
use MagePal\Core\Helper\Data;
class Badge extends Template
{
const SEARCH_URL = 'https://www.magepal.com/catalogsearch/result/';
/**
* @var Data
*/
private $dataHelper;
/**
* Badge constructor.
* @param Template\Context $context
* @param Data $dataHelper
* @param array $data
*/
public function __construct(
Template\Context $context,
Data $dataHelper,
array $data = []
) {
parent::__construct($context, $data);
$this->dataHelper = $dataHelper;
}
/**
* @return bool
*/
public function getNotificationOption()
{
return $this->dataHelper->getBadgeNotificationValue();
}
/**
* @return string
*/
public function getNotificationUrl()
{
return $this->getUrl('magepal/version/index');
}
/**
* @return bool
*/
public function isAuthorized()
{
return $this->_authorization->isAllowed(Index::ADMIN_RESOURCE);
}
/**
* @return string
*/
public function getSearchUrl()
{
return self::SEARCH_URL . '?utm_source=search&utm_medium=admin&utm_campaign=core';
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Block\Adminhtml\System\Config\Composer;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
use MagePal\Core\Model\Module;
class Version extends Field
{
/**
* @var Module
*/
private $module;
/**
* @param Context $context
* @param Module $module
* @param array $data
*/
public function __construct(
Context $context,
Module $module,
array $data = []
) {
parent::__construct($context, $data);
$this->module = $module;
}
/**
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
// Remove scope label
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
/**
* @param AbstractElement $element
* @return string
*/
protected function _getElementHtml(AbstractElement $element)
{
$isElementIdModuleName = (strpos($element->getOriginalData('id'), 'MagePal_') === 0);
$moduleName = $isElementIdModuleName ? $element->getOriginalData('id') : $this->getModuleName();
return 'v' . $this->module->getInstalledVersion($moduleName);
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Block\Adminhtml\System\Config\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
use MagePal\Core\Model\Module;
class Extensions extends Field
{
/**
* @var string
*/
protected $_template = 'MagePal_Core::system/config/field/extensions.phtml';
/**
* @var Module
*/
private $module;
public function __construct(
Context $context,
Module $module,
array $data = []
) {
parent::__construct($context, $data);
$this->module = $module;
}
/**
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
return $this->toHtml();
}
/**
* @return int
*/
public function getUpdateCount()
{
return $this->module->getUpdateCount();
}
/**
* @return array
*/
public function getExtensionList()
{
return $this->module->getOutDatedExtension();
}
/**
* @return array
*/
public function getRelatedProduct()
{
$result = [];
$installedExtensions = $this->module->getMyExtensionList();
foreach ($this->module->getProductFeed() as $key => $item) {
if (isset($item['upsell']) && $item['upsell'] == 1 && !array_key_exists($key, $installedExtensions)) {
$result[$key] = $item;
}
}
return $result;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Block\Adminhtml\System\Config\Field;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Search extends Field
{
/**
* @var string
*/
protected $_template = 'MagePal_Core::system/config/field/search.phtml';
/**
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
return $this->toHtml();
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Controller\Adminhtml\Version;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Controller\ResultInterface;
use MagePal\Core\Model\Module;
class Index extends Action
{
const ADMIN_RESOURCE = 'MagePal_Core::config';
/**
* @var JsonFactory
*/
private $resultJsonFactory;
/**
* @var Module
*/
private $module;
/**
* Index constructor.
* @param Context $context
* @param JsonFactory $resultJsonFactory
* @param Module $module
*/
public function __construct(
Context $context,
JsonFactory $resultJsonFactory,
Module $module
) {
parent::__construct($context);
$this->resultJsonFactory = $resultJsonFactory;
$this->module = $module;
}
/**
* Index action
*
* @return ResultInterface
*/
public function execute()
{
$data = [
'success' => 1,
'count' => $this->module->getCachedUpdateCount()
];
$result = $this->resultJsonFactory->create();
$result->setHeader('Cache-Control', 'max-age=302400', true);
$result->setHeader('Pragma', 'cache', true);
$result->setData($data);
return $result;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;
class Data extends AbstractHelper
{
const XML_PATH_ACTIVE = 'magepal_core/general/badge_notification';
const NOTIFICATION_DISABLED = 0;
const NOTIFICATION_ENABLED = 1;
const NOTIFICATION_WITHIN_TAB = 2;
/**
* If enabled
*
* @param null $scopeCode
* @return bool
*/
public function getBadgeNotificationValue($scopeCode = null)
{
return (int) $this->scopeConfig->getValue(
self::XML_PATH_ACTIVE,
ScopeInterface::SCOPE_STORE,
$scopeCode
);
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Model\Config\Source;
use Magento\Framework\Option\ArrayInterface;
use MagePal\Core\Helper\Data;
class BadgeNotification implements ArrayInterface
{
/**
* @return array
*/
public function toOptionArray()
{
return [
['value' => Data::NOTIFICATION_ENABLED, 'label' => 'Yes'],
['value' => Data::NOTIFICATION_WITHIN_TAB, 'label' => 'When Tab Open'],
['value' => Data::NOTIFICATION_DISABLED, 'label' => __('No')]
];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\Core\Model;
use Exception;
use InvalidArgumentException;
use Magento\Backend\Model\Session;
use Magento\Framework\App\Cache\Type\Config;
use Magento\Framework\Component\ComponentRegistrar;
use Magento\Framework\Component\ComponentRegistrarInterface;
use Magento\Framework\Filesystem\Directory\ReadFactory;
use Magento\Framework\HTTP\ClientFactory;
use Magento\Framework\Module\ModuleListInterface;
class Module
{
const URL = "https://updates.magepal.com/extensions.json";
const CACHE_KEY = 'magepal_extension_installed_list';
const SESSION_KEY = 'magepal-core-notification-data';
const DATA_VERSION = '1.0.3';
const LIFE_TIME = 604800;
/** @var int $updateCounter */
protected $updateCounter = 0;
/** @var string[] $ignoreList */
private $ignoreList = [
'MagePal_Core'
];
/** @var string */
private $filterModule = 'MagePal_';
private $composerJsonData = [];
private $myExtensionList = [];
private $outDatedExtensionList = [];
/**
* @var ModuleListInterface
*/
private $moduleList;
/**
* @var ComponentRegistrarInterface
*/
private $componentRegistrar;
/**
* @var ReadFactory
*/
private $readFactory;
/**
* @var ClientFactory
*/
private $httpClientFactory;
/**
* @var Config
*/
private $cache;
/**
* @var Session
*/
private $session;
private $timeStamp;
/**
* @var array|mixed
*/
private $latestVersions = [];
/**
* @param ModuleListInterface $moduleList
* @param ComponentRegistrarInterface $componentRegistrar
* @param ClientFactory $httpClientFactory
* @param ReadFactory $readFactory
* @param Config $cache
* @param Session $session
*/
public function __construct(
ModuleListInterface $moduleList,
ComponentRegistrarInterface $componentRegistrar,
ClientFactory $httpClientFactory,
ReadFactory $readFactory,
Config $cache,
Session $session
) {
$this->moduleList = $moduleList;
$this->componentRegistrar = $componentRegistrar;
$this->readFactory = $readFactory;
$this->httpClientFactory = $httpClientFactory;
$this->cache = $cache;
$this->session = $session;
}
/**
* @param string $needle
* @param array $haystack
* @param bool $defaultValue
* @return bool|mixed
*/
public function getArrayKeyIfExist($needle, $haystack, $defaultValue = false)
{
return array_key_exists($needle, $haystack) ? $haystack[$needle] : $defaultValue;
}
/**
* @return int
*/
public function getUpdateCount()
{
$this->getOutDatedExtension();
return $this->updateCounter;
}
/**
* @return int
*/
protected function getTimeStamp()
{
if (empty($this->timeStamp)) {
$this->timeStamp = strtotime("now");
}
return $this->timeStamp;
}
/**
* @return int
*/
protected function getTtl()
{
return $this->getTimeStamp() + 60 * 60 * 24;
}
/**
* @return int
*/
public function getCachedUpdateCount()
{
$now = $this->getTimeStamp();
$hash = $this->getHash();
$data = (array) $this->session->getData(self::SESSION_KEY);
if (empty($data) || !is_array($data)
|| $this->getArrayKeyIfExist('hash', $data, '') !== $hash
|| $now > (int) $this->getArrayKeyIfExist('ttl', $data, 0)
) {
$data = $this->setCountCache($this->getUpdateCount());
}
return (int) $data['count'] ?? 0;
}
/**
* @param $amount
* @return array
*/
protected function setCountCache($amount)
{
$data = [
'count' => $amount,
'hash' => $this->getHash(),
'ttl' => $this->getTtl()
];
$this->session->setData(self::SESSION_KEY, $data);
return $data;
}
/**
* @return string
*/
public function getHash()
{
return md5(json_encode($this->getPostData()));
}
public function getProductFeed()
{
if (empty($this->latestVersions)) {
$this->getOutDatedExtension();
}
return (array) $this->latestVersions;
}
/**
* @return array
*/
public function getOutDatedExtension()
{
if (empty($this->outDatedExtensionList)) {
$data = $this->cache->load(self::CACHE_KEY);
try {
$dataObject = $data ? $this->decodeJson($data, true) : [];
} catch (Exception $e) {
$dataObject = [];
}
if (!$data
|| $this->getArrayKeyIfExist('data_version', $dataObject, 0) != self::DATA_VERSION
|| $this->getArrayKeyIfExist('hash', $dataObject, '') !== $this->getHash()
) {
$this->loadOutDatedExtensionCollection();
} else {
if (array_key_exists('count', $dataObject)) {
$this->updateCounter = $dataObject['count'];
}
if (array_key_exists('extensions', $dataObject)) {
$this->outDatedExtensionList = $dataObject['extensions'];
}
if (array_key_exists('latestVersions', $dataObject)) {
$this->latestVersions = $dataObject['latestVersions'];
}
}
}
return $this->outDatedExtensionList;
}
/**
* @return array
*/
public function loadOutDatedExtensionCollection()
{
try {
$extensionList = $this->getMyExtensionList();
$feed = $this->callApi(self::URL, $this->getPostData());
$this->latestVersions = $feed['extensions'] ?? [];
$hasUpdate = false;
foreach ($extensionList as $item) {
$item['latest_version'] = $item['install_version'];
$item['has_update'] = false;
$item['url'] = 'https://www.magepal.com/extensions.com';
$item['name'] = $this->getTitleFromModuleName($item['moduleName']);
if (array_key_exists($item['composer_name'], $this->latestVersions)) {
$latest = $this->latestVersions[$item['composer_name']];
$item['latest_version'] = $latest['latest_version'];
$item['has_update'] = version_compare($item['latest_version'], $item['install_version']) > 0;
$item['url'] = $latest['url'];
$item['name'] = $latest['name'] ?? $item['name'];
if ($item['has_update']) {
$this->updateCounter += 1;
$hasUpdate = true;
}
}
$this->outDatedExtensionList[] = $item;
}
if ($hasUpdate) {
$this->setCountCache($this->updateCounter);
}
$dataObject = [
'count' => $this->updateCounter,
'extensions' => $this->outDatedExtensionList,
'data_version' => self::DATA_VERSION,
'hash' => $this->getHash(),
'latestVersions' => $this->latestVersions
];
$this->cache->save(json_encode($dataObject), self::CACHE_KEY, [], self::LIFE_TIME);
} catch (Exception $e) {
$this->outDatedExtensionList = [];
}
return $this->outDatedExtensionList;
}
/**
* @param $moduleName
* @return string
*/
private function getTitleFromModuleName($moduleName)
{
$moduleName = str_replace($this->filterModule, '', $moduleName);
return implode(
' ',
preg_split(
'/(?<=[a-z])(?=[A-Z])/x',
$moduleName
)
);
}
/**
* @return array
*/
public function getPostData()
{
$result = [];
foreach ($this->getMyExtensionList() as $key => $value) {
$result[$key] = $value['install_version'] ?? '0.0.0';
}
return $result;
}
/**
* @return array
*/
public function getMyExtensionList()
{
if (empty($this->myExtensionList)) {
foreach ($this->moduleList->getNames() as $name) {
if (strpos($name, $this->filterModule) !== false && !in_array($name, $this->ignoreList)) {
$composerName = $this->getInstalledComposerName($name);
if ($composerName) {
$this->myExtensionList[$composerName] = [
'moduleName' => $name,
'composer_name' => $composerName,
'install_version' => $this->getInstalledVersion($name),
];
}
}
}
}
return $this->myExtensionList;
}
/**
* @param $moduleName
* @param bool $assoc
* @return mixed
*/
public function getLocalComposerData($moduleName, $assoc = false)
{
if (!array_key_exists($moduleName, $this->composerJsonData)) {
$path = $this->componentRegistrar->getPath(
ComponentRegistrar::MODULE,
$moduleName
);
try {
$directoryRead = $this->readFactory->create($path);
$composerJsonData = $directoryRead->readFile('composer.json');
$this->composerJsonData[$moduleName] = $this->decodeJson($composerJsonData, $assoc);
} catch (Exception $e) {
$this->composerJsonData[$moduleName] = [];
}
}
return $this->composerJsonData[$moduleName];
}
/**
* @param $moduleName
* @return mixed|string
*/
public function getInstalledVersion($moduleName)
{
$version = '0.0.0';
if ($data = $this->getLocalComposerData($moduleName, true)) {
$version = $data['version'] ?? $version;
}
return $version;
}
/**
* @param $moduleName
* @return mixed|string
*/
public function getInstalledComposerName($moduleName)
{
$name = '';
if ($data = $this->getLocalComposerData($moduleName, true)) {
$name = $data['name'] ?? '';
}
return $name;
}
/**
* @param $data
* @param $assoc
* @return array|object
* @throws InvalidArgumentException
*/
public function decodeJson($data, $assoc = false)
{
$result = json_decode($data, $assoc);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new InvalidArgumentException("Unable to unserialize value. Error: " . json_last_error_msg());
}
return $result;
}
/**
* @param $url
* @param $post
* @return array
*/
protected function callApi($url, $post)
{
$client = $this->httpClientFactory->create();
$client->setOption(CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36');
//$client->setOption(CURLOPT_RETURNTRANSFER, 1);
$client->setOption(CURLOPT_FOLLOWLOCATION, 1);
$client->get($url. "?" . http_build_query($post));
try {
$result = $this->decodeJson($client->getBody(), true);
} catch (Exception $e) {
$result = [];
}
return $result;
}
}
<a href="http://www.magepal.com" title="Magento Extensions" ><img src="https://image.ibb.co/dHBkYH/Magepal_logo.png" width="100" align="right" title="Magento Custom Modules" /></a>
# MagePal Core Extension:
![magepal-core](https://user-images.githubusercontent.com/1415141/87557253-a9cdac80-c685-11ea-9f22-d2fa44b184a9.png)
---
- [Custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html)
- [Catalog Hover Image for Magento](https://www.magepal.com/magento2/extensions/catalog-hover-image-for-magento.html)
- [Enhanced Success Page for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-success-page.html)
- [Enhanced Transactional Emails for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html)
- [Google Tag Manager](https://www.magepal.com/magento2/extensions/google-tag-manager.html)
- [Enhanced E-commerce](https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html)
- [Reindex](https://www.magepal.com/magento2/extensions/reindex.html)
- [Custom Shipping Method](https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html)
- [Preview Order Confirmation](https://www.magepal.com/magento2/extensions/preview-order-confirmation-page-for-magento-2.html)
- [Guest to Customer](https://www.magepal.com/magento2/extensions/guest-to-customer.html)
- [Admin Form Fields Manager](https://www.magepal.com/magento2/extensions/admin-form-fields-manager-for-magento-2.html)
- [Customer Dashboard Links Manager](https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html)
- [Lazy Loader](https://www.magepal.com/magento2/extensions/lazy-load.html)
- [Order Confirmation Page Miscellaneous Scripts](https://www.magepal.com/magento2/extensions/order-confirmation-miscellaneous-scripts-for-magento-2.html)
- [HTML Minifier for Magento2](https://www.magepal.com/magento2/extensions/html-minifier.html)
© MagePal LLC. | [www.magepal.com](https://www.magepal.com)
{
"name": "magepal/magento2-core",
"description": "MagePal core extension",
"keywords": [
"magento 2",
"magepal core extension"
],
"license": [
"proprietary"
],
"homepage": "https://www.magepal.com/",
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-core/issues/"
},
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"require": {
"magento/framework": "*"
},
"type": "magento2-module",
"version": "1.1.12",
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\Core\\": ""
}
}
}
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="MagePal_Core::config" title="MagePal Notification"/>
</resource>
</resources>
</acl>
</config>
<?xml version="1.0"?>
<!--
/*
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="magepal" translate="label" sortOrder="400" class="admin__page-magepal-tab">
<label>MagePal</label>
</tab>
<section id="magepal_core" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1">
<label><![CDATA[Notifications & Updates]]></label>
<tab>magepal</tab>
<resource>MagePal_Core::config</resource>
<group id="notification" type="text" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="0" translate="label">
<label>Search</label>
<attribute type="expanded">1</attribute>
<frontend_model>MagePal\Core\Block\Adminhtml\System\Config\Field\Search</frontend_model>
</group>
<group id="updates" type="text" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="10" translate="label">
<label>Updates</label>
<attribute type="expanded">1</attribute>
<frontend_model>MagePal\Core\Block\Adminhtml\System\Config\Field\Extensions</frontend_model>
</group>
<group id="general" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="20" translate="label">
<label>Setting</label>
<field id="badge_notification" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Show Badge Notification</label>
<source_model>MagePal\Core\Model\Config\Source\BadgeNotification</source_model>
<comment><![CDATA[When Tab Open - Badge update count only shows when MagePal tab is expanded, in the "Notifications & Updates" section.]]></comment>
</field>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<magepal_core>
<general>
<badge_notification>1</badge_notification>
</general>
</magepal_core>
</default>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="MagePal_Core" setup_version="1.0.0" />
</config>
var config = {
map: {
'*': {
magePalCoreNotificationIcon: 'MagePal_Core/js/notification-icon'
}
}
};
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
/** @var $block \MagePal\Core\Block\Adminhtml\Badge */
?>
<?php if ($block->isAuthorized()) : ?>
<script type="text/x-magento-init">
{
"*": {
"magePalCoreNotificationIcon": {
"url":"<?= $block->escapeUrl($block->getNotificationUrl()) ?>",
"searchUrl":"<?= $block->escapeUrl($block->getSearchUrl()) ?>",
"notificationOption": "<?= $block->getNotificationOption() ?>"
}
}
}
</script>
<?php endif; ?>
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
/** @var $block MagePal\Core\Block\Adminhtml\System\Config\Field\Extensions */
?>
<div id="magepal-extension-list">
<?php if ($block->getUpdateCount()): ?>
<div class="entry-edit-head">
<?= __('Get the latest updates') ?>
</div>
<div class="cards">
<?php foreach ($block->getExtensionList() as $item): ?>
<?php if ($item['has_update']): ?>
<div class="card">
<div class="headerline">
<?= $block->escapeHtml($item['name'] ?? '') ?>
</div>
<div class="wrapper">
<div class="text-version update">
<span><?= $block->escapeHtml($item['install_version'] ?? '') ?></span>
<span>&#8594;</span>
<span><?= $block->escapeHtml($item['latest_version'] ?? '') ?></span>
</div>
<?php if (isset($item['url'])): ?>
<div class="info">
<a href="<?= $block->escapeUrl($item['url']) ?>" target="_blank">Learn More</a>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (count($block->getExtensionList()) > $block->getUpdateCount()): ?>
<div class="entry-edit-head">
<?= __('Up-to-Date Extensions') ?>
</div>
<div class="cards">
<?php foreach ($block->getExtensionList() as $item): ?>
<?php if (!$item['has_update']): ?>
<div class="card">
<div class="headerline"><?= $block->escapeHtml($item['name'] ?? '') ?></div>
<div class="wrapper">
<div class="text-version">
<?= $block->escapeHtml($item['install_version'] ?? '') ?>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
<?php foreach ($block->getRelatedProduct() as $item): ?>
<div class="card">
<div class="headerline"><?= $block->escapeHtml($item['name'] ?? '') ?></div>
<div class="wrapper">
<div class="latest-version">
<?= __('Not Installed') ?>
</div>
<?php if (isset($item['url'])): ?>
<div class="info">
<a href="<?= $block->escapeUrl($item['url']) ?>" target="_blank">Get Now!</a>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
?>
<div class="magepal-extension-search">
<div class="search">
<div id="cover">
<div class="tb">
<div class="td">
<input type="text" name="search_query" class="search-input"
placeholder="Search MagePal Extension Marketplace for..." />
</div>
<div class="td" id="s-cover">
<button type="button">
<div id="s-circle"></div>
<span></span>
</button>
</div>
</div>
</div>
</div>
</div>
.config-nav-block {
.mp-logo {
background: url(../images/magepal-logo.png) no-repeat;
vertical-align: middle;
background-size: cover;
display: inline-block;
height: 30px;
line-height: 25px;
margin-top: -1.2em;
min-width: 30px;
position: absolute;
top: 50%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
&.admin__page-magepal-tab {
strong {
display: flex;
align-items: center;
&:before {
/*background: url(../images/magepal-logo.png) no-repeat;*/
width: 20px;
height: 16px;
background-size: cover;
content: '';
display: inline-block;
margin-right: 18px;
vertical-align: middle;
}
}
@media (min-width: 1245px) {
&._hide li.item {
.notifications-counter {
display: none;
}
}
&._show strong {
.notifications-counter {
display: none;
}
}
}
@media (max-width: 1245px) {
li.item {
.notifications-counter {
display: none;
}
}
}
.notifications-counter {
background-color: #eb5202;
border-radius: 1em;
color: #ffffff;
display: inline-block;
font-size: 1.2rem;
font-weight: 700;
height: 25px;
left: 100%;
line-height: 25px;
margin-left: -5.7em;
margin-top: -1.1em;
min-width: 25px;
position: absolute;
text-align: center;
top: 50%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
}
}
#magepal-extension-list {
.entry-edit-head {
font-size: 2.0rem;
padding: 1.8rem 1.5rem;
text-transform: uppercase;
background-color: #f8f8f8;
border-bottom: 1px solid #e3e3e3;
border-top: 1px solid #e3e3e3;
margin-bottom: 13px;
}
.headerline {
color: black;
font-weight: 600;
text-align: center;
height: 45px;
box-shadow: 0 11px 17px -17px #000000;
}
.text-version {
margin-top: 12px;
text-align: center;
color: green;
}
.latest-version {
margin-top: 12px;
text-align: center;
color: red;
}
.update {
color: red;
}
.card {
background-color: #f8f8f8;
border: #41362f;
color: white;
padding: 1rem;
height: 10rem;
border-radius: 10px;
box-shadow: 0 8px 5px 0 rgba(0,0,0,0.2), 0 6px 20px 0 rgba(0,0,0,0.19);
}
.cards {
margin-top: 10px;
margin-bottom: 30px;
display: grid;
grid-gap: 1rem;
}
/* Screen larger than 600px? 2 column */
@media (min-width: 400px) {
.cards { grid-template-columns: repeat(2, 1fr); }
}
/* Screen larger than 900px? 3 columns */
@media (min-width: 700px) {
.cards { grid-template-columns: repeat(3, 1fr); }
}
/* Screen larger than 900px? 3 columns */
@media (min-width: 1200px) {
.cards { grid-template-columns: repeat(4, 1fr); }
}
/* Screen larger than 900px? 3 columns */
@media (min-width: 1300px) {
.cards { grid-template-columns: repeat(5, 1fr); }
}
/* Screen larger than 900px? 3 columns */
@media (min-width: 1600px) {
.cards { grid-template-columns: repeat(6, 1fr); }
}
.info {
text-align: center;
color: black;
padding-top: 3px;
a {
color: black;
}
}
}
.magepal-extension-search {
background-color: #514943;
margin-bottom: 20px;
.tb
{
display: table;
width: 100%;
}
.td
{
display: table-cell;
vertical-align: middle;
}
input, button
{
color: #fff;
padding: 0;
margin: 0;
border: 0;
background-color: transparent;
}
#cover
{
padding: 35px;
background-color: #1C96BC;
border-radius: 20px;
box-shadow: 0 10px 40px #a9afb0, 0 0 0 20px #ffffffeb;
transform: scale(0.6);
}
input[type="text"]
{
width: 100%;
height: 65px;
font-size: 20px;
line-height: 1;
@media (min-width: 1130px) {
font-size: 25px;
}
@media (min-width: 1325px) {
font-size: 32px;
}
}
input[type="text"]::placeholder
{
color: black;
}
input[type="text"]::-webkit-input-placeholder {
color: black;
}
input[type="text"]:-moz-placeholder { /* Firefox 18- */
color: black;
}
input[type="text"]::-moz-placeholder { /* Firefox 19+ */
color: black;
}
input[type="text"]:-ms-input-placeholder {
color: black;
}
#s-cover
{
width: 1px;
padding-left: 35px;
}
button
{
position: relative;
display: block;
width: 84px;
height: 96px;
cursor: pointer;
}
#s-circle
{
position: relative;
top: -8px;
left: 0;
width: 43px;
height: 43px;
margin-top: 0;
border: 15px solid #fff;
background-color: transparent;
border-radius: 50%;
transition: 0.5s ease all;
}
button span
{
position: absolute;
top: 68px;
left: 43px;
display: block;
width: 45px;
height: 15px;
background-color: transparent;
border-radius: 10px;
transform: rotateZ(52deg);
transition: 0.5s ease all;
}
button span:before, button span:after
{
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 45px;
height: 15px;
background-color: #fff;
border-radius: 10px;
transform: rotateZ(0);
transition: 0.5s ease all;
}
#s-cover:hover #s-circle
{
top: -1px;
width: 67px;
height: 15px;
border-width: 0;
background-color: #fff;
border-radius: 20px;
}
#s-cover:hover span
{
top: 50%;
left: 56px;
width: 25px;
margin-top: -9px;
transform: rotateZ(0);
}
#s-cover:hover button span:before
{
bottom: 11px;
transform: rotateZ(52deg);
}
#s-cover:hover button span:after
{
bottom: -11px;
transform: rotateZ(-52deg);
}
#s-cover:hover button span:before, #s-cover:hover button span:after
{
right: -6px;
width: 40px;
background-color: #fff;
}
#ytd-url {
display: block;
position: fixed;
right: 0;
bottom: 0;
padding: 10px 14px;
margin: 20px;
color: #fff;
font-size: 14px;
text-decoration: none;
background-color: #ff7575;
border-radius: 4px;
box-shadow: 0 10px 20px -5px rgba(255, 117, 117, 0.86);
z-index: 125;
}
}
.section-config.active {
#magepal-info, #magepal-info-dl, #magepal-info-ee, .magepal-info{
padding-bottom: 5px;
a {
font-weight: bold;
border-left: 2px solid #e3e3e3;
padding-left:10px;
padding-right:10px;
color: #ef7e1e;
&:first-child {
padding-left: 5px;
border-left: none;
}
}
}
#upgrade-to-enhanced-ecommerce,
#upgrade-to-datalayer,
#upgrade-to-google-analytics4,
.magepal-extension-promo
{
padding: 7px 10px;
border: 1px solid #e3e3e3;
background: #f8f8f8;
margin-bottom: 5px;
}
#system_gmailsmtpapp-head,
#googletagmanager_about-head,
#googletagmanager_enhanced_ecommerce-head,
#googletagmanager_datalayer-head,
#enhanced_transactional_emails_general-head,
#magepal_checkout_preview_success_page-head
{
padding-bottom: 0;
}
a[id$="about_magepal-head"] {
padding-bottom: 0;
}
#row_googletagmanager_gdpr_note label span{
display:none;
}
.magepal-hr {
border-top: 1px solid #e3e3e3
}
}
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
define([
'jquery'
], function ($) {
'use strict';
var $searchField = $('.magepal-extension-search .search-input');
var $searchButton = $('.magepal-extension-search button');
var $element = $('div.config-nav-block.admin__page-magepal-tab strong');
var html = '<span class="mp-logo"></span>';
$element.before(html);
var showBadge = function (config) {
$.ajax({
url: config.url,
type: 'get',
dataType: 'json',
error: function (){}
}).done(function (response) {
if (typeof response === 'object' && response.hasOwnProperty('count') && response.count > 0) {
var html = '<span class="notifications-counter">' + response.count + '</span>';
if (config.notificationOption !== 1) {
$element.append(html);
}
$element.parent().parent().find('ul.items li.item:first').append(html)
}
});
};
return function (config) {
if (config.notificationOption !== 0) {
showBadge(config);
}
var openWindow = function ($element) {
if ($($element).val().length > 2) {
var newTab = window.open();
newTab.location.href = config.searchUrl + '&q=' + encodeURI($element.val());
}
};
$searchField.keypress(function (event) {
if (event.keyCode === 13 || event.which === 13) {
openWindow($(this));
event.preventDefault();
}
});
$searchButton.on('click', function (event) {
event.preventDefault();
openWindow($searchField)
});
}
});
*.phtml linguist-language=PHP
*.html linguist-language=PHP
# These are supported funding model platforms
#github: [srenon]
custom: ['https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=github%20sponsor']
<!--- Before adding a new issue, please check all closed and existing issues to make sure this is not a duplicate -->
<!--- https://www.magepal.com/magento2/extensions/custom-smtp.html for fast Premium Support -->
#### Magento version #:
#### Edition (EE, CE, OS, etc):
#### Expected behavior:
#### Actual behavior:
#### Steps to reproduce:
#### Preconditions
<!--- Provide a more detailed information of environment you use -->
<!--- Magento version, tag, HEAD, etc., PHP & MySQL version, etc.. -->
<!---
PLEASE NOTE:
We receive multiple emails & support tickets almost daily asking for help.
In most cases these issues have nothing to do with our extension and mostly
caused by lack of basic Magento knowledge or not following installation instructions.
At MagePal, our goal is to develop a wide variety of both free and paid extension
and due to our limited resources, our main focus are fixing reported bugs and developing
other great extensions.
Because of this, we cannot provide free support for our free extensions.
However, we do offer very affordable support options and/or training.
For more information visit www.magepal.com or email us at support@magepal.com.
-->
name: PHPCS
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: PHPCS
run: docker run --rm -v $PWD:/code:ro domw/phpcs phpcs --colors --standard=Magento2 --report=full,summary,gitblame --extensions=php,phtml ./
- name: compatibility
run: docker run --rm -v $PWD:/code:ro domw/phpcompatibility phpcs --standard=PHPCompatibility --runtime-set testVersion 5.6-7.4 --colors --warning-severity=0 --report=full,summary --extensions=php,phtml ./
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Composer;
use Exception;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Component\ComponentRegistrar;
use Magento\Framework\Component\ComponentRegistrarInterface;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\Filesystem\Directory\ReadFactory;
class Version extends Field
{
/**
* @var DeploymentConfig
*/
protected $deploymentConfig;
/**
* @var ComponentRegistrarInterface
*/
protected $componentRegistrar;
/**
* @var ReadFactory
*/
protected $readFactory;
/**
* @param Context $context
* @param DeploymentConfig $deploymentConfig
* @param ComponentRegistrarInterface $componentRegistrar
* @param ReadFactory $readFactory
* @param array $data
*/
public function __construct(
Context $context,
DeploymentConfig $deploymentConfig,
ComponentRegistrarInterface $componentRegistrar,
ReadFactory $readFactory,
array $data = []
) {
$this->deploymentConfig = $deploymentConfig;
$this->componentRegistrar = $componentRegistrar;
$this->readFactory = $readFactory;
parent::__construct($context, $data);
}
/**
* Render button
*
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
// Remove scope label
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
/**
* Return element html
*
* @param AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _getElementHtml(AbstractElement $element)
{
return 'v' . $this->getVersion();
}
/**
* Get Module version number
*
* @return string
*/
public function getVersion()
{
return $this->getComposerVersion($this->getModuleName());
}
/**
* Get module composer version
*
* @param $moduleName
* @return string
*/
public function getComposerVersion($moduleName)
{
$path = $this->componentRegistrar->getPath(
ComponentRegistrar::MODULE,
$moduleName
);
try {
$directoryRead = $this->readFactory->create($path);
$composerJsonData = $directoryRead->readFile('composer.json');
if ($composerJsonData) {
$data = json_decode($composerJsonData);
return !empty($data->version) ? $data->version : __('Unknown');
}
} catch (Exception $e) {
return 'Unknown';
}
return 'Unknown';
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Field;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
class Link extends Field
{
/**
* Render button
*
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
// Remove scope label
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
/**
* Return element html
*
* @param AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _getElementHtml(AbstractElement $element)
{
return sprintf(
'<a href ="%s#system_gmailsmtpapp-link">%s</a>',
rtrim($this->_urlBuilder->getUrl('adminhtml/system_config/edit/section/system'), '/'),
__('Stores > Configuration > Advanced > System > SMTP Configuration and Settings')
);
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Module;
use Magento\Backend\Block\Template\Context;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Module\ModuleListInterface;
class Version extends Field
{
/**
* @var ModuleListInterface
*/
protected $_moduleList;
/**
* @param Context $context
* @param ModuleListInterface $moduleList
* @param array $data
*/
public function __construct(
Context $context,
ModuleListInterface $moduleList,
array $data = []
) {
parent::__construct($context, $data);
$this->_moduleList = $moduleList;
}
/**
* Render button
*
* @param AbstractElement $element
* @return string
* @throws LocalizedException
*/
public function render(AbstractElement $element)
{
// Remove scope label
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
/**
* Return element html
*
* @param AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _getElementHtml(AbstractElement $element)
{
return 'v' . $this->getVersion();
}
/**
* Get Module version number
*
* @return string
*/
public function getVersion()
{
$moduleInfo = $this->_moduleList->getOne($this->getModuleName());
return $moduleInfo['setup_version'];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Block\Adminhtml\System\Config;
use Magento\Backend\Block\Template\Context;
use Magento\Backend\Block\Widget\Button;
use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\Exception\LocalizedException;
/**
* "Reset to Defaults" button renderer
*
*/
class ValidateConfigButton extends Field
{
/** @var UrlInterface */
protected $_urlBuilder;
/**
* @param Context $context
* @param array $data
*/
public function __construct(
Context $context,
array $data = []
) {
$this->_urlBuilder = $context->getUrlBuilder();
parent::__construct($context, $data);
}
/**
* Set template
*
* @return void
*/
protected function _construct()
{
parent::_construct();
$this->setTemplate('MagePal_GmailSmtpApp::system/config/validateConfigButton.phtml');
}
/**
* Generate button html
*
* @return string
* @throws LocalizedException
*/
public function getButtonHtml()
{
$button = $this->getLayout()->createBlock(
Button::class
)->setData(
[
'id' => 'gmailsmtpapp_debug_result_button',
'label' => __('Send Test Email')
]
);
return $button->toHtml();
}
public function getAdminUrl()
{
return $this->_urlBuilder->getUrl(
'magepalsmtp/validateconfig',
['store' => $this->_request->getParam('store')]
);
}
/**
* Render button
*
* @param AbstractElement $element
* @return string
*/
public function render(AbstractElement $element)
{
// Remove scope label
$element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
return parent::render($element);
}
/**
* Return element html
*
* @param AbstractElement $element
* @return string
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function _getElementHtml(AbstractElement $element)
{
return $this->_toHtml();
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Block\Adminhtml;
use Exception;
use Laminas\Mime\Message as MineMessage;
use Laminas\Mime\Part as MinePart;
use Magento\Backend\Block\Template;
use Magento\Backend\Block\Template\Context;
use Magento\Framework\Validator\EmailAddress;
use MagePal\GmailSmtpApp\Helper\Data;
use MagePal\GmailSmtpApp\Model\Email;
use Laminas\Mail\Message;
use Laminas\Mail\Transport\Smtp;
use Laminas\Mail\Transport\SmtpOptions;
class ValidateConfig extends Template
{
/**
* @var Data
*/
protected $_dataHelper;
/**
* @var Email
*/
protected $_email;
/**
* @var string
*/
protected $toAddress;
/**
* @var string
*/
protected $fromAddress;
/**
* @var string
*/
protected $storeId;
/**
* @var string
*/
protected $hash;
/**
* Remove values from global post and store values locally
* @var array()
*/
protected $configFields = [
'active' => '',
'name' => '',
'auth' => '',
'ssl' => '',
'smtphost' => '',
'smtpport' => '',
'username' => '',
'password' => '',
'set_reply_to' => '',
'set_from' => '',
'set_return_path' => '',
'return_path_email' => '',
'custom_from_email' => '',
'email' => '',
'from_email' => ''
];
/**
* @var EmailAddress
*/
protected $emailAddressValidator;
/**
* EmailTest constructor.
* @param Context $context
* @param Data $dataHelper
* @param Email $email
* @param EmailAddress $emailAddressValidator
* @param array $data
*/
public function __construct(
Context $context,
Data $dataHelper,
Email $email,
EmailAddress $emailAddressValidator,
array $data = []
) {
parent::__construct($context, $data);
$this->_dataHelper = $dataHelper;
$this->_email = $email;
$this->emailAddressValidator = $emailAddressValidator;
$this->init();
}
/**
* @param $id
* @return $this
*/
public function setStoreId($id)
{
$this->storeId = $id;
return $this;
}
/**
* @return int \ null
*/
public function getStoreId()
{
return $this->storeId;
}
/**
* @param null $key
* @return array|mixed|string
*/
public function getConfig($key = null)
{
if ($key === null) {
return $this->configFields;
} elseif (!array_key_exists($key, $this->configFields)) {
return '';
} else {
return $this->configFields[$key];
}
}
/**
* @param string $key
* @param string $value
* @return array|mixed|string
*/
public function setConfig($key, $value = null)
{
if (array_key_exists($key, $this->configFields)) {
$this->configFields[$key] = $value;
}
return $this;
}
/**
* Load default config if config is lock using "bin/magento config:set"
*/
public function loadDefaultConfig()
{
$request = $this->getRequest();
$formPostArray = (array) $request->getPost();
$fields = array_keys($this->configFields);
foreach ($fields as $field) {
if (!array_key_exists($field, $formPostArray)) {
$this->setConfig($field, $this->_dataHelper->getConfigValue($field), $this->getStoreId());
} else {
$this->setConfig($field, $request->getPost($field));
}
}
//if password mask (6 stars)
if ($this->getConfig('password') === '******') {
$password = $this->_dataHelper->getConfigPassword($this->getStoreId());
$this->setConfig('password', $password);
}
return $this;
}
/**
* @return void
*/
protected function init()
{
$request = $this->getRequest();
$this->setStoreId($request->getParam('store', null));
$this->loadDefaultConfig();
$this->toAddress = $this->getConfig('email') ? $this->getConfig('email') : $this->getConfig('username');
$this->fromAddress = trim($this->getConfig('from_email'));
if (!$this->emailAddressValidator->isValid($this->fromAddress)) {
$this->fromAddress = $this->toAddress;
}
$this->hash = time() . '.' . rand(300000, 900000);
}
/**
* @return array
*/
public function verify()
{
$settings = [
'server_email' => 'validateServerEmailSetting',
'magento_email_setting' => 'validateMagentoEmailStatus',
'module_email_setting' => 'validateModuleEmailStatus',
'magento_email' => 'validateMagentoEmailSetting'
];
$result = $this->error();
$hasError = false;
foreach ($settings as $functionName) {
$result = $this->$functionName();
if (array_key_exists('has_error', $result)) {
if ($result['has_error'] === true) {
$hasError = true;
break;
}
} else {
$hasError = true;
$result = $this->error(true, 'MP103 - Unknown Error');
break;
}
}
if (!$hasError) {
$result['msg'] = __('Please check your email') . ' ' . $this->toAddress . ' ' .
__('and flush your Magento cache');
}
return [$result];
}
/**
* @return array
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
protected function validateServerEmailSetting()
{
$request = $this->getRequest();
$username = $this->getConfig('username');
$password = $this->getConfig('password');
$auth = strtolower($this->getConfig('auth'));
//if default view
//see https://github.com/magento/magento2/issues/3019
if (!$request->getParam('store', false)) {
if ($auth != 'none' && (empty($username) || empty($password))) {
return $this->error(
true,
__('Please enter a valid username/password')
);
}
}
$name = 'Test from MagePal SMTP';
$from = trim($this->getConfig('from_email'));
$from = filter_var($from, FILTER_VALIDATE_EMAIL) ? $from : $username;
$this->fromAddress = filter_var($username, FILTER_VALIDATE_EMAIL) ? $username : $from;
$htmlBody = $this->_email->setTemplateVars(['hash' => $this->hash])->getEmailBody();
$optionsArray = [
'name' => $this->getConfig('name'),
'host' => $this->getConfig('smtphost'),
'port' => $this->getConfig('smtpport')
];
if ($auth != 'none') {
$optionsArray['connection_class'] = $auth;
$optionsArray['connection_config'] = [
'username' => $username,
'password' => $password,
];
}
$ssl = $this->getConfig('ssl');
if ($ssl != 'none') {
$optionsArray = array_merge_recursive(
['connection_config' => ['ssl' => $ssl]],
$optionsArray
);
}
$options = new SmtpOptions($optionsArray);
$transport = new Smtp();
$transport->setOptions($options);
$bodyMessage = new MinePart($htmlBody);
$bodyMessage->type = 'text/html';
$body = new MineMessage();
$body->addPart($bodyMessage);
$message = new Message();
$message->addTo($this->toAddress, 'MagePal SMTP')
->addFrom($this->fromAddress, $name)
->setSubject('Hello from MagePal SMTP (1 of 2)')
->setBody($body)
->setEncoding('UTF-8');
$result = $this->error();
try {
$transport->send($message);
} catch (Exception $e) {
$result = $this->error(true, __($e->getMessage()));
}
return $result;
}
/**
* @return array
*/
protected function validateMagentoEmailSetting()
{
$result = $this->error();
$this->_dataHelper->setTestMode(true);
$this->_dataHelper->setStoreId($this->getStoreId());
$this->_dataHelper->setTestConfig($this->getConfig());
try {
$this->_email
->setTemplateVars(['hash' => $this->hash])
->send(
['email' => $this->fromAddress, 'name' => 'Test from MagePal SMTP'],
['email' => $this->toAddress, 'name' => "MagePal SMTP"]
);
} catch (Exception $e) {
$result = $this->error(true, __($e->getMessage()));
}
$this->_dataHelper->setTestMode(false);
return $result;
}
/**
* @return array
*/
public function validateMagentoEmailStatus()
{
$result = $this->error();
// system_smtp_disable
if ($this->_dataHelper->getScopeConfigValue('system/smtp/disable')) {
$result = $this->error(
true,
__('"Disable Email Communications" is set is "Yes", please set to "NO" in "Mail Sending Setting"')
);
}
return $result;
}
/**
* @return array
*/
public function validateModuleEmailStatus()
{
$result = $this->error();
if (!$this->getConfig('active')) {
$result = $this->error(
true,
__('SMTP module is not enabled')
);
}
return $result;
}
/**
* Format error msg
* @param string $s
* @return string
*/
public function formatErrorMsg($s)
{
return preg_replace(
'@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@',
'<a href="https://www.magepal.com/help/docs/smtp-magento/" target="_blank">$1</a>',
nl2br($s)
);
}
/**
* @param bool $hasError
* @param string $msg
* @return array
*/
public function error($hasError = false, $msg = '')
{
return [
'has_error' => (bool) $hasError,
'msg' => (string) $msg
];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Controller\Adminhtml\Validateconfig;
use Magento\Backend\App\Action;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
class Index extends Action
{
/**
* Authorization level of a basic admin session
*/
const ADMIN_RESOURCE = 'MagePal_GmailSmtpApp::magepal_gmailsmtpapp';
/**
* Index action
*
* @return ResultInterface
*/
public function execute()
{
return $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Store\Model\ScopeInterface;
class Data extends AbstractHelper
{
/**
* @var null $storeId
*/
protected $storeId = null;
/** @var bool $testMode */
protected $testMode = false;
/** @var array $testConfig */
protected $testConfig = [];
/**
* @param null $key
* @return array|mixed|string
*/
public function getTestConfig($key = null)
{
if ($key === null) {
return $this->testConfig;
} elseif (!array_key_exists($key, $this->testConfig)) {
return '';
} else {
return $this->testConfig[$key];
}
}
/**
* @param null $fields
* @return $this
*/
public function setTestConfig($fields)
{
$this->testConfig = (array) $fields;
return $this;
}
/**
* @param null $store_id
* @return bool
*/
public function isActive($store_id = null)
{
if ($store_id == null && $this->getStoreId() > 0) {
$store_id = $this->getStoreId();
}
return $this->scopeConfig->isSetFlag(
'system/gmailsmtpapp/active',
ScopeInterface::SCOPE_STORE,
$store_id
);
}
/**
* Get local client name
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigName($store_id = null)
{
return $this->getConfigValue('name', $store_id);
}
/**
* Get system config password
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigPassword($store_id = null)
{
return $this->getConfigValue('password', $store_id);
}
/**
* Get system config username
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigUsername($store_id = null)
{
return $this->getConfigValue('username', $store_id);
}
/**
* Get system config auth
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigAuth($store_id = null)
{
return $this->getConfigValue('auth', $store_id);
}
/**
* Get system config ssl
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigSsl($store_id = null)
{
return $this->getConfigValue('ssl', $store_id);
}
/**
* Get system config host
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigSmtpHost($store_id = null)
{
return $this->getConfigValue('smtphost', $store_id);
}
/**
* Get system config port
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigSmtpPort($store_id = null)
{
return $this->getConfigValue('smtpport', $store_id);
}
/**
* Get system config reply to
*
* @param ScopeInterface::SCOPE_STORE $store
* @return bool
*/
public function getConfigSetReplyTo($store_id = null)
{
return $this->scopeConfig->isSetFlag(
'system/gmailsmtpapp/set_reply_to',
ScopeInterface::SCOPE_STORE,
$store_id
);
}
/**
* Get system config set return path
*
* @param ScopeInterface::SCOPE_STORE $store
* @return int
*/
public function getConfigSetReturnPath($store_id = null)
{
return (int) $this->getConfigValue('set_return_path', $store_id);
}
/**
* Get system config return path email
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigReturnPathEmail($store_id = null)
{
return $this->getConfigValue('return_path_email', $store_id);
}
/**
* Get system config from
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigSetFrom($store_id = null)
{
return (int) $this->getConfigValue('set_from', $store_id);
}
/**
* Get system config from
*
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigCustomFromEmail($store_id = null)
{
return $this->getConfigValue('custom_from_email', $store_id);
}
/**
* Get system config
*
* @param String path
* @param ScopeInterface::SCOPE_STORE $store
* @return string
*/
public function getConfigValue($path, $store_id = null)
{
//send test mail
if ($this->isTestMode()) {
return $this->getTestConfig($path);
}
//return value from core config
return $this->getScopeConfigValue(
"system/gmailsmtpapp/{$path}",
$store_id
);
}
/**
* @param String path
* @param ScopeInterface::SCOPE_STORE $store
* @return mixed
*/
public function getScopeConfigValue($path, $store_id = null)
{
//use global store id
if ($store_id === null && $this->getStoreId() > 0) {
$store_id = $this->getStoreId();
}
//return value from core config
return $this->scopeConfig->getValue(
$path,
ScopeInterface::SCOPE_STORE,
$store_id
);
}
/**
* @return int/null
*/
public function getStoreId()
{
return $this->storeId;
}
/**
* @param int/null $storeId
*/
public function setStoreId($storeId = null)
{
$this->storeId = $storeId;
}
/**
* @return bool
*/
public function isTestMode()
{
return (bool) $this->testMode;
}
/**
* @param bool $testMode
* @return Data
*/
public function setTestMode($testMode)
{
$this->testMode = (bool) $testMode;
return $this;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Mail;
use Exception;
use Laminas\Mail\AddressList;
use Laminas\Mail\Header\HeaderInterface;
use Laminas\Mail\Message;
use Laminas\Mail\Transport\Smtp as SmtpTransport;
use Laminas\Mail\Transport\SmtpOptions;
use Laminas\Mime\Mime;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\EmailMessageInterface;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Phrase;
use MagePal\GmailSmtpApp\Helper\Data;
use MagePal\GmailSmtpApp\Model\Store;
/**
* Class Smtp
* For Magento >= 2.2.8
*/
class Smtp
{
/**
* @var Data
*/
protected $dataHelper;
/**
* @var Store
*/
protected $storeModel;
/**
* @param Data $dataHelper
* @param Store $storeModel
*/
public function __construct(
Data $dataHelper,
Store $storeModel
) {
$this->dataHelper = $dataHelper;
$this->storeModel = $storeModel;
}
/**
* @param Data $dataHelper
* @return Smtp
*/
public function setDataHelper(Data $dataHelper)
{
$this->dataHelper = $dataHelper;
return $this;
}
/**
* @param Store $storeModel
* @return Smtp
*/
public function setStoreModel(Store $storeModel)
{
$this->storeModel = $storeModel;
return $this;
}
/**
* @param $message
* @return Message
*/
protected function convertMessage($message)
{
/**
* Issues in Zend Framework 2
* https://github.com/zendframework/zendframework/issues/2492
* https://github.com/zendframework/zendframework/issues/2492
*/
$encoding = 'utf-8';
try {
$reflect = new \ReflectionClass($message);
$zendMessageObject = $reflect->getProperty('zendMessage');
$zendMessageObject->setAccessible(true);
/** @var Message $zendMessage */
$zendMessage = $zendMessageObject->getValue($message);
if ($message instanceof EmailMessageInterface) {
$encoding = $message->getEncoding();
} else {
$encoding = $zendMessage->getEncoding();
}
if (!$zendMessage instanceof Message) {
throw new MailException('Not instance of Message');
}
} catch (Exception $e) {
$zendMessage = Message::fromString($message->getRawMessage());
}
$zendMessage->setEncoding($encoding);
return $zendMessage;
}
/**
* @param MessageInterface | EmailMessageInterface $message
* @throws MailException
*/
public function sendSmtpMessage(
$message
) {
$dataHelper = $this->dataHelper;
$dataHelper->setStoreId($this->storeModel->getStoreId());
/** @var Message $message */
$message = $this->convertMessage($message);
$this->setReplyToPath($message);
$this->setSender($message);
foreach ($message->getHeaders()->toArray() as $headerKey => $headerValue) {
$mailHeader = $message->getHeaders()->get($headerKey);
if ($mailHeader instanceof HeaderInterface) {
$this->updateMailHeader($mailHeader);
} elseif ($mailHeader instanceof \ArrayIterator) {
foreach ($mailHeader as $header) {
$this->updateMailHeader($header);
}
}
}
try {
$transport = new SmtpTransport();
$transport->setOptions($this->getSmtpOptions());
$transport->send($message);
} catch (Exception $e) {
throw new MailException(
new Phrase($e->getMessage()),
$e
);
}
}
/**
*
* @param Message $message
*/
protected function setSender($message)
{
$dataHelper = $this->dataHelper;
//Set from address
switch ($dataHelper->getConfigSetFrom()) {
case 1:
$setFromEmail = $message->getFrom()->count() ? $message->getFrom() : $this->getFromEmailAddress();
break;
case 2:
$setFromEmail = $dataHelper->getConfigCustomFromEmail();
break;
default:
$setFromEmail = null;
break;
}
if ($setFromEmail !== null && $dataHelper->getConfigSetFrom()) {
if (is_string($setFromEmail)) {
$name = $this->getFromName();
$message->setFrom(trim($setFromEmail), $name);
$message->setSender(trim($setFromEmail), $name);
} elseif ($setFromEmail instanceof AddressList) {
foreach ($setFromEmail as $address) {
$message->setFrom($address);
$message->setSender($address);
}
}
}
if (!$message->getFrom()->count()) {
$result = $this->storeModel->getFrom();
$message->setFrom($result['email'], $result['name']);
}
}
/**
* @param Message $message
*/
protected function setReplyToPath($message)
{
$dataHelper = $this->dataHelper;
//Set reply-to path
switch ($dataHelper->getConfigSetReturnPath()) {
case 1:
$returnPathEmail = $message->getFrom()->count() ? $message->getFrom() : $this->getFromEmailAddress();
break;
case 2:
$returnPathEmail = $dataHelper->getConfigReturnPathEmail();
break;
default:
$returnPathEmail = null;
break;
}
if (!$message->getReplyTo()->count() && $dataHelper->getConfigSetReplyTo()) {
if (is_string($returnPathEmail)) {
$name = $this->getFromName();
$message->setReplyTo(trim($returnPathEmail), $name);
} elseif ($returnPathEmail instanceof AddressList) {
foreach ($returnPathEmail as $address) {
$message->setReplyTo($address);
}
}
}
}
/**
* @return SmtpOptions
*/
protected function getSmtpOptions()
{
$dataHelper = $this->dataHelper;
//set config
$options = new SmtpOptions([
'name' => $dataHelper->getConfigName(),
'host' => $dataHelper->getConfigSmtpHost(),
'port' => $dataHelper->getConfigSmtpPort(),
]);
$connectionConfig = [];
$auth = strtolower($dataHelper->getConfigAuth());
if ($auth != 'none') {
$options->setConnectionClass($auth);
$connectionConfig = [
'username' => $dataHelper->getConfigUsername(),
'password' => $dataHelper->getConfigPassword()
];
}
$ssl = $dataHelper->getConfigSsl();
if ($ssl != 'none') {
$connectionConfig['ssl'] = $ssl;
}
if (!empty($connectionConfig)) {
$options->setConnectionConfig($connectionConfig);
}
return $options;
}
/**
* @param $header
*/
public function updateMailHeader($header)
{
if ($header instanceof HeaderInterface) {
if (Mime::isPrintable($header->getFieldValue())) {
$header->setEncoding('ASCII');
} else {
$header->setEncoding('utf-8');
}
}
}
/**
* @return string
*/
public function getFromEmailAddress()
{
$result = $this->storeModel->getFrom();
return isset($result['email']) ? $result['email'] : '';
}
/**
* @return string
*/
public function getFromName()
{
$result = $this->storeModel->getFrom();
return isset($result['name']) ? $result['name'] : '';
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Model;
use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Mail\Template\Factory;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\Mail\TemplateInterface;
use Magento\Framework\Translate\Inline\StateInterface;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use MagePal\GmailSmtpApp\Helper\Data;
class Email
{
const XML_PATH_EMAIL_TEMPLATE_ZEND_TEST = 'system/gmailsmtpapp/zend_email_template';
const XML_PATH_EMAIL_TEMPLATE_MAGENTO_TEST = 'system/gmailsmtpapp/magento_email_template';
/**
* @var ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var Data
*/
protected $dataHelper;
/**
* @var Factory
*/
protected $templateFactory;
/**
* Store manager
*
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* @var array
*/
private $templateVars = [];
/**
* @var array
*/
private $templateOptions = [];
/**
* Template Model
*
* @var string
*/
private $templateModel;
/**
* @var StateInterface
*/
protected $inlineTranslation;
/**
* @var TransportBuilder
*/
protected $_transportBuilder;
/**
* @param Data $dataHelper
* @param Factory $templateFactory
* @param ScopeConfigInterface $scopeConfig
* @param StoreManagerInterface $storeManager
* @param StateInterface $inlineTranslation
* @param TransportBuilder $transportBuilder
*/
public function __construct(
Data $dataHelper,
Factory $templateFactory,
ScopeConfigInterface $scopeConfig,
StoreManagerInterface $storeManager,
StateInterface $inlineTranslation,
TransportBuilder $transportBuilder
) {
$this->dataHelper = $dataHelper;
$this->templateFactory = $templateFactory;
$this->scopeConfig = $scopeConfig;
$this->storeManager = $storeManager;
$this->inlineTranslation = $inlineTranslation;
$this->_transportBuilder = $transportBuilder;
}
/**
* @param Mixed $senderInfo
* @param Mixed $receiverInfo
* @return $this
* @throws NoSuchEntityException
*/
public function generateTemplate($senderInfo, $receiverInfo)
{
$templateId = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_MAGENTO_TEST);
$this->getTransportBuilder()
->setTemplateIdentifier($templateId)
->setTemplateOptions(
[
'area' => Area::AREA_ADMINHTML,
'store' => $this->getStore()->getId(),
]
)
->setTemplateVars($this->templateVars)
->setFrom($senderInfo)
->addTo($receiverInfo['email'], $receiverInfo['name']);
return $this;
}
/**
* @param $senderInfo
* @param $receiverInfo
* @throws MailException
* @throws NoSuchEntityException
* @throws LocalizedException
*/
public function send($senderInfo, $receiverInfo)
{
$this->inlineTranslation->suspend();
$this->generateTemplate($senderInfo, $receiverInfo);
$transport = $this->_transportBuilder->getTransport();
$result = $transport->sendMessage();
$this->inlineTranslation->resume();
return $result;
}
/**
* @return TemplateInterface
* @throws NoSuchEntityException
*/
protected function getTemplate()
{
$this->setTemplateOptions(
[
'area' => Area::AREA_ADMINHTML,
'store' => $this->getStore()->getId(),
]
);
$templateIdentifier = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_ZEND_TEST);
return $this->templateFactory->get($templateIdentifier, $this->templateModel)
->setVars($this->templateVars)
->setOptions($this->templateOptions);
}
/**
* @return mixed
* @throws NoSuchEntityException
*/
public function getEmailBody()
{
return $this->getTemplate()->processTemplate();
}
/**
* Return template id according to store
*
* @param $xmlPath
* @return mixed
* @throws NoSuchEntityException
*/
public function getTemplateId($xmlPath)
{
return $this->getConfigValue($xmlPath, $this->getStore()->getStoreId());
}
/**
* Return store configuration value of your template field that which id you set for template
*
* @param string $path
* @param int $storeId
* @return mixed
*/
protected function getConfigValue($path, $storeId)
{
return $this->scopeConfig->getValue(
$path,
ScopeInterface::SCOPE_STORE,
$storeId
);
}
/**
* Return store
*
* @return StoreInterface
* @throws NoSuchEntityException
*/
public function getStore()
{
return $this->storeManager->getStore();
}
/**
* @param mixed $templateVars
* @return Email
*/
public function setTemplateVars($templateVars)
{
$this->templateVars = (array) $templateVars;
return $this;
}
/**
* @param mixed $templateOptions
* @return Email
*/
public function setTemplateOptions($templateOptions)
{
$this->templateOptions = (array) $templateOptions;
return $this;
}
/**
* Set template model
*
* @param string $templateModel
* @return $this
*/
public function setTemplateModel($templateModel)
{
$this->templateModel = $templateModel;
return $this;
}
/**
* @return TransportBuilder
*/
public function getTransportBuilder()
{
return $this->_transportBuilder;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Model;
use Exception;
use InvalidArgumentException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\TransportInterface;
use Magento\Framework\Phrase;
use Zend_Mail;
use Zend_Mail_Transport_Sendmail;
class Transport extends Zend_Mail_Transport_Sendmail implements TransportInterface
{
/**
* @var MessageInterface
*/
protected $_message;
/**
* @param MessageInterface $message
* @param null $parameters
*/
public function __construct(MessageInterface $message, $parameters = null)
{
if (!$message instanceof Zend_Mail) {
throw new InvalidArgumentException('The message should be an instance of \Zend_Mail');
}
parent::__construct($parameters);
$this->_message = $message;
}
/**
* Send a mail using this transport
*
* @return void
* @throws MailException
*/
public function sendMessage()
{
try {
parent::send($this->_message);
} catch (Exception $e) {
throw new MailException(new Phrase($e->getMessage()), $e);
}
}
/**
* @return MessageInterface|Zend_Mail
*/
public function getMessage()
{
return $this->_message;
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Plugin\Mail\Template;
use Magento\Framework\Mail\Template\TransportBuilder;
use MagePal\GmailSmtpApp\Model\Store;
class TransportBuilderPlugin
{
/** @var Store */
protected $storeModel;
/**
* @param Store $storeModel
*/
public function __construct(
Store $storeModel
) {
$this->storeModel = $storeModel;
}
/**
* @param TransportBuilder $subject
* @param $templateOptions
* @return array
*/
public function beforeSetTemplateOptions(
TransportBuilder $subject,
$templateOptions
) {
if (array_key_exists('store', $templateOptions)) {
$this->storeModel->setStoreId($templateOptions['store']);
} else {
$this->storeModel->setStoreId(null);
}
return [$templateOptions];
}
}
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
namespace MagePal\GmailSmtpApp\Plugin\Mail;
use Closure;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\EmailMessageInterface;
use Magento\Framework\Mail\Message;
use Magento\Framework\Mail\TransportInterface;
use MagePal\GmailSmtpApp\Helper\Data;
use MagePal\GmailSmtpApp\Mail\SmtpFactory;
use MagePal\GmailSmtpApp\Mail\Smtp;
use MagePal\GmailSmtpApp\Model\Store;
use Zend_Mail_Exception;
class TransportPlugin
{
/**
* @var Data
*/
protected Data $dataHelper;
/**
* @var Store
*/
protected $storeModel;
/**
* @var Smtp
*/
private SmtpFactory $smtpFactory;
/**
* @param Data $dataHelper
* @param Store $storeModel
* @param SmtpFactory $smtpFactory
*/
public function __construct(
Data $dataHelper,
Store $storeModel,
SmtpFactory $smtpFactory
) {
$this->dataHelper = $dataHelper;
$this->storeModel = $storeModel;
$this->smtpFactory = $smtpFactory;
}
/**
* @param TransportInterface $subject
* @param Closure $proceed
* @throws MailException
* @throws Zend_Mail_Exception
*/
public function aroundSendMessage(
TransportInterface $subject,
Closure $proceed
) {
if ($this->dataHelper->isActive()) {
if (method_exists($subject, 'getStoreId')) {
$this->storeModel->setStoreId($subject->getStoreId());
}
$message = $subject->getMessage();
if ($message instanceof Message || $message instanceof EmailMessageInterface) {
$smtp = $this->smtpFactory->create(
['dataHelper' => $this->dataHelper, 'storeModel' => $this->storeModel]
);
$smtp->sendSmtpMessage($message);
} else {
$proceed();
}
} else {
$proceed();
}
}
}
<a href="https://www.magepal.com" title="Magento Extensions" ><img src="https://image.ibb.co/dHBkYH/Magepal_logo.png" width="100" align="right" title="Magento Custom Modules" /></a>
# Magento 2 SMTP Extension - Gmail, G Suite, Amazon SES, Office 365, Mailgun, SendGrid, Mandrill and other SMTP servers.
[![Total Downloads](https://poser.okvpn.org/magepal/magento2-gmailsmtpapp/downloads)](https://www.magepal.com/magento2/extensions/custom-smtp.html)
[![Latest Stable Version](https://poser.okvpn.org/magepal/magento2-gmailsmtpapp/v/stable)](https://www.magepal.com/magento2/extensions/custom-smtp.html)
[![GitHub stars](https://img.shields.io/github/stars/magepal/magento2-gmail-smtp-app.svg)](https://www.magepal.com/magento2/extensions/custom-smtp.html)
[![GitHub forks](https://img.shields.io/github/forks/magepal/magento2-gmail-smtp-app.svg)](https://www.magepal.com/magento2/extensions/custom-smtp.html)
##### For Magento 2.0.x, 2.1.x, 2.2.x, 2.3.x and 2.4.x
Configure Magento 2 to send all transactional emails using Google App, Gmail, Amazon Simple Email Service (SES), Microsoft Office365 or any other SMTP servers.
<a href="https://bit.ly/mp-gh-smpt"><img src="https://user-images.githubusercontent.com/1415141/90457464-9a4ce380-e0c9-11ea-8aea-61d4f7cb679b.jpg" alt="Magento SMTP Extension" /></a>
Sending transactional emails to customers is a vital part of running an e-commerce store. Our free custom Magento extension integrates with all major email service providers and third-party SMTP servers to reliably deliver messages to customers' inboxes.
#### What is SMTP - Simple Mail Transfer Protocol
SMTP or Simple Mail Transfer Protocol allows you to send emails from your Magento 2 store through a specific third-party mail SMTP server. For example, if you want to use your Gmail, Amazon, Microsoft or any other mail server account to send email from your Magento web store, all you need is to configure that mail server setting in our extension in Magento without having to do any complex server configuration.
<a href="https://bit.ly/mp-gh-smpt"><img src="https://image.ibb.co/ecWinc/Mage_Pal_Magento_2_SMTP_Extension.gif" alt="Magento SMTP Email Extension" /></a>
#### Why use a Custom SMTP Server with Magento
By default, most hosting companies' mail servers are configured to send email from unauthorized senders which prevents emails from reliable delivered to recipients. Therefore, most Magento store owners struggle to limit the number of transactional emails that end up in clients' junk mail. Take full control of your email sending settings in Magento 2 and reduce sending emails to your valuable customers' junk mail folder. Emails are delivered instantaneously to their intended recipients without delays or get trap in the spam folder.
Out of the box, Magento 2 doesn't provide the ability to specify your custom SMTP settings for outgoing emails using an external SMTP server. Using this extension bridges the gap and allows your Magento store to connect to your preferred email provider securely and easily.
All you need is either an (i) free Gmail account, (ii) paid Google Apps account or any other SMTP service (i.e Amazon Simple Email Service / Amazon SES, Microsoft Office365).
Learn more about our [custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Learn%20More) extension.
> ### NOTE - Gmail / Google Account
>To help keep your account secure, starting May 30, 2022, ​​Google will no longer support the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password.
>Please note this deadline does not apply to Google Workspace or Google Cloud Identity customers. The enforcement date for these customers will be announced on the Workspace blog at a later date.
>For more information, please continue reading.
>Special Note on Apple Device Sign-Ins. Users who have not recently signed into their Google Account using only username and password will be able to only make new sign in attempts using the Google account type starting from February 28, 2022. Existing users may continue to sign into their Google Account using their username and password until May 30, 2022.
### Benefits of using Gmail SMTP
Since Google's, Gmail and G Suite SMTP server does not use Port 25, you'll reduce the probability that an ISP might block your email or flag it as SPAM. Also, all your emails sent from Magento will be searchable and backed-up in your email account on Google's servers.
### Features
* Send emails through virtually any external SMTP server from your Magento store
* Easily configure Magento 2 SMTP settings from within Magento2 store admin
* Complete control of custom SMTP server settings: Hostname, Port, Username, Password, ...
* Self-test option, which lets you verify your email credentials are correct before saving
* Support Multi-store, configurable different email providers/accounts per store
* Support secure SMTP servers: TLS / SSL, Plain-text, username/password, CRAM-MD5 authentication
* Customize email headers: From / Reply-To / Return-Path
* Easily disable/enable our extension from admin
* Developer Friendly
* Integrate with any third-party SMTP server
Get more from your order confirmation emails by promoting other complementary products and services.
Learn more about our new <a href="https://www.magepal.com/enhanced-transactional-emails.html?utm_source=ete&utm_medium=GitHub%20Learn%20More" target="_blank">Enhanced Transactional Email</a> extension.
### Documentation
- [How to Install SMTP Magento 2 Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#installation)
- [How to setup Magento 2 SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#configuration)
- [How to debugging Magento 2 SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#debug)
### SMTP Service Providers
* Gmail
* Google App
* G Suite
* Amazon Simple Email Service (SES)
* Microsoft Office365
* Outlook
* SparkPost
* GoDaddy
* Mandrill
* MailGun
* SendGrid
* Elastic Email
* Hotmail
* AOL Mail
* Yahoo Mail
* AT&T
* Verizon
* Postmark
* O2 Mail
* Zoho
* Mailjet
* Mail.com
* Your Company SMTP Server
* and many other SMTP servers
### How to Install Magento SMTP Extension
##### Using Composer (recommended)
```sh
composer require magepal/magento2-gmailsmtpapp
```
Contribution
---
Want to contribute to this extension? The quickest way is to open a [pull request on GitHub](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
Support
---
If you encounter any problems or bugs, please open an issue on [GitHub](https://github.com/magepal/magento2-gmail-smtp-app/issues). For fast Premium Support visit our [Custom SMTP Extension](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Premium%20Support) product page for detail.
Need help setting up or want to customize this extension to meet your business needs? Please email support@magepal.com and if we like your idea we will add this feature for free or at a discounted rate.
Magento 2 Extensions
---
- [Custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html)
- [Catalog Hover Image for Magento](https://www.magepal.com/magento2/extensions/catalog-hover-image-for-magento.html)
- [Enhanced Success Page for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-success-page.html)
- [Enhanced Transactional Emails for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html)
- [Google Tag Manager](https://www.magepal.com/magento2/extensions/google-tag-manager.html)
- [Enhanced E-commerce](https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html)
- [Reindex](https://www.magepal.com/magento2/extensions/reindex.html)
- [Custom Shipping Method](https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html)
- [Preview Order Confirmation](https://www.magepal.com/magento2/extensions/preview-order-confirmation-page-for-magento-2.html)
- [Guest to Customer](https://www.magepal.com/magento2/extensions/guest-to-customer.html)
- [Admin Form Fields Manager](https://www.magepal.com/magento2/extensions/admin-form-fields-manager-for-magento-2.html)
- [Customer Dashboard Links Manager](https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html)
- [Lazy Loader](https://www.magepal.com/magento2/extensions/lazy-load.html)
- [Order Confirmation Page Miscellaneous Scripts](https://www.magepal.com/magento2/extensions/order-confirmation-miscellaneous-scripts-for-magento-2.html)
- [HTML Minifier for Magento2](https://www.magepal.com/magento2/extensions/html-minifier.html)
© MagePal LLC. | [www.magepal.com](https://www.magepal.com)
{
"name": "magepal/magento2-gmailsmtpapp",
"description":"Magento 2 SMTP Extension - Configure Magento 2 to send all transactional email using Gmail, G Suite, Amazon SES, Office360, Mailgun, SendGrid, Mandrill or any other SMTP servers",
"keywords": [
"magento 2",
"gmail smtp",
"google app",
"magento2 email",
"Amazon Simple Email Service",
"Amazon SES",
"magento2 smtp",
"magento2 email setup",
"send email magento2",
"g suite",
"how to configure magento email",
"how to setup email magento2"
],
"require": {
"php": "~7.4.0||~8.1.0",
"magento/module-backend": "102.0.*",
"magento/framework": "103.0.*",
"magepal/magento2-core":">1.1.0"
},
"type": "magento2-module",
"version": "2.9.0",
"license": [
"proprietary"
],
"homepage": "https://www.magepal.com/",
"support": {
"email": "support@magepal.com",
"issues": "https://github.com/magepal/magento2-gmail-smtp-app/issues/"
},
"authors": [
{
"name": "Renon Stewart",
"email": "renon@magepal.com",
"homepage": "https://www.magepal.com/",
"role": "Leader"
}
],
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"MagePal\\GmailSmtpApp\\": ""
}
}
}
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="admin">
<route id="magepalSmtp" frontName="magepalsmtp">
<module name="MagePal_GmailSmtpApp" before="Magento_Backend" />
</route>
</router>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="magepal" sortOrder="400" translate="label">
<label>MagePal</label>
</tab>
<section id="magepal_gmailsmtpapp" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="5" translate="label">
<label>SMTP Configuration</label>
<tab>magepal</tab>
<resource>MagePal_GmailSmtpApp::magepal_gmailsmtpapp</resource>
<group id="about_magepal" showInDefault="1" showInStore="1" showInWebsite="1" sortOrder="0" translate="label">
<attribute type="expanded">1</attribute>
<label>About MagePal SMTP</label>
<comment>
<![CDATA[
<div class="magepal-info">
Copyright © 2022 <a href="https://www.magepal.com/magento2/extensions.html?utm_source=smtp&utm_medium=admin" target="_blank">MagePal, LLC</a>
<a href="https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=admin#documentation" target="_blank">Documentation</a>
<a href="https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=admin#support">Support</a>
<a href="https://www.magepal.com/help/docs/smtp-magento?utm_source=smtp&utm_medium=admin#current_version" target="_blank">Latest Version</a>
<a href="https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=admin" target="_blank">Extension Detail</a>
<a href="https://www.magepal.com/magento2/extensions.html?utm_source=smtp&utm_medium=admin" target="_blank">More Extensions</a>
</div>
<div id="magepal-promo">
Get more from your order confirmation emails by promoting other complementary products!
Learn more about our new <a href="http://bit.ly/smtp-esp" target="_blank">Enhanced Transactional Email</a> extension.
</div>
<hr class="magepal-hr" />
]]>
</comment>
<field id="composer_version" translate="label" type="label" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Composer Version</label>
<frontend_model>MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Composer\Version</frontend_model>
</field>
<field id="link" translate="label" type="label" sortOrder="2" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Configuration</label>
<frontend_model>MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Field\Link</frontend_model>
</field>
</group>
</section>
<section id="system">
<group id="gmailsmtpapp" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SMTP Configuration and Settings (Gmail/Google/AWS/Office365 etc)</label>
<comment>
<![CDATA[
<div class="magepal-info">
Copyright © 2022 <a href="https://www.magepal.com/magento2/extensions.html?utm_source=smtp&utm_medium=admin" target="_blank">MagePal, LLC</a>
<a href="https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=admin#documentation" target="_blank">Documentation</a>
<a href="https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=admin#support">Support</a>
<a href="https://www.magepal.com/help/docs/smtp-magento?utm_source=smtp&utm_medium=admin#current_version" target="_blank">Latest Version</a>
<a href="https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=admin" target="_blank">Extension Detail</a>
<a href="https://www.magepal.com/magento2/extensions.html?utm_source=smtp&utm_medium=admin" target="_blank">More Extensions</a>
</div>
<div id="magepal-promo">
Get more from your order confirmation emails by promoting other complementary products!
Learn more about our new <a href="http://bit.ly/smtp-esp" target="_blank">Enhanced Transactional Email</a> extension.
</div>
<hr class="magepal-hr" />
]]>
</comment>
<field id="version" translate="label" type="label" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Version</label>
<frontend_model>MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Composer\Version</frontend_model>
</field>
<field id="active" translate="label" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enable</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Local client name (domain or IP)</label>
<comment>Default: localhost</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="auth" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Authentication method</label>
<source_model>Magento\Config\Model\Config\Source\Email\Smtpauth</source_model>
<comment>Default: login</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="ssl" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SSL type</label>
<source_model>MagePal\GmailSmtpApp\Model\Config\Source\Authtype</source_model>
<comment>Default: ssl</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="smtphost" translate="label" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SMTP Host</label>
<comment>The server name (eg smtp.gmail.com).</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="smtpport" translate="label" type="text" sortOrder="17" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SMTP Port</label>
<validate>validate-number</validate>
<frontend_class>validate-number</frontend_class>
<comment>Use 465 (ssl) or 587 (tls) if port 25 is throttled or blocked.</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="username" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Username</label>
<comment>Email Address or Account ID.</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="password" translate="label" type="obscure" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Password</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="set_reply_to" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Set Reply-to</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="set_from" translate="label" type="select" sortOrder="35" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Set From</label>
<source_model>Magento\Config\Model\Config\Source\Yesnocustom</source_model>
<comment>Use Return-Path email address for the From address rather than the Magento supplied value.</comment>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="custom_from_email" translate="label" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>From Email Address</label>
<validate>validate-email</validate>
<backend_model>Magento\Config\Model\Config\Backend\Email\Address</backend_model>
<comment>Use specify From Address instead of Magento supplied value.</comment>
<depends>
<field id="set_from">2</field>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="set_return_path" translate="label" type="select" sortOrder="45" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Set Return-Path</label>
<source_model>Magento\Config\Model\Config\Source\Yesnocustom</source_model>
<depends>
<field id="*/*/active">1</field>
</depends>
</field>
<field id="return_path_email" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Return-Path Email</label>
<validate>validate-email</validate>
<backend_model>Magento\Config\Model\Config\Backend\Email\Address</backend_model>
<depends>
<field id="set_return_path">2</field>
<field id="*/*/active">1</field>
</depends>
</field>
<group id="debug" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100">
<depends>
<field id="*/*/active">1</field>
</depends>
<label>Test Email Server Configuration Settings</label>
<attribute type="expanded">0</attribute>
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
<field id="email" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Email address</label>
<comment>Email address to send test to.</comment>
<frontend_class>validate-email</frontend_class>
</field>
<field id="from_email" translate="label comment" type="text" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="1">
<label>From Email Address</label>
<comment>Leave blank to use Username instead</comment>
<frontend_class>validate-email</frontend_class>
</field>
<field id="button" translate="label comment" type="button" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1">
<frontend_model>MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\ValidateConfigButton</frontend_model>
</field>
</group>
</group>
</section>
</system>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<system>
<gmailsmtpapp>
<active>0</active>
<name>localhost</name>
<ssl>ssl</ssl>
<auth>LOGIN</auth>
<smtphost>smtp.gmail.com</smtphost>
<smtpport>465</smtpport>
<set_return_path>1</set_return_path>
<set_reply_to>1</set_reply_to>
<set_from>0</set_from>
<password backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
<zend_email_template>magepal_smtp_zend_email_test</zend_email_template>
<magento_email_template>magepal_smtp_magento_email_test</magento_email_template>
</gmailsmtpapp>
</system>
</default>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<!-- For Magento < 2.2 -->
<preference for="Magento\Framework\Mail\Transport" type="MagePal\GmailSmtpApp\Model\Transport"/>
<type name="MagePal\GmailSmtpApp\Model\Transport">
<plugin sortOrder="100" name="magePalGmailSmtpAppTransport" type="MagePal\GmailSmtpApp\Plugin\Mail\TransportPlugin"/>
</type>
<!-- For Magento Eq 2.2 -->
<type name="Magento\Framework\Mail\TransportInterface">
<plugin sortOrder="100" name="magePalGmailSmtpAppTransportInterface" type="MagePal\GmailSmtpApp\Plugin\Mail\TransportPlugin"/>
</type>
<type name="Magento\Framework\Mail\Template\TransportBuilder">
<plugin sortOrder="1" name="magePalGmailSmtpAppTransportBuilder"
type="MagePal\GmailSmtpApp\Plugin\Mail\Template\TransportBuilderPlugin"/>
</type>
<type name="Magento\Framework\Mail\Template\TransportBuilderByStore">
<plugin sortOrder="1" name="magePalGmailSmtpAppTransportBuilderByStore"
type="MagePal\GmailSmtpApp\Plugin\Mail\Template\TransportBuilderByStorePlugin"/>
</type>
<type name="Magento\Config\Model\Config\TypePool">
<arguments>
<argument name="environment" xsi:type="array">
<item name="system/gmailsmtpapp/active" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/ssl" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/auth" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/smtphost" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/smtpport" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/username" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/password" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/set_reply_to" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/set_from" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/custom_from_email" xsi:type="string">1</item>
<item name="system/gmailsmtpapp/return_path_email" xsi:type="string">1</item>
</argument>
</arguments>
</type>
</config>
<?xml version="1.0"?>
<!--
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="MagePal_GmailSmtpApp" setup_version="1.5.1">
<sequence>
<module name="Magento_Email"/>
<module name="Magento_Backend" />
<module name="Magento_Config" />
<module name="Magento_Store" />
<module name="MagePal_Core"/>
</sequence>
</module>
</config>
"SMTP Configuration","SMTP Configuration"
"Module Version","Module Version"
"Composer Version","Composer Version"
"Configuration","Configuration"
"Version","Version"
"Enable","Enable"
"Local client name (domain or IP)","Local client name (domain or IP)"
"Default: localhost","Default: localhost"
"Authentication method","Authentication method"
"Use ""login"" for Gmail or Google Apps.","Use ""login"" for Gmail or Google Apps."
"SSL type","SSL type"
"Use ""ssl"" for Gmail or Google Apps.","Use ""ssl"" for Gmail or Google Apps."
"SMTP Host","SMTP Host"
"SMTP Port","SMTP Port"
"Use 465 (ssl) or 587 (tls) if port 25 is throttled or blocked.","Use 465 (ssl) or 587 (tls) if port 25 is throttled or blocked."
"Username","Username"
"Email Address or Account ID.","Email Address or Account ID."
"Password","Password"
"Set Reply-to","Set Reply-to"
"Set From","Set From"
"Use Return-Path email address for the From address rather than the Magento supplied value.","Use Return-Path email address for the From address rather than the Magento supplied value."
"Set Return-Path","Set Return-Path"
"Return-Path Email","Return-Path Email"
"Test Email Server Configuration Settings","Test Email Server Configuration Settings"
"Email address","Email address"
"Email address to send test to.","Email address to send test to."
"From Email Address","From Email Address"
"Leave blank to use Username instead","Leave blank to use Username instead"
"Send Test Email","Send Test Email"
"Stores > Configuration > Advanced > System","Stores > Configuration > Advanced > System"
"None","None"
"SSL (Gmail / Google Apps)","SSL (Gmail / Google Apps)"
"TLS (Gmail / Google Apps)","TLS (Gmail / Google Apps)"
"Please check your email","Please check your email"
"and flush your Magento cache","and flush your Magento cache"
"Please enter a valid username/password","Please enter a valid username/password"
"""Disable Email Communications"" is set is ""Yes"", please set to ""NO"" in ""Mail Sending Setting""","""Disable Email Communications"" is set is ""Yes"", please set to ""NO"" in ""Mail Sending Setting"""
"SMTP module is not enabled","SMTP module is not enabled"
"Stores > Configuration > Advanced > System > SMTP Configuration and Settings","Stores > Configuration > Advanced > System > SMTP Configuration and Settings"
"From Email Address","From Email Address"
"Use specify From Address instead of Magento supplied value.","Use specify From Address instead of Magento supplied value."
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'MagePal_GmailSmtpApp',
__DIR__
);
<!--@subject Hello from MagePal SMTP (2 of 2) @-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 9]><xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width">
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->
<title></title>
<!--[if !mso]><!-- -->
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" type="text/css">
<!--<![endif]-->
<style type="text/css" id="media-query">
body {
margin: 0;
padding: 0; }
table, tr, td {
vertical-align: top;
border-collapse: collapse; }
.ie-browser table, .mso-container table {
table-layout: fixed; }
* {
line-height: inherit; }
a[x-apple-data-detectors=true] {
color: inherit !important;
text-decoration: none !important; }
[owa] .img-container div, [owa] .img-container button {
display: block !important; }
[owa] .fullwidth button {
width: 100% !important; }
[owa] .block-grid .col {
display: table-cell;
float: none !important;
vertical-align: top; }
.ie-browser .num12, .ie-browser .block-grid, [owa] .num12, [owa] .block-grid {
width: 650px !important; }
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
line-height: 100%; }
.ie-browser .mixed-two-up .num4, [owa] .mixed-two-up .num4 {
width: 176px !important; }
.ie-browser .mixed-two-up .num8, [owa] .mixed-two-up .num8 {
width: 352px !important; }
.ie-browser .block-grid.two-up .col, [owa] .block-grid.two-up .col {
width: 265px !important; }
.ie-browser .block-grid.three-up .col, [owa] .block-grid.three-up .col {
width: 176px !important; }
.ie-browser .block-grid.four-up .col, [owa] .block-grid.four-up .col {
width: 132px !important; }
.ie-browser .block-grid.five-up .col, [owa] .block-grid.five-up .col {
width: 106px !important; }
.ie-browser .block-grid.six-up .col, [owa] .block-grid.six-up .col {
width: 88px !important; }
.ie-browser .block-grid.seven-up .col, [owa] .block-grid.seven-up .col {
width: 75px !important; }
.ie-browser .block-grid.eight-up .col, [owa] .block-grid.eight-up .col {
width: 66px !important; }
.ie-browser .block-grid.nine-up .col, [owa] .block-grid.nine-up .col {
width: 58px !important; }
.ie-browser .block-grid.ten-up .col, [owa] .block-grid.ten-up .col {
width: 53px !important; }
.ie-browser .block-grid.eleven-up .col, [owa] .block-grid.eleven-up .col {
width: 48px !important; }
.ie-browser .block-grid.twelve-up .col, [owa] .block-grid.twelve-up .col {
width: 44px !important; }
@media only screen and (min-width: 550px) {
.block-grid {
width: 650px !important; }
.block-grid .col {
vertical-align: top; }
.block-grid .col.num12 {
width: 650px !important; }
.block-grid.mixed-two-up .col.num4 {
width: 176px !important; }
.block-grid.mixed-two-up .col.num8 {
width: 352px !important; }
.block-grid.two-up .col {
width: 265px !important; }
.block-grid.three-up .col {
width: 176px !important; }
.block-grid.four-up .col {
width: 132px !important; }
.block-grid.five-up .col {
width: 106px !important; }
.block-grid.six-up .col {
width: 88px !important; }
.block-grid.seven-up .col {
width: 75px !important; }
.block-grid.eight-up .col {
width: 66px !important; }
.block-grid.nine-up .col {
width: 58px !important; }
.block-grid.ten-up .col {
width: 53px !important; }
.block-grid.eleven-up .col {
width: 48px !important; }
.block-grid.twelve-up .col {
width: 44px !important; } }
@media (max-width: 550px) {
.block-grid, .col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important; }
.block-grid {
width: calc(100% - 40px) !important; }
.col {
width: 100% !important; }
.col > div {
margin: 0 auto; }
img.fullwidth, img.fullwidthOnMobile {
max-width: 100% !important; }
.no-stack .col {
min-width: 0 !important;
display: table-cell !important; }
.no-stack.two-up .col {
width: 50% !important; }
.no-stack.mixed-two-up .col.num4 {
width: 33% !important; }
.no-stack.mixed-two-up .col.num8 {
width: 66% !important; }
.no-stack.three-up .col.num4 {
width: 33% !important; }
.no-stack.four-up .col.num3 {
width: 25% !important; } }
</style>
</head>
<body class="clean-body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #FFFFFF">
<style type="text/css" id="media-query-bodytag">
@media (max-width: 520px) {
.block-grid {
min-width: 320px!important;
max-width: 100%!important;
width: 100%!important;
display: block!important;
}
.col {
min-width: 320px!important;
max-width: 100%!important;
width: 100%!important;
display: block!important;
}
.col > div {
margin: 0 auto;
}
img.fullwidth {
max-width: 100%!important;
}
img.fullwidthOnMobile {
max-width: 100%!important;
}
.no-stack .col {
min-width: 0!important;
display: table-cell!important;
}
.no-stack.two-up .col {
width: 50%!important;
}
.no-stack.mixed-two-up .col.num4 {
width: 33%!important;
}
.no-stack.mixed-two-up .col.num8 {
width: 66%!important;
}
.no-stack.three-up .col.num4 {
width: 33%!important
}
.no-stack.four-up .col.num3 {
width: 25%!important
}
}
</style>
<!--[if IE]><div class="ie-browser"><![endif]-->
<!--[if mso]><div class="mso-container"><![endif]-->
<table class="nl-container" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #FFFFFF;width: 100%" cellpadding="0" cellspacing="0">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color: #FFFFFF;"><![endif]-->
<div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #F4F5F6;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#F4F5F6;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#F4F5F6;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="650" style=" width:650px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
&#160;
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #FFFFFF;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#FFFFFF;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#FFFFFF;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="528" style=" width:528px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 1px solid #EFE4E4; border-left: 1px solid #EFE4E4; border-bottom: 1px solid #EFE4E4; border-right: 1px solid #EFE4E4;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 1px solid #EFE4E4; border-left: 1px solid #EFE4E4; border-bottom: 1px solid #EFE4E4; border-right: 1px solid #EFE4E4; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
<div align="center" class="img-container center fixedwidth" style="padding-right: 0px; padding-left: 0px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px;" align="center"><![endif]-->
<div style="line-height:25px;font-size:1px">&#160;</div> <a href="https://bit.ly/MP4SMTP2" target="_blank">
<img class="center fixedwidth" align="center" border="0" src="https://user-images.githubusercontent.com/1415141/35169222-c3a36a5a-fd29-11e7-93a6-dd0cc18b9366.png" alt="MagePal" title="MagePal" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;width: 100%;max-width: 105.6px" width="105.6">
</a>
<div style="line-height:60px;font-size:1px">&#160;</div><!--[if mso]></td></tr></table><![endif]-->
</div>
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 30px; padding-bottom: 15px;"><![endif]-->
<div style="font-family:'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;line-height:120%;color:#555555; padding-right: 30px; padding-left: 30px; padding-top: 30px; padding-bottom: 15px;">
<div style="font-family:Montserrat, 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;font-size:12px;line-height:14px;color:#555555;text-align:left;"><p style="margin: 0;font-size: 12px;line-height: 14px;text-align: center"><span style="font-size: 18px; line-height: 21px;color:green;">Congratulations! Your SMTP is setup correctly</span></p></div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!-- [if mso]></td></tr></table><![endif]--><!-- [if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 20px; padding-bottom: 0px; font-family: 'Trebuchet MS', Tahoma, sans-serif"><![endif]-->
<div style="color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif; line-height: 120%; padding: 20px 30px 0px 30px;">
<div style="font-size: 12px; line-height: 14px; color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;">
<p style="font-size: 12px; line-height: 14px; text-align: center; margin: 0;"><span style="font-size: 20px; line-height: 24px;"><strong>Get more MagePal extensions today!</strong></span></p>
</div>
</div>
<!-- [if mso]></td></tr></table><![endif]--><!-- [if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 0px; padding-bottom: 10px; font-family: 'Trebuchet MS', Tahoma, sans-serif"><![endif]-->
<div style="color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif; line-height: 120%; padding: 10px 30px 10px 30px;">
<div style="font-size: 12px; line-height: 14px; color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;">
<ul>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/google-tag-manager.html?utm_source=gtm%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtm" target="_blank" rel="noopener">Google Tag Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html?utm_source=enhanced%20ecommerce%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=eegtm" target="_blank" rel="noopener">Enhanced Ecommerce for Google Tag Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/google-analytics-4-for-google-tag-manager.html?utm_source=ga4%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtmga4" target="_blank" rel="noopener">Google Analytics 4</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/datalayer-for-google-tag-manager.html?utm_source=datalayer%20gtm%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtmdl" target="_blank" rel="noopener">Google Tag Manager DataLayer</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/reindex.html?utm_source=reindex%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=reindex" target="_blank" rel="noopener">Admin Reindex</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-success-page-magento-2.html?utm_source=enhanced%20success%20page%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=esp" target="_blank" rel="noopener">Enhanced Success Page Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html?utm_source=custom%20shipping%20rates%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=shipping%20rates" target="_blank" rel="noopener">Custom Shipping Rate</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/lazy-load.html?utm_source=lazy%20load%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=lazyload" target="_blank" rel="noopener">Lazy Load Images</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/custom-maintenance-page-for-magento-2.html?utm_source=custom%20maintancece&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=maintenance%20page" target="_blank" rel="noopener">Beautiful Custom Maintenance Page Design</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/order-shipment-tracking-for-magento-2.html?utm_source=order%20tracking%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=order%20tracking" target="_blank" rel="noopener">Order Shipment Tracking Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/address-autocomplete-for-magento-2.html?utm_source=autocomplete%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=autocomplete" target="_blank" rel="noopener">Checkout Address Autocomplete</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/payment-restrictions-for-magento-2.html?utm_source=paymentrestrictions%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=paymentrestrictions" target="_blank" rel="noopener">Magento Payment Restrictions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html?utm_source=customer%20dashboard%20links%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=dashboard%20links" target="_blank" rel="noopener">Customer Dashboard Links Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html?utm_source=enhanced%20transactional%20emails%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=ete" target="_blank" rel="noopener">Enhanced Transactional Email Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/html-minifier.html?utm_source=html%20minifier%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=minifier" target="_blank" rel="noopener">HTML Minifier</a></span></li>
</ul>
</div>
</div>
<!-- [if mso]></td></tr></table><![endif]-->
<div align="center" class="button-container center" style="padding-right: 10px; padding-left: 10px; padding-top:10px; padding-bottom:10px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing: 0; border-collapse: collapse; mso-table-lspace:0pt; mso-table-rspace:0pt;"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top:10px; padding-bottom:10px;" align="center"><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://bit.ly/MP4SMTP" style="height:31pt; v-text-anchor:middle; width:153pt;" arcsize="24%" strokecolor="#F06D46" fillcolor="#F06D46"><w:anchorlock/><v:textbox inset="0,0,0,0"><center style="color:#ffffff; font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size:16px;"><![endif]-->
<a href="https://bit.ly/MP4SMTP" target="_blank" style="display: block;text-decoration: none;-webkit-text-size-adjust: none;text-align: center;color: #ffffff; background-color: #F06D46; border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px; max-width: 204px; width: 164px;width: auto; border-top: 0px solid transparent; border-right: 0px solid transparent; border-bottom: 0px solid transparent; border-left: 0px solid transparent; padding-top: 5px; padding-right: 20px; padding-bottom: 5px; padding-left: 20px; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;mso-border-alt: none">
<span style="font-size:16px;line-height:32px;">Get More from Magento with MagePal 40+ Extensions.</span>
</a>
<!--[if mso]></center></v:textbox></v:roundrect></td></tr></table><![endif]-->
</div>
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 20px; padding-bottom: 30px;"><![endif]-->
<div style="font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;color:#989898; padding-right: 10px; padding-left: 10px; padding-top: 20px; padding-bottom: 30px;">
<div style="font-size:12px;line-height:14px;color:#989898;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;text-align:left;"><p style="margin: 0;font-size: 14px;line-height: 17px;text-align: center"><span style="color: rgb(152, 152, 152); font-size: 14px; line-height: 16px;"><em>Help us by "starring" our extensions on <a style="color:#0068A5;text-decoration: underline; color: rgb(152, 152, 152);" title="Github" href="https://bit.ly/mp4github" target="_blank" rel="noopener">github.com/MagePal</a></em></span></p></div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<div align="center" style="padding-right: 10px; padding-left: 10px; padding-bottom: 10px;">
<div style="line-height:20px;font-size:13px">Stay up to date by following us on</div>
<div style="display: table; max-width:225px;">
<!--[if (mso)|(IE)]><table width="205" cellpadding="0" cellspacing="0" border="0"><tr><td style="border-collapse:collapse; padding-right: 10px; padding-left: 10px; padding-bottom: 10px;" align="center"><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse; mso-table-lspace: 0pt;mso-table-rspace: 0pt; width:205px;"><tr><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP2" title="Facebook" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178563-f0e5abfc-fd58-11e7-8235-18e43b23d453.png" alt="Facebook" title="Facebook" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP2" title="Twitter" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178568-ff2b3ef2-fd58-11e7-9e72-9cf73a2c8b45.png" alt="Twitter" title="Twitter" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP2" title="LinkedIn" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178573-0ab38180-fd59-11e7-863b-befdc0bd062b.png" alt="LinkedIn" title="LinkedIn" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="mailto:support@magepal.com" title="E-Mail" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178581-2a73d95c-fd59-11e7-85b6-c95cd017afad.png" alt="E-Mail" title="E-Mail" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 0;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 0">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP2" title="Web Site" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178590-3e8e6fe2-fd59-11e7-8b8b-87e86a63cc2f.png" alt="Web Site" title="Web Site" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #F4F5F6;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#F4F5F6;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#F4F5F6;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="650" style=" width:650px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
&#160;
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <!--[if (mso)|(IE)]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]></div><![endif]-->
<img src="http://www.google-analytics.com/collect?v=1&tid=UA-112738735-2&cid={{var hash}}&t=event&ec=email&ea=open&el={{var hash}}&cs=smtp+tester&cm=email+magento&cn=SMTP&cm1=1" width="1" height="1" />
</body></html>
<!--@subject Hello from MagePal SMTP (2 of 2) @-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<!--[if gte mso 9]><xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml><![endif]-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width">
<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->
<title></title>
<!--[if !mso]> -->
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet" type="text/css">
<!--<![endif]-->
<style type="text/css" id="media-query">
body {
margin: 0;
padding: 0; }
table, tr, td {
vertical-align: top;
border-collapse: collapse; }
.ie-browser table, .mso-container table {
table-layout: fixed; }
* {
line-height: inherit; }
a[x-apple-data-detectors=true] {
color: inherit !important;
text-decoration: none !important; }
[owa] .img-container div, [owa] .img-container button {
display: block !important; }
[owa] .fullwidth button {
width: 100% !important; }
[owa] .block-grid .col {
display: table-cell;
float: none !important;
vertical-align: top; }
.ie-browser .num12, .ie-browser .block-grid, [owa] .num12, [owa] .block-grid {
width: 650px !important; }
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {
line-height: 100%; }
.ie-browser .mixed-two-up .num4, [owa] .mixed-two-up .num4 {
width: 176px !important; }
.ie-browser .mixed-two-up .num8, [owa] .mixed-two-up .num8 {
width: 352px !important; }
.ie-browser .block-grid.two-up .col, [owa] .block-grid.two-up .col {
width: 265px !important; }
.ie-browser .block-grid.three-up .col, [owa] .block-grid.three-up .col {
width: 176px !important; }
.ie-browser .block-grid.four-up .col, [owa] .block-grid.four-up .col {
width: 132px !important; }
.ie-browser .block-grid.five-up .col, [owa] .block-grid.five-up .col {
width: 106px !important; }
.ie-browser .block-grid.six-up .col, [owa] .block-grid.six-up .col {
width: 88px !important; }
.ie-browser .block-grid.seven-up .col, [owa] .block-grid.seven-up .col {
width: 75px !important; }
.ie-browser .block-grid.eight-up .col, [owa] .block-grid.eight-up .col {
width: 66px !important; }
.ie-browser .block-grid.nine-up .col, [owa] .block-grid.nine-up .col {
width: 58px !important; }
.ie-browser .block-grid.ten-up .col, [owa] .block-grid.ten-up .col {
width: 53px !important; }
.ie-browser .block-grid.eleven-up .col, [owa] .block-grid.eleven-up .col {
width: 48px !important; }
.ie-browser .block-grid.twelve-up .col, [owa] .block-grid.twelve-up .col {
width: 44px !important; }
@media only screen and (min-width: 550px) {
.block-grid {
width: 650px !important; }
.block-grid .col {
vertical-align: top; }
.block-grid .col.num12 {
width: 650px !important; }
.block-grid.mixed-two-up .col.num4 {
width: 176px !important; }
.block-grid.mixed-two-up .col.num8 {
width: 352px !important; }
.block-grid.two-up .col {
width: 265px !important; }
.block-grid.three-up .col {
width: 176px !important; }
.block-grid.four-up .col {
width: 132px !important; }
.block-grid.five-up .col {
width: 106px !important; }
.block-grid.six-up .col {
width: 88px !important; }
.block-grid.seven-up .col {
width: 75px !important; }
.block-grid.eight-up .col {
width: 66px !important; }
.block-grid.nine-up .col {
width: 58px !important; }
.block-grid.ten-up .col {
width: 53px !important; }
.block-grid.eleven-up .col {
width: 48px !important; }
.block-grid.twelve-up .col {
width: 44px !important; } }
@media (max-width: 550px) {
.block-grid, .col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important; }
.block-grid {
width: calc(100% - 40px) !important; }
.col {
width: 100% !important; }
.col > div {
margin: 0 auto; }
img.fullwidth, img.fullwidthOnMobile {
max-width: 100% !important; }
.no-stack .col {
min-width: 0 !important;
display: table-cell !important; }
.no-stack.two-up .col {
width: 50% !important; }
.no-stack.mixed-two-up .col.num4 {
width: 33% !important; }
.no-stack.mixed-two-up .col.num8 {
width: 66% !important; }
.no-stack.three-up .col.num4 {
width: 33% !important; }
.no-stack.four-up .col.num3 {
width: 25% !important; } }
</style>
</head>
<body class="clean-body" style="margin: 0;padding: 0;-webkit-text-size-adjust: 100%;background-color: #FFFFFF">
<style type="text/css" id="media-query-bodytag">
@media (max-width: 520px) {
.block-grid {
min-width: 320px!important;
max-width: 100%!important;
width: 100%!important;
display: block!important;
}
.col {
min-width: 320px!important;
max-width: 100%!important;
width: 100%!important;
display: block!important;
}
.col > div {
margin: 0 auto;
}
img.fullwidth {
max-width: 100%!important;
}
img.fullwidthOnMobile {
max-width: 100%!important;
}
.no-stack .col {
min-width: 0!important;
display: table-cell!important;
}
.no-stack.two-up .col {
width: 50%!important;
}
.no-stack.mixed-two-up .col.num4 {
width: 33%!important;
}
.no-stack.mixed-two-up .col.num8 {
width: 66%!important;
}
.no-stack.three-up .col.num4 {
width: 33%!important
}
.no-stack.four-up .col.num3 {
width: 25%!important
}
}
</style>
<!--[if IE]><div class="ie-browser"><![endif]-->
<!--[if mso]><div class="mso-container"><![endif]-->
<table class="nl-container" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;min-width: 320px;Margin: 0 auto;background-color: #FFFFFF;width: 100%" cellpadding="0" cellspacing="0">
<tbody>
<tr style="vertical-align: top">
<td style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color: #FFFFFF;"><![endif]-->
<div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #F4F5F6;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#F4F5F6;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#F4F5F6;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="650" style=" width:650px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
&#160;
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #FFFFFF;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#FFFFFF;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#FFFFFF;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="528" style=" width:528px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 1px solid #EFE4E4; border-left: 1px solid #EFE4E4; border-bottom: 1px solid #EFE4E4; border-right: 1px solid #EFE4E4;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 1px solid #EFE4E4; border-left: 1px solid #EFE4E4; border-bottom: 1px solid #EFE4E4; border-right: 1px solid #EFE4E4; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
<div align="center" class="img-container center fixedwidth" style="padding-right: 0px; padding-left: 0px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px;" align="center"><![endif]-->
<div style="line-height:25px;font-size:1px">&#160;</div> <a href="https://bit.ly/MP4SMTP" target="_blank">
<img class="center fixedwidth" align="center" border="0" src="https://user-images.githubusercontent.com/1415141/35169222-c3a36a5a-fd29-11e7-93a6-dd0cc18b9366.png" alt="MagePal" title="MagePal" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;width: 100%;max-width: 105.6px" width="105.6">
</a>
<div style="line-height:60px;font-size:1px">&#160;</div><!--[if mso]></td></tr></table><![endif]-->
</div>
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 0px; padding-bottom: 15px;"><![endif]-->
<div style="font-family:'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;line-height:120%;color:#555555; padding-right: 30px; padding-left: 30px; padding-top: 30px; padding-bottom: 15px;">
<div style="font-family:Montserrat, 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;font-size:12px;line-height:14px;color:#555555;text-align:left;"><p style="margin: 0;font-size: 12px;line-height: 14px;text-align: center"><span style="font-size: 18px; line-height: 21px;color: green;font-weight: bold;">Congratulations! Your server is configured correctly.</span></p></div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!-- [if mso]></td></tr></table><![endif]--><!-- [if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 20px; padding-bottom: 0px; font-family: 'Trebuchet MS', Tahoma, sans-serif"><![endif]-->
<div style="color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif; line-height: 120%; padding: 20px 30px 0px 30px;">
<div style="font-size: 12px; line-height: 14px; color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;">
<p style="font-size: 12px; line-height: 14px; text-align: center; margin: 0;"><span style="font-size: 20px; line-height: 24px;"><strong>Get more MagePal extensions today!</strong></span></p>
</div>
</div>
<!-- [if mso]></td></tr></table><![endif]--><!-- [if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 30px; padding-left: 30px; padding-top: 0px; padding-bottom: 10px; font-family: 'Trebuchet MS', Tahoma, sans-serif"><![endif]-->
<div style="color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif; line-height: 120%; padding: 10px 30px 10px 30px;">
<div style="font-size: 12px; line-height: 14px; color: #555555; font-family: 'Montserrat', 'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif;">
<ul>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/google-tag-manager.html?utm_source=gtm%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtm" target="_blank" rel="noopener">Google Tag Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html?utm_source=enhanced%20ecommerce%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=eegtm" target="_blank" rel="noopener">Enhanced Ecommerce for Google Tag Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/google-analytics-4-for-google-tag-manager.html?utm_source=ga4%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtmga4" target="_blank" rel="noopener">Google Analytics 4</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/datalayer-for-google-tag-manager.html?utm_source=datalayer%20gtm%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=gtmdl" target="_blank" rel="noopener">Google Tag Manager DataLayer</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/reindex.html?utm_source=reindex%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=reindex" target="_blank" rel="noopener">Admin Reindex</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-success-page-magento-2.html?utm_source=enhanced%20success%20page%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=esp" target="_blank" rel="noopener">Enhanced Success Page Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html?utm_source=custom%20shipping%20rates%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=shipping%20rates" target="_blank" rel="noopener">Custom Shipping Rate</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/lazy-load.html?utm_source=lazy%20load%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=lazyload" target="_blank" rel="noopener">Lazy Load Images</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/custom-maintenance-page-for-magento-2.html?utm_source=custom%20maintancece&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=maintenance%20page" target="_blank" rel="noopener">Beautiful Custom Maintenance Page Design</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/order-shipment-tracking-for-magento-2.html?utm_source=order%20tracking%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=order%20tracking" target="_blank" rel="noopener">Order Shipment Tracking Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/address-autocomplete-for-magento-2.html?utm_source=autocomplete%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=autocomplete" target="_blank" rel="noopener">Checkout Address Autocomplete</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/payment-restrictions-for-magento-2.html?utm_source=paymentrestrictions%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=paymentrestrictions" target="_blank" rel="noopener">Magento Payment Restrictions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html?utm_source=customer%20dashboard%20links%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=dashboard%20links" target="_blank" rel="noopener">Customer Dashboard Links Manager</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html?utm_source=enhanced%20transactional%20emails%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=ete" target="_blank" rel="noopener">Enhanced Transactional Email Item Promotions</a></span></li>
<li style="font-size: 12px; padding: 3px 0; line-height: 14px;"><span style="line-height: 19px; font-size: 16px;"><a style="text-decoration: underline; color: #555555;" href="https://www.magepal.com/magento2/extensions/html-minifier.html?utm_source=html%20minifier%20promo&amp;utm_medium=email&amp;utm_campaign=top%20extensions&amp;utm_content=minifier" target="_blank" rel="noopener">HTML Minifier</a></span></li>
</ul>
</div>
</div>
<!-- [if mso]></td></tr></table><![endif]-->
<div align="center" class="button-container center" style="padding-right: 10px; padding-left: 10px; padding-top:10px; padding-bottom:10px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing: 0; border-collapse: collapse; mso-table-lspace:0pt; mso-table-rspace:0pt;"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top:10px; padding-bottom:10px;" align="center"><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="https://bit.ly/MP4SMTP" style="height:31pt; v-text-anchor:middle; width:153pt;" arcsize="24%" strokecolor="#F06D46" fillcolor="#F06D46"><w:anchorlock/><v:textbox inset="0,0,0,0"><center style="color:#ffffff; font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size:16px;"><![endif]-->
<a href="https://bit.ly/MP4SMTP" target="_blank" style="display: block;text-decoration: none;-webkit-text-size-adjust: none;text-align: center;color: #ffffff; background-color: #F06D46; border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px; max-width: 204px; width: 164px;width: auto; border-top: 0px solid transparent; border-right: 0px solid transparent; border-bottom: 0px solid transparent; border-left: 0px solid transparent; padding-top: 5px; padding-right: 20px; padding-bottom: 5px; padding-left: 20px; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;mso-border-alt: none">
<span style="font-size:16px;line-height:32px;">Get More from Magento with MagePal 40+ Extensions.</span>
</a>
<!--[if mso]></center></v:textbox></v:roundrect></td></tr></table><![endif]-->
</div>
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 20px; padding-bottom: 30px;"><![endif]-->
<div style="font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;color:#989898; padding-right: 10px; padding-left: 10px; padding-top: 20px; padding-bottom: 30px;">
<div style="font-size:12px;line-height:14px;color:#989898;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;text-align:left;"><p style="margin: 0;font-size: 14px;line-height: 17px;text-align: center"><span style="color: rgb(152, 152, 152); font-size: 14px; line-height: 16px;"><em>Help us by "starring" our extensions on <a style="color:#0068A5;text-decoration: underline; color: rgb(152, 152, 152);" title="Github" href="https://bit.ly/mp4github" target="_blank" rel="noopener">github.com/MagePal</a></em></span></p></div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<div align="center" style="padding-right: 10px; padding-left: 10px; padding-bottom: 10px;">
<div style="line-height:20px;font-size:13px">Stay up to date by following us on:</div>
<div style="display: table; max-width:225px;">
<!--[if (mso)|(IE)]><table width="205" cellpadding="0" cellspacing="0" border="0"><tr><td style="border-collapse:collapse; padding-right: 10px; padding-left: 10px; padding-bottom: 10px;" align="center"><table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse:collapse; mso-table-lspace: 0pt;mso-table-rspace: 0pt; width:205px;"><tr><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP" title="Facebook" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178563-f0e5abfc-fd58-11e7-8235-18e43b23d453.png" alt="Facebook" title="Facebook" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP" title="Twitter" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178568-ff2b3ef2-fd58-11e7-9e72-9cf73a2c8b45.png" alt="Twitter" title="Twitter" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP" title="LinkedIn" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178573-0ab38180-fd59-11e7-863b-befdc0bd062b.png" alt="LinkedIn" title="LinkedIn" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 5px;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 5px">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="mailto:support@magepal.com" title="E-Mail" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178581-2a73d95c-fd59-11e7-85b6-c95cd017afad.png" alt="E-Mail" title="E-Mail" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td><td width="32" style="width:32px; padding-right: 0;" valign="top"><![endif]-->
<table align="left" border="0" cellspacing="0" cellpadding="0" width="32" height="32" style="border-collapse: collapse;table-layout: fixed;border-spacing: 0;mso-table-lspace: 0pt;mso-table-rspace: 0pt;vertical-align: top;Margin-right: 0">
<tbody><tr style="vertical-align: top"><td align="left" valign="middle" style="word-break: break-word;border-collapse: collapse !important;vertical-align: top">
<a href="https://bit.ly/MP4SMTP" title="Web Site" target="_blank">
<img src="https://user-images.githubusercontent.com/1415141/35178590-3e8e6fe2-fd59-11e7-8b8b-87e86a63cc2f.png" alt="Web Site" title="Web Site" width="32" style="outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;clear: both;display: block !important;border: none;height: auto;float: none;max-width: 32px !important">
</a>
<div style="line-height:5px;font-size:1px">&#160;</div>
</td></tr>
</tbody></table>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <div style="background-color:#F4F5F6;">
<div style="Margin: 0 auto;min-width: 320px;max-width: 650px;overflow-wrap: break-word;word-wrap: break-word;word-break: break-word;background-color: #F4F5F6;" class="block-grid ">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:#F4F5F6;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="background-color:#F4F5F6;" align="center"><table cellpadding="0" cellspacing="0" border="0" style="width: 650px;"><tr class="layout-full-width" style="background-color:#F4F5F6;"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="650" style=" width:650px; padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><![endif]-->
<div class="col num12" style="min-width: 320px;max-width: 650px;display: table-cell;vertical-align: top;">
<div style="background-color: transparent; width: 100% !important;">
<!--[if (!mso)&(!IE)]><!--><div style="border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;"><!--<![endif]-->
&#160;
<!--[if (!mso)&(!IE)]><!--></div><!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div> <!--[if (mso)|(IE)]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
<!--[if (mso)|(IE)]></div><![endif]-->
<img src="http://www.google-analytics.com/collect?v=1&tid=UA-112738735-2&cid={{var hash}}&t=event&ec=email&ea=open&el={{var hash}}&cs=smtp+tester&cm=email+zend&cn=SMTP&cm1=1" width="1" height="1" />
</body></html>
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
<container name="root">
<block class="MagePal\GmailSmtpApp\Block\Adminhtml\ValidateConfig" name="magepal.smtp_validate"
template="MagePal_GmailSmtpApp::validateConfig/result.phtml" />
</container>
</layout>
var config = {
map: {
'*': {
magePalGmailSmtpAppValidateConfig: 'MagePal_GmailSmtpApp/js/validate-config'
}
}
};
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
?>
<?php /** @var $block MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\ValidateConfigButton **/ ?>
<?= $block->getButtonHtml() ?>
<script type="text/x-magento-init">
{
"#gmailsmtpapp_debug_result_button": {
"magePalGmailSmtpAppValidateConfig": {
"postUrl":"<?= $block->escapeUrl($block->getAdminUrl()) ?>"
}
}
}
</script>
<?php
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* https://www.magepal.com | support@magepal.com
*/
?>
<style>
svg {
width: 50px;
display: block;
margin: 20px auto 0;
}
.path {
stroke-dasharray: 1000;
stroke-dashoffset: 0;
}
.path.circle {
-webkit-animation: dash 0.9s ease-in-out;
animation: dash 0.9s ease-in-out;
}
.path.line {
stroke-dashoffset: 1000;
-webkit-animation: dash 0.9s 0.35s ease-in-out forwards;
animation: dash 0.9s 0.35s ease-in-out forwards;
}
.path.check {
stroke-dashoffset: -100;
-webkit-animation: dash-check 0.9s 0.35s ease-in-out forwards;
animation: dash-check 0.9s 0.35s ease-in-out forwards;
}
.modal-popup p {
text-align: center;
margin: 20px 0 30px;
font-size: 1.25em;
}
.modal-popup p.success {
color: #73AF55;
font-size: 1.75em;
}
.modal-popup p.error {
color: #D06079;
}
h2.center {
text-align: center;
}
@-webkit-keyframes dash {
0% {
stroke-dashoffset: 1000;
}
100% {
stroke-dashoffset: 0;
}
}
@keyframes dash {
0% {
stroke-dashoffset: 1000;
}
100% {
stroke-dashoffset: 0;
}
}
@-webkit-keyframes dash-check {
0% {
stroke-dashoffset: -100;
}
100% {
stroke-dashoffset: 900;
}
}
@keyframes dash-check {
0% {
stroke-dashoffset: -100;
}
100% {
stroke-dashoffset: 900;
}
}
p.external-link {
font-style: italic;
}
div.error-wrapper {
max-height: 300px;
overflow: scroll;
margin-bottom: 40px;
}
div.error-wrapper p {
text-align: left;
}
.modal-popup.confirm .modal-inner-wrap {
width: 85rem;
padding-right: 25px;
}
.modal-popup.confirm .modal-inner-wrap .modal-content {
padding-right: 0;
}
hr.style {
border: 0;
height: 1px;
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0));
margin-bottom: 30px;
}
.tg {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: none;
margin-bottom: 50px;
}
.tg td {
font-family: Arial, sans-serif;
font-size: 14px;
padding: 15px 5px;
border: none;
overflow: hidden;
word-break: normal;
}
.tg th {
font-family: Arial, sans-serif;
font-size: 14px;
font-weight: normal;
padding: 15px 5px;
border-width: 1px;
overflow: hidden;
word-break: normal;
}
.tg .tg-baqh {
text-align: center;
vertical-align: top
}
</style>
<!--[if lte IE 9]>
<style>
.path {stroke-dasharray: 0 !important;}
</style>
<![endif]-->
<?php /** @var $block MagePal\GmailSmtpApp\Block\Adminhtml\ValidateConfig */ ?>
<?php foreach ($block->verify() as $result): ?>
<?php if ($result['has_error'] === false): ?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2">
<circle class="path circle" fill="none" stroke="#73AF55" stroke-width="6" stroke-miterlimit="10" cx="65.1"
cy="65.1" r="62.1"/>
<polyline class="path check" fill="none" stroke="#73AF55" stroke-width="6" stroke-linecap="round"
stroke-miterlimit="10" points="100.2,40.2 51.5,88.8 29.8,67.5 "/>
</svg>
<p class="success">Congratulations, You Did It!</p>
<p><?= $block->escapeHtml($result['msg']) ?></p>
<hr class="style" />
<h2 class="center">What next? Take a look at our other top extensions.</h2>
<table class="tg">
<tbody><tr>
<td class="tg-baqh" colspan="2">
<a href="https://bit.ly/smtp-ex-ee" target="_blank">Enhanced E-commerce for Google Tag Manager</a>
</td>
</tr><tr>
<th class="tg-baqh">
<a href="https://bit.ly/smtp-ex-esp" target="_blank">Enhanced Success Page</a>
</th>
<th class="tg-baqh">
<a href="https://bit.ly/smtp-ex-ete" target="_blank">Enhanced Sales Email</a>
</th>
</tr>
</tr>
</tbody>
</table>
<?php else: ?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 130.2 130.2">
<circle class="path circle" fill="none" stroke="#D06079" stroke-width="6" stroke-miterlimit="10" cx="65.1"
cy="65.1" r="62.1"/>
<line class="path line" fill="none" stroke="#D06079" stroke-width="6" stroke-linecap="round"
stroke-miterlimit="10" x1="34.4" y1="37.9" x2="95.8" y2="92.3"/>
<line class="path line" fill="none" stroke="#D06079" stroke-width="6" stroke-linecap="round"
stroke-miterlimit="10" x1="95.8" y1="38" x2="34.4" y2="92.2"/>
</svg>
<p class="error">Bummer!</p>
<div class="error-wrapper">
<p><?= $block->escapeHtml($block->formatErrorMsg($result['msg']), ['a']) ?> </p>
</div>
<hr class="style"/>
<h2 class="center">Need help with configuring your SMTP?</h2>
<p>
Our affordable technical support include help with installation, usage, configuration, and troubleshooting
our Free Open Source extensions.
</p>
<p>
Visit <a href="http://bit.ly/MpSmtpEr" target="_blank">www.magepal.com</a> to learn more about our
<a href="http://bit.ly/MpSmtpEr"
target="_blank">Premium 30 Minutes Paid Support</a>.
</p>
<?php endif; ?>
<?php endforeach; ?>
svg {
width: 50px;
display: block;
margin: 20px auto 0;
}
.path {
stroke-dasharray: 1000;
stroke-dashoffset: 0;
&.circle {
-webkit-animation: dash .9s ease-in-out;
animation: dash .9s ease-in-out;
}
&.line {
stroke-dashoffset: 1000;
-webkit-animation: dash .9s .35s ease-in-out forwards;
animation: dash .9s .35s ease-in-out forwards;
}
&.check {
stroke-dashoffset: -100;
-webkit-animation: dash-check .9s .35s ease-in-out forwards;
animation: dash-check .9s .35s ease-in-out forwards;
}
}
.modal-popup {
p {
text-align: center;
margin: 20px 0 60px;
font-size: 1.25em;
&.success {
color: #73AF55;
}
&.error {
color: #D06079;
}
}
}
@-webkit-keyframes dash {
0% {
stroke-dashoffset: 1000;
}
100% {
stroke-dashoffset: 0;
}
}
@keyframes dash {
0% {
stroke-dashoffset: 1000;
}
100% {
stroke-dashoffset: 0;
}
}
@-webkit-keyframes dash-check {
0% {
stroke-dashoffset: -100;
}
100% {
stroke-dashoffset: 900;
}
}
@keyframes dash-check {
0% {
stroke-dashoffset: -100;
}
100% {
stroke-dashoffset: 900;
}
}
/**
* Copyright © MagePal LLC. All rights reserved.
* See COPYING.txt for license details.
* http://www.magepal.com | support@magepal.com
*/
define([
'jquery',
'Magento_Ui/js/modal/alert'
], function ($, alert) {
var formSubmit = function (config) {
var postData = {
form_key: FORM_KEY
};
/** global var configForm **/
configForm.find('[id^=system_gmailsmtpapp]').find(':input').serializeArray().map(function (field) {
var name = field.name.match(/groups\[gmailsmtpapp\]?(\[groups\]\[debug\])?\[fields\]\[(.*)\]\[value]/);
/**
* groups[gmailsmtpapp][groups][debug][fields][email][value]
* groups[gmailsmtpapp][fields][password][value]
*/
if (name && name.length === 3) {
postData[name[2]] = field.value;
}
});
$.ajax({
url: config.postUrl,
type: 'post',
dataType: 'html',
data: postData,
showLoader: true
}).done(function (response) {
if (typeof response === 'object') {
if (response.error) {
alert({ title: 'Error', content: response.message });
} else if (response.ajaxExpired) {
window.location.href = response.ajaxRedirect;
}
} else {
alert({
title:'',
content:response,
buttons: []
});
}
});
};
return function (config, element) {
$(element).on('click', function () {
formSubmit(config);
});
}
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment