File: /var/www/vhost/disk-apps/magento.bikenow.co/vendor/magento/module-email/Model/AbstractTemplate.php
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Email\Model;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Model\AbstractModel;
use Magento\Store\Model\Information as StoreInformation;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\Store;
use Magento\MediaStorage\Helper\File\Storage\Database;
/**
* Template model class.
*
* phpcs:disable Magento2.Classes.AbstractApi
* @author Magento Core Team <core@magentocommerce.com>
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
* @api
* @since 100.0.2
*/
abstract class AbstractTemplate extends AbstractModel implements TemplateTypesInterface
{
/**
* Default design area for emulation
*/
const DEFAULT_DESIGN_AREA = 'frontend';
/**
* Default path to email logo
*/
const DEFAULT_LOGO_FILE_ID = 'Magento_Email::logo_email.png';
/**
* Email logo url
*/
const XML_PATH_DESIGN_EMAIL_LOGO = 'design/email/logo';
/**
* Email logo alt text
*/
const XML_PATH_DESIGN_EMAIL_LOGO_ALT = 'design/email/logo_alt';
/**
* Email logo width
*/
const XML_PATH_DESIGN_EMAIL_LOGO_WIDTH = 'design/email/logo_width';
/**
* Email logo height
*/
const XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT = 'design/email/logo_height';
/**
* Configuration of design package for template
*
* @var DataObject
*/
private $designConfig;
/**
* Whether template is child of another template
*
* @var bool
*/
private $isChildTemplate = false;
/**
* Email template filter
*
* @var \Magento\Email\Model\Template\Filter
*/
private $templateFilter;
/**
* Configuration of emulated design package.
*
* @var DataObject|boolean
*/
private $emulatedDesignConfig = false;
/**
* Package area
*
* @var string
*/
private $area;
/**
* Store id
*
* @var int
*/
private $store;
/**
* Tracks whether design has been applied within the context of this template model.
*
* Important as there are multiple entry points for the applyDesignConfig method.
*
* @var bool
*/
private $hasDesignBeenApplied = false;
/**
* @var \Magento\Email\Model\TemplateFactory
*/
protected $templateFactory = null;
/**
* Design package instance
*
* @var \Magento\Framework\View\DesignInterface
*/
protected $design = null;
/**
* @var \Magento\Store\Model\App\Emulation
*/
protected $appEmulation;
/**
* @var \Magento\Store\Model\StoreManagerInterface
*/
protected $storeManager;
/**
* Asset service
*
* @var \Magento\Framework\View\Asset\Repository
*/
protected $assetRepo;
/**
* @var \Magento\Framework\Filesystem
*/
protected $filesystem;
/**
* Scope config
*
* @var \Magento\Framework\App\Config\ScopeConfigInterface
*/
protected $scopeConfig;
/**
* @var \Magento\Email\Model\Template\Config
*/
protected $emailConfig;
/**
* @var \Magento\Framework\Filter\FilterManager
*/
protected $filterManager;
/**
* @var \Magento\Framework\UrlInterface
*/
private $urlModel;
/**
* @var Database
*/
private $fileStorageDatabase;
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\View\DesignInterface $design
* @param \Magento\Framework\Registry $registry
* @param \Magento\Store\Model\App\Emulation $appEmulation
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @param \Magento\Framework\View\Asset\Repository $assetRepo
* @param \Magento\Framework\Filesystem $filesystem
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Email\Model\Template\Config $emailConfig
* @param \Magento\Email\Model\TemplateFactory $templateFactory
* @param \Magento\Framework\Filter\FilterManager $filterManager
* @param \Magento\Framework\UrlInterface $urlModel
* @param array $data
* @param Database $fileStorageDatabase
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\View\DesignInterface $design,
\Magento\Framework\Registry $registry,
\Magento\Store\Model\App\Emulation $appEmulation,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\View\Asset\Repository $assetRepo,
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Email\Model\Template\Config $emailConfig,
\Magento\Email\Model\TemplateFactory $templateFactory,
\Magento\Framework\Filter\FilterManager $filterManager,
\Magento\Framework\UrlInterface $urlModel,
array $data = [],
Database $fileStorageDatabase = null
) {
$this->design = $design;
$this->area = isset($data['area']) ? $data['area'] : null;
$this->store = isset($data['store']) ? $data['store'] : null;
$this->appEmulation = $appEmulation;
$this->storeManager = $storeManager;
$this->assetRepo = $assetRepo;
$this->filesystem = $filesystem;
$this->scopeConfig = $scopeConfig;
$this->emailConfig = $emailConfig;
$this->templateFactory = $templateFactory;
$this->filterManager = $filterManager;
$this->urlModel = $urlModel;
$this->fileStorageDatabase = $fileStorageDatabase ?:
\Magento\Framework\App\ObjectManager::getInstance()->get(Database::class);
parent::__construct($context, $registry, null, null, $data);
}
/**
* Get contents of the included template for template directive
*
* @param string $configPath
* @param array $variables
* @return string
*/
public function getTemplateContent($configPath, array $variables)
{
$template = $this->getTemplateInstance();
// Ensure child templates have the same area/store context as parent
$template->setDesignConfig($this->getDesignConfig()->toArray())
->loadByConfigPath($configPath, $variables)
->setTemplateType($this->getType())
->setIsChildTemplate(true);
// automatically strip tags if in a plain-text parent
if ($this->isPlain()) {
$templateText = $this->filterManager->stripTags($template->getTemplateText());
$template->setTemplateText(trim($templateText));
}
$processedTemplate = $template->getProcessedTemplate($variables);
if ($this->isPlain()) {
$processedTemplate = trim($processedTemplate);
}
return $processedTemplate;
}
/**
* Return a new instance of the template object. Used by the template directive.
*
* @return \Magento\Email\Model\AbstractTemplate
*/
protected function getTemplateInstance()
{
return $this->templateFactory->create();
}
/**
* Load template from database when overridden in configuration or load default from relevant file system location.
*
* @param string $configPath
* @return \Magento\Email\Model\AbstractTemplate
*/
public function loadByConfigPath($configPath)
{
$store = $this->getDesignConfig()->getStore();
$templateId = $this->scopeConfig->getValue($configPath, ScopeInterface::SCOPE_STORE, $store);
if (is_numeric($templateId)) {
$this->load($templateId);
} else {
$this->loadDefault($templateId);
}
return $this;
}
/**
* Load default email template
*
* @param string $templateId
* @return $this
*/
public function loadDefault($templateId)
{
$designParams = $this->getDesignParams();
$templateFile = $this->emailConfig->getTemplateFilename($templateId, $designParams);
$templateType = $this->emailConfig->getTemplateType($templateId);
$templateTypeCode = $templateType == 'html' ? self::TYPE_HTML : self::TYPE_TEXT;
$this->setTemplateType($templateTypeCode);
$rootDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);
$templateText = $rootDirectory->readFile($rootDirectory->getRelativePath($templateFile));
/**
* trim copyright message
*/
if (preg_match('/^<!--[\w\W]+?-->/m', $templateText, $matches) && strpos($matches[0], 'Copyright') !== false) {
$templateText = str_replace($matches[0], '', $templateText);
}
if (preg_match('/<!--@subject\s*(.*?)\s*@-->/u', $templateText, $matches)) {
$this->setTemplateSubject($matches[1]);
$templateText = str_replace($matches[0], '', $templateText);
}
if (preg_match('/<!--@vars\s*((?:.)*?)\s*@-->/us', $templateText, $matches)) {
$this->setData('orig_template_variables', str_replace("\n", '', $matches[1]));
$templateText = str_replace($matches[0], '', $templateText);
}
if (preg_match('/<!--@styles\s*(.*?)\s*@-->/s', $templateText, $matches)) {
$this->setTemplateStyles($matches[1]);
$templateText = str_replace($matches[0], '', $templateText);
}
// Remove comment lines and extra spaces
$templateText = trim(preg_replace('#\{\*.*\*\}#suU', '', $templateText));
$this->setTemplateText($templateText);
$this->setId($templateId);
return $this;
}
/**
* Process email template code
*
* @param array $variables
* @return string
* @throws \Magento\Framework\Exception\MailException
*/
public function getProcessedTemplate(array $variables = [])
{
$processor = $this->getTemplateFilter()
->setPlainTemplateMode($this->isPlain())
->setIsChildTemplate($this->isChildTemplate())
->setTemplateProcessor([$this, 'getTemplateContent']);
$variables['this'] = $this;
$isDesignApplied = $this->applyDesignConfig();
// Set design params so that CSS will be loaded from the proper theme
$processor->setDesignParams($this->getDesignParams());
if (isset($variables['subscriber'])) {
$storeId = $variables['subscriber']->getStoreId();
} else {
$storeId = $this->getDesignConfig()->getStore();
}
$processor->setStoreId($storeId);
// Populate the variables array with store, store info, logo, etc. variables
$variables = $this->addEmailVariables($variables, $storeId);
$processor->setVariables($variables);
// Type legacy id strict
// db legacy true numeric false
// db new false numeric true
// filesystem false string false
// preview false null true
$isLegacy = $this->getData('is_legacy');
$templateId = $this->getTemplateId();
$previousStrictMode = $processor->setStrictMode(
!$isLegacy && (is_numeric($templateId) || empty($templateId))
);
try {
$result = $processor->filter($this->getTemplateText());
} catch (\Exception $e) {
$this->cancelDesignConfig();
throw new \LogicException(__($e->getMessage()), $e->getCode(), $e);
} finally {
$processor->setStrictMode($previousStrictMode);
}
if ($isDesignApplied) {
$this->cancelDesignConfig();
}
return $result;
}
/**
* Get default email logo image
*
* @return string
*/
public function getDefaultEmailLogo()
{
$designParams = $this->getDesignParams();
return $this->assetRepo->getUrlWithParams(
self::DEFAULT_LOGO_FILE_ID,
$designParams
);
}
/**
* Return logo URL for emails. Take logo from theme if custom logo is undefined
*
* @param Store|int|string $store
* @return string
*/
protected function getLogoUrl($store)
{
$store = $this->storeManager->getStore($store);
$fileName = $this->scopeConfig->getValue(
self::XML_PATH_DESIGN_EMAIL_LOGO,
ScopeInterface::SCOPE_STORE,
$store
);
if ($fileName) {
$uploadDir = \Magento\Email\Model\Design\Backend\Logo::UPLOAD_DIR;
$mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
if ($this->fileStorageDatabase->checkDbUsage() &&
!$mediaDirectory->isFile($uploadDir . '/' . $fileName)
) {
$this->fileStorageDatabase->saveFileToFilesystem($uploadDir . '/' . $fileName);
}
if ($mediaDirectory->isFile($uploadDir . '/' . $fileName)) {
return $this->storeManager->getStore()->getBaseUrl(
\Magento\Framework\UrlInterface::URL_TYPE_MEDIA
) . $uploadDir . '/' . $fileName;
}
}
return $this->getDefaultEmailLogo();
}
/**
* Return logo alt for emails
*
* @param Store|int|string $store
* @return string
*/
protected function getLogoAlt($store)
{
$store = $this->storeManager->getStore($store);
$alt = $this->scopeConfig->getValue(
self::XML_PATH_DESIGN_EMAIL_LOGO_ALT,
ScopeInterface::SCOPE_STORE,
$store
);
if ($alt) {
return $alt;
}
return $store->getFrontendName();
}
/**
* Add variables that are used by transactional and newsletter emails
*
* @param array $variables
* @param null|string|bool|int|Store $storeId
* @return mixed
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
protected function addEmailVariables($variables, $storeId)
{
$store = $this->storeManager->getStore($storeId);
if (!isset($variables['store'])) {
$variables['store'] = $store;
}
if (!isset($variables['store']['frontend_name'])) {
$variables['store']['frontend_name'] = $store->getFrontendName();
}
if (!isset($variables['logo_url'])) {
$variables['logo_url'] = $this->getLogoUrl($storeId);
}
if (!isset($variables['logo_alt'])) {
$variables['logo_alt'] = $this->getLogoAlt($storeId);
}
if (!isset($variables['logo_width'])) {
$variables['logo_width'] = $this->scopeConfig->getValue(
self::XML_PATH_DESIGN_EMAIL_LOGO_WIDTH,
ScopeInterface::SCOPE_STORE,
$store
);
}
if (!isset($variables['logo_height'])) {
$variables['logo_height'] = $this->scopeConfig->getValue(
self::XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT,
ScopeInterface::SCOPE_STORE,
$store
);
}
if (!isset($variables['store_phone'])) {
$variables['store_phone'] = $this->scopeConfig->getValue(
StoreInformation::XML_PATH_STORE_INFO_PHONE,
ScopeInterface::SCOPE_STORE,
$store
);
}
if (!isset($variables['store_hours'])) {
$variables['store_hours'] = $this->scopeConfig->getValue(
StoreInformation::XML_PATH_STORE_INFO_HOURS,
ScopeInterface::SCOPE_STORE,
$store
);
}
if (!isset($variables['store_email'])) {
$variables['store_email'] = $this->scopeConfig->getValue(
'trans_email/ident_support/email',
ScopeInterface::SCOPE_STORE,
$store
);
}
// If template is text mode, don't include styles
if (!$this->isPlain() && !isset($variables['template_styles'])) {
$variables['template_styles'] = $this->getTemplateStyles();
}
return $variables;
}
/**
* Apply design config so that emails are processed within the context of the appropriate area/store/theme.
*
* @return bool
*/
protected function applyDesignConfig()
{
// Only run app emulation if this is the parent template and emulation isn't already running.
// Otherwise child will run inside parent emulation.
if ($this->isChildTemplate() || $this->hasDesignBeenApplied) {
return false;
}
$this->hasDesignBeenApplied = true;
$designConfig = $this->getDesignConfig();
$storeId = $designConfig->getStore();
$area = $designConfig->getArea();
if ($storeId !== null) {
// Force emulation in case email is being sent from same store so that theme will be loaded. Helpful
// for situations where emails may be sent from bootstrap files that load frontend store, but not theme
$this->appEmulation->startEnvironmentEmulation($storeId, $area, true);
}
return true;
}
/**
* Revert design settings to previous
*
* @return $this
*/
protected function cancelDesignConfig()
{
$this->appEmulation->stopEnvironmentEmulation();
$this->urlModel->setScope(null);
$this->hasDesignBeenApplied = false;
return $this;
}
/**
* Store the area associated with a template so that it will be returned by getDesignConfig and getDesignParams
*
* @param string $templateId
* @return $this
*/
public function setForcedArea($templateId)
{
if ($this->area === null) {
$this->area = $this->emailConfig->getTemplateArea($templateId);
}
return $this;
}
/**
* Manually set a theme that will be used by getParams
*
* Used to force the loading of an email template from a specific theme
*
* @param string $templateId
* @param string $theme
* @return $this
*/
public function setForcedTheme($templateId, $theme)
{
$area = $this->emailConfig->getTemplateArea($templateId);
$this->design->setDesignTheme($theme, $area);
return $this;
}
/**
* Returns the design params for the template being processed
*
* @return array
*/
public function getDesignParams()
{
return [
// Retrieve area from getDesignConfig, rather than the getDesignTheme->getArea(), as the latter doesn't
// return the emulated area
'area' => $this->getDesignConfig()->getArea(),
'theme' => $this->design->getDesignTheme()->getCode(),
'themeModel' => $this->design->getDesignTheme(),
'locale' => $this->design->getLocale(),
];
}
/**
* Get design configuration data
*
* @return DataObject
*/
public function getDesignConfig()
{
if ($this->designConfig === null) {
if ($this->area === null) {
$this->area = $this->design->getArea();
}
if ($this->store === null) {
$this->store = $this->storeManager->getStore()->getId();
}
$this->designConfig = new DataObject(
['area' => $this->area, 'store' => $this->store]
);
}
return $this->designConfig;
}
/**
* Initialize design information for template processing
*
* @param array $config
* @return $this
* @throws LocalizedException
*/
public function setDesignConfig(array $config)
{
if (!isset($config['area']) || !isset($config['store'])) {
throw new LocalizedException(
__('The design config needs an area and a store. Verify that both are set and try again.')
);
}
$this->getDesignConfig()->setData($config);
return $this;
}
/**
* Check whether template is child of another template
*
* @return bool
*/
public function isChildTemplate()
{
return $this->isChildTemplate;
}
/**
* Set whether template is child of another template
*
* @param bool $isChildTemplate
* @return $this
*/
public function setIsChildTemplate($isChildTemplate)
{
$this->isChildTemplate = (bool) $isChildTemplate;
return $this;
}
/**
* Declare template processing filter
*
* @param \Magento\Email\Model\Template\Filter $filter
* @return $this
*/
public function setTemplateFilter(Template\Filter $filter)
{
$this->templateFilter = $filter;
return $this;
}
/**
* Get filter object for template processing
*
* @return \Magento\Email\Model\Template\Filter
*/
public function getTemplateFilter()
{
if (empty($this->templateFilter)) {
$this->templateFilter = $this->getFilterFactory()->create();
$this->templateFilter->setUseAbsoluteLinks($this->getUseAbsoluteLinks())
->setStoreId($this->getDesignConfig()->getStore())
->setUrlModel($this->urlModel);
}
return $this->templateFilter;
}
/**
* Save current design config and replace with design config from specified store. Event is not dispatched.
*
* @param null|bool|int|string $storeId
* @param string $area
* @return void
*/
public function emulateDesign($storeId, $area = self::DEFAULT_DESIGN_AREA)
{
if ($storeId !== null && $storeId !== false) {
// save current design settings
$this->emulatedDesignConfig = clone $this->getDesignConfig();
if ($this->getDesignConfig()->getStore() != $storeId
|| $this->getDesignConfig()->getArea() != $area
) {
$this->setDesignConfig(['area' => $area, 'store' => $storeId]);
$this->applyDesignConfig();
}
} else {
$this->emulatedDesignConfig = false;
}
}
/**
* Revert to last design config, used before emulation
*
* @return void
*/
public function revertDesign()
{
if ($this->emulatedDesignConfig) {
$this->setDesignConfig($this->emulatedDesignConfig->getData());
$this->cancelDesignConfig();
$this->emulatedDesignConfig = false;
}
}
/**
* Return true if template type eq text
*
* @return boolean
*/
public function isPlain()
{
return $this->getType() == self::TYPE_TEXT;
}
/**
* Getter for filter factory that is specific to the type of template being processed
*
* @return mixed
*/
abstract protected function getFilterFactory();
/**
* Getter for template type
*
* @return int|string
*/
abstract public function getType();
/**
* Generate URL for the specified store.
*
* @param Store $store
* @param string $route
* @param array $params
* @return string
*/
public function getUrl(Store $store, $route = '', $params = [])
{
$url = $this->urlModel->setScope($store);
if ($this->storeManager->getStore()->getId() != $store->getId()) {
$params['_scope_to_url'] = true;
}
return $url->getUrl($route, $params);
}
}