Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
AbstractTemplate.php
Go to the documentation of this file.
1 <?php
7 namespace Magento\Email\Model;
8 
14 use Magento\Store\Model\Information as StoreInformation;
17 
27 abstract class AbstractTemplate extends AbstractModel implements TemplateTypesInterface
28 {
32  const DEFAULT_DESIGN_AREA = 'frontend';
33 
37  const DEFAULT_LOGO_FILE_ID = 'Magento_Email::logo_email.png';
38 
42  const XML_PATH_DESIGN_EMAIL_LOGO = 'design/email/logo';
43 
47  const XML_PATH_DESIGN_EMAIL_LOGO_ALT = 'design/email/logo_alt';
48 
52  const XML_PATH_DESIGN_EMAIL_LOGO_WIDTH = 'design/email/logo_width';
53 
57  const XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT = 'design/email/logo_height';
58 
64  private $designConfig;
65 
71  private $isChildTemplate = false;
72 
78  private $templateFilter;
79 
85  private $emulatedDesignConfig = false;
86 
92  private $area;
93 
99  private $store;
100 
108  private $hasDesignBeenApplied = false;
109 
113  protected $templateFactory = null;
114 
120  protected $design = null;
121 
125  protected $appEmulation;
126 
130  protected $storeManager;
131 
137  protected $assetRepo;
138 
142  protected $filesystem;
143 
149  protected $scopeConfig;
150 
154  protected $emailConfig;
155 
159  protected $filterManager;
160 
164  private $urlModel;
165 
183  public function __construct(
184  \Magento\Framework\Model\Context $context,
185  \Magento\Framework\View\DesignInterface $design,
186  \Magento\Framework\Registry $registry,
187  \Magento\Store\Model\App\Emulation $appEmulation,
189  \Magento\Framework\View\Asset\Repository $assetRepo,
190  \Magento\Framework\Filesystem $filesystem,
191  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
192  \Magento\Email\Model\Template\Config $emailConfig,
193  \Magento\Email\Model\TemplateFactory $templateFactory,
194  \Magento\Framework\Filter\FilterManager $filterManager,
195  \Magento\Framework\UrlInterface $urlModel,
196  array $data = []
197  ) {
198  $this->design = $design;
199  $this->area = isset($data['area']) ? $data['area'] : null;
200  $this->store = isset($data['store']) ? $data['store'] : null;
201  $this->appEmulation = $appEmulation;
202  $this->storeManager = $storeManager;
203  $this->assetRepo = $assetRepo;
204  $this->filesystem = $filesystem;
205  $this->scopeConfig = $scopeConfig;
206  $this->emailConfig = $emailConfig;
207  $this->templateFactory = $templateFactory;
208  $this->filterManager = $filterManager;
209  $this->urlModel = $urlModel;
210  parent::__construct($context, $registry, null, null, $data);
211  }
212 
220  public function getTemplateContent($configPath, array $variables)
221  {
222  $template = $this->getTemplateInstance();
223 
224  // Ensure child templates have the same area/store context as parent
225  $template->setDesignConfig($this->getDesignConfig()->toArray())
226  ->loadByConfigPath($configPath, $variables)
227  ->setTemplateType($this->getType())
228  ->setIsChildTemplate(true);
229 
230  // automatically strip tags if in a plain-text parent
231  if ($this->isPlain()) {
232  $templateText = $this->filterManager->stripTags($template->getTemplateText());
233  $template->setTemplateText(trim($templateText));
234  }
235 
236  $processedTemplate = $template->getProcessedTemplate($variables);
237  if ($this->isPlain()) {
238  $processedTemplate = trim($processedTemplate);
239  }
240 
241  return $processedTemplate;
242  }
243 
249  protected function getTemplateInstance()
250  {
251  return $this->templateFactory->create();
252  }
253 
260  public function loadByConfigPath($configPath)
261  {
262  $store = $this->getDesignConfig()->getStore();
263  $templateId = $this->scopeConfig->getValue($configPath, ScopeInterface::SCOPE_STORE, $store);
264 
265  if (is_numeric($templateId)) {
266  $this->load($templateId);
267  } else {
268  $this->loadDefault($templateId);
269  }
270  return $this;
271  }
272 
279  public function loadDefault($templateId)
280  {
281  $designParams = $this->getDesignParams();
282  $templateFile = $this->emailConfig->getTemplateFilename($templateId, $designParams);
283  $templateType = $this->emailConfig->getTemplateType($templateId);
284  $templateTypeCode = $templateType == 'html' ? self::TYPE_HTML : self::TYPE_TEXT;
285  $this->setTemplateType($templateTypeCode);
286 
287  $rootDirectory = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);
288  $templateText = $rootDirectory->readFile($rootDirectory->getRelativePath($templateFile));
289 
293  if (preg_match('/^<!--[\w\W]+?-->/m', $templateText, $matches) && strpos($matches[0], 'Copyright') !== false) {
294  $templateText = str_replace($matches[0], '', $templateText);
295  }
296 
297  if (preg_match('/<!--@subject\s*(.*?)\s*@-->/u', $templateText, $matches)) {
298  $this->setTemplateSubject($matches[1]);
299  $templateText = str_replace($matches[0], '', $templateText);
300  }
301 
302  if (preg_match('/<!--@vars\s*((?:.)*?)\s*@-->/us', $templateText, $matches)) {
303  $this->setData('orig_template_variables', str_replace("\n", '', $matches[1]));
304  $templateText = str_replace($matches[0], '', $templateText);
305  }
306 
307  if (preg_match('/<!--@styles\s*(.*?)\s*@-->/s', $templateText, $matches)) {
308  $this->setTemplateStyles($matches[1]);
309  $templateText = str_replace($matches[0], '', $templateText);
310  }
311 
312  // Remove comment lines and extra spaces
313  $templateText = trim(preg_replace('#\{\*.*\*\}#suU', '', $templateText));
314 
315  $this->setTemplateText($templateText);
316  $this->setId($templateId);
317 
318  return $this;
319  }
320 
328  public function getProcessedTemplate(array $variables = [])
329  {
330  $processor = $this->getTemplateFilter()
331  ->setUseSessionInUrl(false)
332  ->setPlainTemplateMode($this->isPlain())
333  ->setIsChildTemplate($this->isChildTemplate())
334  ->setTemplateProcessor([$this, 'getTemplateContent']);
335 
336  $variables['this'] = $this;
337 
338  $isDesignApplied = $this->applyDesignConfig();
339 
340  // Set design params so that CSS will be loaded from the proper theme
341  $processor->setDesignParams($this->getDesignParams());
342 
343  if (isset($variables['subscriber'])) {
344  $storeId = $variables['subscriber']->getStoreId();
345  } else {
346  $storeId = $this->getDesignConfig()->getStore();
347  }
348  $processor->setStoreId($storeId);
349 
350  // Populate the variables array with store, store info, logo, etc. variables
351  $variables = $this->addEmailVariables($variables, $storeId);
352  $processor->setVariables($variables);
353 
354  try {
355  $result = $processor->filter($this->getTemplateText());
356  } catch (\Exception $e) {
357  $this->cancelDesignConfig();
358  throw new \LogicException(__($e->getMessage()), $e->getCode(), $e);
359  }
360  if ($isDesignApplied) {
361  $this->cancelDesignConfig();
362  }
363  return $result;
364  }
365 
371  public function getDefaultEmailLogo()
372  {
373  $designParams = $this->getDesignParams();
374  return $this->assetRepo->getUrlWithParams(
375  self::DEFAULT_LOGO_FILE_ID,
376  $designParams
377  );
378  }
379 
386  protected function getLogoUrl($store)
387  {
388  $store = $this->storeManager->getStore($store);
389  $fileName = $this->scopeConfig->getValue(
390  self::XML_PATH_DESIGN_EMAIL_LOGO,
392  $store
393  );
394  if ($fileName) {
396  $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA);
397  if ($mediaDirectory->isFile($uploadDir . '/' . $fileName)) {
398  return $this->storeManager->getStore()->getBaseUrl(
399  \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
400  ) . $uploadDir . '/' . $fileName;
401  }
402  }
403  return $this->getDefaultEmailLogo();
404  }
405 
412  protected function getLogoAlt($store)
413  {
414  $store = $this->storeManager->getStore($store);
415  $alt = $this->scopeConfig->getValue(
416  self::XML_PATH_DESIGN_EMAIL_LOGO_ALT,
418  $store
419  );
420  if ($alt) {
421  return $alt;
422  }
423  return $store->getFrontendName();
424  }
425 
436  protected function addEmailVariables($variables, $storeId)
437  {
438  $store = $this->storeManager->getStore($storeId);
439  if (!isset($variables['store'])) {
440  $variables['store'] = $store;
441  }
442  if (!isset($variables['logo_url'])) {
443  $variables['logo_url'] = $this->getLogoUrl($storeId);
444  }
445  if (!isset($variables['logo_alt'])) {
446  $variables['logo_alt'] = $this->getLogoAlt($storeId);
447  }
448  if (!isset($variables['logo_width'])) {
449  $variables['logo_width'] = $this->scopeConfig->getValue(
450  self::XML_PATH_DESIGN_EMAIL_LOGO_WIDTH,
452  $store
453  );
454  }
455  if (!isset($variables['logo_height'])) {
456  $variables['logo_height'] = $this->scopeConfig->getValue(
457  self::XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT,
459  $store
460  );
461  }
462  if (!isset($variables['store_phone'])) {
463  $variables['store_phone'] = $this->scopeConfig->getValue(
464  StoreInformation::XML_PATH_STORE_INFO_PHONE,
466  $store
467  );
468  }
469  if (!isset($variables['store_hours'])) {
470  $variables['store_hours'] = $this->scopeConfig->getValue(
471  StoreInformation::XML_PATH_STORE_INFO_HOURS,
473  $store
474  );
475  }
476  if (!isset($variables['store_email'])) {
477  $variables['store_email'] = $this->scopeConfig->getValue(
478  'trans_email/ident_support/email',
480  $store
481  );
482  }
483  // If template is text mode, don't include styles
484  if (!$this->isPlain() && !isset($variables['template_styles'])) {
485  $variables['template_styles'] = $this->getTemplateStyles();
486  }
487 
488  return $variables;
489  }
490 
497  protected function applyDesignConfig()
498  {
499  // Only run app emulation if this is the parent template and emulation isn't already running.
500  // Otherwise child will run inside parent emulation.
501  if ($this->isChildTemplate() || $this->hasDesignBeenApplied) {
502  return false;
503  }
504  $this->hasDesignBeenApplied = true;
505 
506  $designConfig = $this->getDesignConfig();
507  $storeId = $designConfig->getStore();
508  $area = $designConfig->getArea();
509  if ($storeId !== null) {
510  // Force emulation in case email is being sent from same store so that theme will be loaded. Helpful
511  // for situations where emails may be sent from bootstrap files that load frontend store, but not theme
512  $this->appEmulation->startEnvironmentEmulation($storeId, $area, true);
513  }
514  return true;
515  }
516 
522  protected function cancelDesignConfig()
523  {
524  $this->appEmulation->stopEnvironmentEmulation();
525  $this->hasDesignBeenApplied = false;
526  return $this;
527  }
528 
535  public function setForcedArea($templateId)
536  {
537  if ($this->area === null) {
538  $this->area = $this->emailConfig->getTemplateArea($templateId);
539  }
540 
541  return $this;
542  }
543 
554  {
555  $area = $this->emailConfig->getTemplateArea($templateId);
556  $this->design->setDesignTheme($theme, $area);
557  return $this;
558  }
559 
565  public function getDesignParams()
566  {
567  return [
568  // Retrieve area from getDesignConfig, rather than the getDesignTheme->getArea(), as the latter doesn't
569  // return the emulated area
570  'area' => $this->getDesignConfig()->getArea(),
571  'theme' => $this->design->getDesignTheme()->getCode(),
572  'themeModel' => $this->design->getDesignTheme(),
573  'locale' => $this->design->getLocale(),
574  ];
575  }
576 
582  public function getDesignConfig()
583  {
584  if ($this->designConfig === null) {
585  if ($this->area === null) {
586  $this->area = $this->design->getArea();
587  }
588  if ($this->store === null) {
589  $this->store = $this->storeManager->getStore()->getId();
590  }
591  $this->designConfig = new DataObject(
592  ['area' => $this->area, 'store' => $this->store]
593  );
594  }
595  return $this->designConfig;
596  }
597 
605  public function setDesignConfig(array $config)
606  {
607  if (!isset($config['area']) || !isset($config['store'])) {
608  throw new LocalizedException(
609  __('The design config needs an area and a store. Verify that both are set and try again.')
610  );
611  }
612  $this->getDesignConfig()->setData($config);
613  return $this;
614  }
615 
621  public function isChildTemplate()
622  {
623  return $this->isChildTemplate;
624  }
625 
632  public function setIsChildTemplate($isChildTemplate)
633  {
634  $this->isChildTemplate = (bool) $isChildTemplate;
635  return $this;
636  }
637 
644  public function setTemplateFilter(Template\Filter $filter)
645  {
646  $this->templateFilter = $filter;
647  return $this;
648  }
649 
655  public function getTemplateFilter()
656  {
657  if (empty($this->templateFilter)) {
658  $this->templateFilter = $this->getFilterFactory()->create();
659  $this->templateFilter->setUseAbsoluteLinks($this->getUseAbsoluteLinks())
660  ->setStoreId($this->getDesignConfig()->getStore())
661  ->setUrlModel($this->urlModel);
662  }
663  return $this->templateFilter;
664  }
665 
674  public function emulateDesign($storeId, $area = self::DEFAULT_DESIGN_AREA)
675  {
676  if ($storeId !== null && $storeId !== false) {
677  // save current design settings
678  $this->emulatedDesignConfig = clone $this->getDesignConfig();
679  if ($this->getDesignConfig()->getStore() != $storeId
680  || $this->getDesignConfig()->getArea() != $area
681  ) {
682  $this->setDesignConfig(['area' => $area, 'store' => $storeId]);
683  $this->applyDesignConfig();
684  }
685  } else {
686  $this->emulatedDesignConfig = false;
687  }
688  }
689 
695  public function revertDesign()
696  {
697  if ($this->emulatedDesignConfig) {
698  $this->setDesignConfig($this->emulatedDesignConfig->getData());
699  $this->cancelDesignConfig();
700  $this->emulatedDesignConfig = false;
701  }
702  }
703 
709  public function isPlain()
710  {
711  return $this->getType() == self::TYPE_TEXT;
712  }
713 
719  abstract protected function getFilterFactory();
720 
726  abstract public function getType();
727 
736  public function getUrl(Store $store, $route = '', $params = [])
737  {
738  $url = $this->urlModel->setScope($store);
739  if ($this->storeManager->getStore()->getId() != $store->getId()) {
740  $params['_scope_to_url'] = true;
741  }
742  return $url->getUrl($route, $params);
743  }
744 }
setTemplateFilter(Template\Filter $filter)
$config
Definition: fraud_order.php:17
$processor
Definition: 404.php:10
__()
Definition: __.php:13
$templateType
Definition: list.phtml:37
__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=[])
$templateId
Definition: queue.php:15
$fileName
Definition: translate.phtml:15
$theme
emulateDesign($storeId, $area=self::DEFAULT_DESIGN_AREA)
getUrl(Store $store, $route='', $params=[])
$mediaDirectory
getTemplateContent($configPath, array $variables)
$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE]
Definition: website.php:18
$rootDirectory
$template
Definition: export.php:12