├── .gitignore
├── Block
└── Adminhtml
│ ├── Edit
│ └── Tab
│ │ └── Mail
│ │ └── Grid
│ │ └── Renderer
│ │ ├── Action.php
│ │ ├── Customer.php
│ │ ├── Recipients.php
│ │ ├── Sent.php
│ │ └── Template.php
│ ├── Grid.php
│ ├── Mail
│ ├── Edit
│ │ ├── Form.php
│ │ ├── Info.php
│ │ └── View.php
│ └── Send
│ │ └── Form.php
│ ├── MailOverview.php
│ ├── MailTab.php
│ └── Widget
│ └── Grid
│ └── Column
│ └── Filter
│ └── Text.php
├── CHANGELOG.md
├── Controller
└── Adminhtml
│ ├── Index
│ └── Mail.php
│ ├── Mail.php
│ └── Mail
│ ├── Attachment.php
│ ├── Edit.php
│ ├── Overview.php
│ ├── Send.php
│ └── SendPost.php
├── LICENSE.txt
├── Model
├── Config.php
├── Config
│ └── Source
│ │ ├── Storage.php
│ │ └── Type.php
├── Mail.php
├── Mail
│ ├── Attachment.php
│ ├── AttachmentCollection.php
│ ├── AttachmentCollectionInterface.php
│ ├── AttachmentInterface.php
│ ├── Message.php
│ └── MessageInterface.php
├── ResourceModel
│ ├── Mail.php
│ └── Mail
│ │ └── Collection.php
├── Simulation
│ └── Config.php
├── Storages
│ ├── Base.php
│ ├── DefaultStorage.php
│ └── StorageInterface.php
├── Template
│ └── TransportBuilder.php
└── Transports
│ ├── Base.php
│ └── TransportInterface.php
├── README.md
├── Setup
├── InstallSchema.php
└── UpgradeSchema.php
├── composer.json
├── docs
├── enhanced-magento2-customer-detail-menu.png
├── local-storeage-for-mails-as-json.png
├── magento2-config-mail-enhancement.png
├── magento2-customer-mail-transactional-review-detail.png
├── magento2-customer-mail-transactional-review-with-mail.png
├── magento2-customer-mail-transactional-review.png
├── magento2-customer-menu.png
├── magento2-guest-email-review-and-resending.jpg
├── magento2-mail-resending-and-recalculation.png
└── magento2-mail-stored-as-json.png
├── etc
├── acl.xml
├── adminhtml
│ ├── menu.xml
│ ├── routes.xml
│ └── system.xml
├── cache.xml
├── config.xml
├── di.xml
└── module.xml
├── i18n
└── en_US.csv
├── registration.php
└── view
└── adminhtml
├── layout
├── customer_index_edit.xml
├── customer_index_mail.xml
├── customer_mail_edit.xml
├── customer_mail_overview.xml
├── customer_mail_send.xml
└── overview_block.xml
└── templates
└── customer
├── edit
└── tab
│ └── mail.phtml
└── mail
├── edit
├── info.phtml
└── view.phtml
└── send
└── form.phtml
/.gitignore:
--------------------------------------------------------------------------------
1 | /.project
2 | /.idea
3 | *~
4 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Edit/Tab/Mail/Grid/Renderer/Action.php:
--------------------------------------------------------------------------------
1 | getUrl('customer/mail/edit', array('id' => $row->getId()));
22 | return "View";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Edit/Tab/Mail/Grid/Renderer/Customer.php:
--------------------------------------------------------------------------------
1 | getData($this->getColumn()->getIndex());
22 | if($customerId > 0) {
23 | return '' . $customerId . '';
24 | }
25 |
26 | return nl2br(htmlspecialchars($row->getData($this->getColumn()->getIndex())));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Edit/Tab/Mail/Grid/Renderer/Recipients.php:
--------------------------------------------------------------------------------
1 | getData($this->getColumn()->getIndex()), true);
22 | if(!empty($recipients))
23 | {
24 | $recipientsString = htmlspecialchars(implode(', ', $recipients));
25 | if(strlen($recipientsString) < 15)
26 | {
27 | return $recipientsString;
28 | }
29 |
30 | return substr($recipientsString, 0, 12) . '...';
31 | }
32 |
33 | return '';
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Edit/Tab/Mail/Grid/Renderer/Sent.php:
--------------------------------------------------------------------------------
1 | getData($this->getColumn()->getIndex()) > 0) ? __('yes') : __('no');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Edit/Tab/Mail/Grid/Renderer/Template.php:
--------------------------------------------------------------------------------
1 | getData($this->getColumn()->getIndex())));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Grid.php:
--------------------------------------------------------------------------------
1 |
19 | */
20 | namespace Shockwavemk\Mail\Base\Block\Adminhtml;
21 |
22 | class Grid extends \Magento\Backend\Block\Widget\Grid\Extended
23 | {
24 | /**
25 | * Review action pager
26 | *
27 | * @var \Magento\Review\Helper\Action\Pager
28 | */
29 | protected $_reviewActionPager = null;
30 |
31 | /**
32 | * Review data
33 | *
34 | * @var \Magento\Review\Helper\Data
35 | */
36 | protected $_reviewData = null;
37 |
38 | /**
39 | * Core registry
40 | *
41 | * @var \Magento\Framework\Registry
42 | */
43 | protected $_coreRegistry = null;
44 |
45 | /**
46 | * Review collection model factory
47 | *
48 | * @var \Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory
49 | */
50 | protected $_productsFactory;
51 |
52 | /**
53 | * Review model factory
54 | *
55 | * @var \Magento\Review\Model\ReviewFactory
56 | */
57 | protected $_reviewFactory;
58 | /**
59 | * @var \Shockwavemk\Mail\Base\Model\ResourceModel\Mail\Collection
60 | */
61 | private $collection;
62 |
63 | /**
64 | * @param \Magento\Backend\Block\Template\Context $context
65 | * @param \Magento\Backend\Helper\Data $backendHelper
66 | * @param \Magento\Framework\Registry $coreRegistry
67 | * @param \Shockwavemk\Mail\Base\Model\ResourceModel\Mail\Collection $collection
68 | * @param array $data
69 | */
70 | public function __construct(
71 | \Magento\Backend\Block\Template\Context $context,
72 | \Magento\Backend\Helper\Data $backendHelper,
73 | \Magento\Framework\Registry $coreRegistry,
74 | \Shockwavemk\Mail\Base\Model\ResourceModel\Mail\Collection $collection,
75 | array $data = []
76 | ) {
77 | $this->_coreRegistry = $coreRegistry;
78 | $this->_collection = $collection;
79 | parent::__construct($context, $backendHelper, $data);
80 | }
81 |
82 | /**
83 | * Initialize grid
84 | *
85 | * @return void
86 | */
87 | protected function _construct()
88 | {
89 | parent::_construct();
90 | $this->setId('mailGrid');
91 | $this->setDefaultSort('sent_at');
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Mail/Edit/Form.php:
--------------------------------------------------------------------------------
1 | coreRegistry = $registry;
140 | $this->accountManagement = $accountManagement;
141 | $this->groupRepository = $groupRepository;
142 | $this->customerDataFactory = $customerDataFactory;
143 | $this->addressHelper = $addressHelper;
144 | $this->dateTime = $dateTime;
145 | $this->addressMapper = $addressMapper;
146 | $this->dataObjectHelper = $dataObjectHelper;
147 | $this->customerLogger = $customerLogger;
148 | $this->manager = $manager;
149 |
150 | $mailId = $this->_request->getParam('id');
151 | $this->_mail = $manager->get('\Shockwavemk\Mail\Base\Model\Mail');
152 | $this->_mail->load($mailId);
153 | }
154 |
155 | public function getMail()
156 | {
157 | return $this->_mail;
158 | }
159 |
160 | public function getStoreName()
161 | {
162 | return $this->_storeManager->getStore($this->_mail->getStoreId())->getName();
163 | }
164 |
165 | public function htmlEscape($text)
166 | {
167 | return $text;
168 | }
169 |
170 | public function getDownloadUrl(\Shockwavemk\Mail\Base\Model\Mail\Attachment $attachment)
171 | {
172 | return $this->getUrl('customer/mail/attachment',
173 | array(
174 | 'id' => base64_encode($this->getMail()->getId()),
175 | 'path' => base64_encode($attachment->getFilePath())
176 | )
177 | );
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Mail/Edit/View.php:
--------------------------------------------------------------------------------
1 | _blockGroup = 'Shockwavemk_Mail_Base';
36 | $this->_controller = 'adminhtml_mail';
37 | $this->_mode = 'edit';
38 | $this->_request = $context->getRequest();
39 | $mailId = $this->_request->getParam('id');
40 | $this->_mail = $manager->get('\Shockwavemk\Mail\Base\Model\Mail');
41 | $this->_mail->load($mailId);
42 |
43 | parent::__construct($context, $data);
44 | }
45 |
46 | /**
47 | * Prepare the layout.
48 | *
49 | * @return $this
50 | */
51 | protected function _prepareLayout()
52 | {
53 | $this->buttonList->remove('save');
54 | $this->buttonList->remove('delete');
55 | $this->buttonList->remove('reset');
56 |
57 | $this->buttonList->add(
58 | 'send',
59 | [
60 | 'label' => __('Send mail ...'),
61 | 'onclick' => "setLocation('{$this->getUrl('*/*/send',
62 | ['id' => $this->_mail->getId()]
63 | )}')",
64 | 'class' => 'task'
65 | ]
66 | );
67 |
68 | $this->buttonList->add(
69 | 'send_post',
70 | [
71 | 'label' => __('Resend mail'),
72 | 'onclick' => "setLocation('{$this->getUrl('*/*/sendPost',
73 | [
74 | 'id' => $this->_mail->getId()
75 | ])}')",
76 | 'class' => 'task'
77 | ]
78 | );
79 |
80 | return parent::_prepareLayout();
81 | }
82 |
83 | /**
84 | * Get URL for back (reset) button
85 | *
86 | * @return string
87 | */
88 | public function getBackUrl()
89 | {
90 | if(!empty($this->_mail->getCustomerId())) {
91 | return $this->getUrl('customer/index/edit', array('id' => $this->_mail->getCustomerId()));
92 | }
93 |
94 | return $this->getUrl('index/index');
95 | }
96 |
97 | public function getMail()
98 | {
99 | return $this->_mail;
100 | }
101 |
102 | public function getUnstructuredData()
103 | {
104 | return
105 | "
Variables
{$this->getVarsOutput()}" .
106 | "Recipient variables
{$this->getRecipientVariablesOutput()}" .
107 | "Options
{$this->getOptionsOutput()}";
108 | }
109 |
110 | /**
111 | * TODO
112 | *
113 | * @param $key
114 | * @param $value
115 | * @return array
116 | */
117 | public function getCustomerRepresentation($key, $value)
118 | {
119 | /** @var \Magento\Customer\Model\Customer $value */
120 | $url = $this->getUrl('customer/index/edit', array('id' => $value->getId()));
121 | return "{$key} (customer): {$value->getFirstname()} {$value->getLastname()}";
122 | }
123 |
124 | /**
125 | * TODO
126 | *
127 | * @param $key
128 | * @param $value
129 | * @return array
130 | */
131 | public function getOrderRepresentation($key, $value)
132 | {
133 | /** @var \Magento\Customer\Model\Customer $value */
134 | $url = $this->getUrl('sales/order/view', array('order_id' => $value->getId()));
135 | return "{$key} (order): {$value->getIncrementId()}";
136 | }
137 |
138 | /**
139 | * @param $key
140 | * @param $value
141 | * @return string
142 | */
143 | public function getLinkRepresentation($key, $value)
144 | {
145 | return "{$key} (link): {$value}";
146 | }
147 |
148 | /**
149 | * @param $key
150 | * @param $value
151 | * @return string
152 | */
153 | public function getStoreRepresentation($key, $value)
154 | {
155 | /** @var \Magento\Store\Model\Store $value */
156 | return "{$key} (store): {$value->getName()}";
157 | }
158 |
159 | /**
160 | * TODO
161 | *
162 | * @param $key
163 | * @param $value
164 | * @return array
165 | */
166 | public function getShipmentRepresentation($key, $value)
167 | {
168 | /** @var \Magento\Customer\Model\Customer $value */
169 | $url = $this->getUrl('sales/shipment/view', array('shipment_id' => $value->getId()));
170 | return "{$key} (shipment): {$value->getIncrementId()}";
171 | }
172 |
173 | /**
174 | * TODO
175 | *
176 | * @param $key
177 | * @param $value
178 | * @return array
179 | */
180 | public function getInvoiceRepresentation($key, $value)
181 | {
182 | /** @var \Magento\Customer\Model\Customer $value */
183 | $url = $this->getUrl('sales/invoice/view', array('invoice_id' => $value->getId()));
184 | return "{$key} (invoice): {$value->getIncrementId()}";
185 | }
186 |
187 | /**
188 | * @param $key
189 | * @param $variable
190 | * @return string
191 | */
192 | public function getVarOutput($key, $variable)
193 | {
194 | try
195 | {
196 | /** @var \Magento\Framework\Model\AbstractModel $variable */
197 | if (is_subclass_of($variable, '\Magento\Framework\Model\AbstractModel') && $variable->getEntityType() == 'customer') {
198 | return $this->getCustomerRepresentation($key, $variable);
199 |
200 | } elseif (is_subclass_of($variable, '\Magento\Framework\Model\AbstractModel') && $variable->getEntityType() == 'order') {
201 | return $this->getOrderRepresentation($key, $variable);
202 |
203 | } elseif (is_subclass_of($variable, '\Magento\Framework\Model\AbstractModel') && $variable->getEntityType() == 'shipment') {
204 | return $this->getShipmentRepresentation($key, $variable);
205 |
206 | } elseif (is_subclass_of($variable, '\Magento\Framework\Model\AbstractModel') && $variable->getEntityType() == 'invoice') {
207 | return $this->getInvoiceRepresentation($key, $variable);
208 |
209 | } elseif (is_subclass_of($variable, 'Magento\Framework\Model\AbstractModel')) {
210 | return $key . ' : ' . json_encode($variable->getData(), JSON_PRETTY_PRINT);
211 |
212 | } elseif (is_subclass_of($variable, '\Magento\Eav\Model\Entity\Collection\AbstractCollection')) {
213 | /** @var \Magento\Eav\Model\Entity\Collection\AbstractCollection $variable*/
214 | return $key . ' : ' . json_encode($variable->getData(), JSON_PRETTY_PRINT);
215 |
216 | } elseif (!filter_var($variable, FILTER_VALIDATE_URL) === false) {
217 | return $this->getLinkRepresentation($key, $variable);
218 |
219 | } elseif (is_string($variable)) {
220 | return $key . ' : ' . $variable;
221 | }
222 | else
223 | {
224 | return $key . ' : ' . json_encode($variable, JSON_PRETTY_PRINT);
225 | }
226 | }
227 | catch (\Exception $e)
228 | {
229 | $variable = 'variable can not be displayed';
230 | }
231 |
232 | return $key . ' : ' . $variable;
233 | }
234 |
235 | /**
236 | * @return string
237 | */
238 | public function getVarsOutput()
239 | {
240 | $output = [];
241 | $vars = $this->_mail->getVars();
242 | if (!empty($vars)) {
243 | foreach ($vars as $key => $value) {
244 | $output[] = $this->getVarOutput($key, $value);
245 | }
246 | }
247 | return implode(',
', $output);
248 | }
249 |
250 | /**
251 | * @return string
252 | */
253 | public function getRecipientVariablesOutput()
254 | {
255 | $output = [];
256 | $recipientVariables = $this->_mail->getRecipientVariables();
257 | if (!empty($recipientVariables)) {
258 | $output[] = $recipientVariables;
259 | }
260 | return implode(',
', $output);
261 | }
262 |
263 | /**
264 | * @return string
265 | */
266 | public function getOptionsOutput()
267 | {
268 | $output = [];
269 | $options = $this->_mail->getOptions();
270 | if (!empty($options)) {
271 | foreach ($options as $key => $value) {
272 | $output[] = $this->getVarOutput($key, $value);
273 | }
274 | }
275 | return implode(',
', $output);
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Mail/Send/Form.php:
--------------------------------------------------------------------------------
1 | _request = $context->getRequest();
30 | $mailId = $this->_request->getParam('id');
31 | $this->_mail = $manager->get('\Shockwavemk\Mail\Base\Model\Mail');
32 | $this->_mail->load($mailId);
33 |
34 | parent::__construct($context, $data);
35 | }
36 |
37 | public function getMail()
38 | {
39 | return $this->_mail;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Block/Adminhtml/MailOverview.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class MailOverview extends \Magento\Backend\Block\Widget\Grid\Container
14 | {
15 | /**
16 | * @return void
17 | */
18 | protected function _construct()
19 | {
20 | $this->_controller = 'adminhtml';
21 | $this->_blockGroup = 'Shockwavemk_Mail_Base';
22 | $this->_headerText = __('Review Transactional Mails');
23 |
24 | parent::_construct();
25 | }
26 |
27 | /**
28 | * Create "New" button
29 | *
30 | * @return void
31 | */
32 | protected function _addNewButton()
33 | {
34 | // do nothing
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Block/Adminhtml/MailTab.php:
--------------------------------------------------------------------------------
1 | coreRegistry = $registry;
41 | parent::__construct($context, $data);
42 | }
43 |
44 | /**
45 | * @inheritdoc
46 | */
47 | public function canShowTab()
48 | {
49 | return $this->coreRegistry->registry(RegistryConstants::CURRENT_CUSTOMER_ID);
50 | }
51 |
52 | /**
53 | * Return Tab label
54 | *
55 | * @return \Magento\Framework\Phrase
56 | */
57 | public function getTabLabel()
58 | {
59 | return __('Mail');
60 | }
61 |
62 | /**
63 | * Return URL link to Tab content
64 | *
65 | * @return string
66 | */
67 | public function getTabUrl()
68 | {
69 | return $this->getUrl('customer/*/mail', ['_current' => true]);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Block/Adminhtml/Widget/Grid/Column/Filter/Text.php:
--------------------------------------------------------------------------------
1 | $this->getValue()];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | ## [unreleased]
6 |
7 | ## [v1.1.0] - 2016-10-10
8 |
9 | ### Added
10 | - Guest/customer mail review and resending grid
11 |
12 | ### Fixed
13 | - Fix resending: In case only one recipient is available, no implode
14 | several email addresses must take place.
15 |
16 | ## [v1.0.6] - 2016-09-05
17 | ### Fixed
18 | - On loop mail model is now reset on transport creation
19 |
20 | ## [v1.0.5] - 2016-09-05
21 | ### Fixed
22 | - SMTP attachment encoding default is now set to base64 if not given
23 |
24 | ## [v1.0.3] - 2016-08-30
25 | ### Fixed
26 | - Handover stroreid to get from name and mail for specific store
27 |
28 | ## [v1.0.2] - 2016-08-30
29 | ### Fixed
30 | - Bugfix for RMA resending
31 |
32 | ## [v1.0.0] - 2016-08-25
33 |
34 | ### Added
35 | - Apache2 license
36 | - First working adminhtml customer mail tracking
37 |
38 | ## Fixed
39 | - Multi-language cron scheduled mails
40 |
41 | ### Changed
42 | - Rename all "storeage" to "storage"
43 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Index/Mail.php:
--------------------------------------------------------------------------------
1 | initCurrentCustomer();
18 |
19 | return $this->resultLayoutFactory->create();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail.php:
--------------------------------------------------------------------------------
1 |
10 | */
11 | namespace Shockwavemk\Mail\Base\Controller\Adminhtml;
12 |
13 | abstract class Mail extends \Magento\Backend\App\Action
14 | {
15 | /**
16 | * Check is allowed access
17 | *
18 | * @return bool
19 | */
20 | protected function _isAllowed()
21 | {
22 | return true; // $this->_authorization->isAllowed('Magento_Newsletter::template');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail/Attachment.php:
--------------------------------------------------------------------------------
1 | _request->getParam('id');
21 |
22 | /** @var \Shockwavemk\Mail\Base\Model\Mail $_mail */
23 | $mail = $this->_objectManager->get('\Shockwavemk\Mail\Base\Model\Mail');
24 | $mail->load(base64_decode($encodedId));
25 |
26 | $encodedPath = $this->_request->getParam('path');
27 | $localPath = base64_decode($encodedPath);
28 |
29 | /** @var \Shockwavemk\Mail\Base\Model\Mail\Attachment[] $attachments */
30 | $attachments = $mail->getAttachments();
31 |
32 | /** @var \Shockwavemk\Mail\Base\Model\Mail\Attachment $attachment */
33 | /** @noinspection IsEmptyFunctionUsageInspection */
34 | if(empty($attachment = $attachments[$localPath])) {
35 |
36 | $this->messageManager->addError(
37 | __('Sorry, there was an error getting requested content. Please contact the store owner.')
38 | );
39 |
40 | return $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl());
41 | }
42 |
43 | try
44 | {
45 | $this->_processDownload($attachment);
46 | exit(0);
47 | }
48 | catch (\Exception $e)
49 | {
50 | $this->messageManager->addError(
51 | __('Sorry, there was an error getting requested content. Please contact the store owner: ' . $e->getMessage())
52 | );
53 | }
54 |
55 | return $this->getResponse()->setRedirect($this->_redirect->getRedirectUrl());
56 | }
57 |
58 | /**
59 | * @param \Shockwavemk\Mail\Base\Model\Mail\Attachment $attachment
60 | */
61 | protected function _processDownload($attachment)
62 | {
63 | /** @var \Magento\Framework\App\ResponseInterface $response */
64 | $response = $this->getResponse();
65 |
66 | $response->setHttpResponseCode(
67 | 200
68 | )->setHeader(
69 | 'Pragma',
70 | 'public',
71 | true
72 | )->setHeader(
73 | 'Cache-Control',
74 | 'must-revalidate, post-check=0, pre-check=0',
75 | true
76 | )->setHeader(
77 | 'Content-type',
78 | $attachment->getFileType(),
79 | true
80 | );
81 |
82 | $this->getResponse()->setHeader('Content-Disposition', $attachment->getDisposition() . '; filename=' . basename($attachment->getFileName()));
83 |
84 | $this->getResponse()->clearBody();
85 | $this->getResponse()->sendHeaders();
86 |
87 | echo $attachment->getBinary();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail/Edit.php:
--------------------------------------------------------------------------------
1 | _request->getParam('id');
19 | $this->_mail = $this->_objectManager->get('\Shockwavemk\Mail\Base\Model\Mail');
20 | $this->_mail->load($mailId);
21 |
22 | $this->_view->loadLayout();
23 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__($this->_mail->getSubject()));
24 | $this->_view->renderLayout();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail/Overview.php:
--------------------------------------------------------------------------------
1 | _view->loadLayout();
19 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Transactional mails'));
20 | $this->_view->renderLayout();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail/Send.php:
--------------------------------------------------------------------------------
1 | _request->getParam('id');
19 | $this->_mail = $this->_objectManager->get('\Shockwavemk\Mail\Base\Model\Mail');
20 | $this->_mail->load($mailId);
21 |
22 | $this->_view->loadLayout();
23 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__($this->_mail->getSubject()));
24 | $this->_view->renderLayout();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Mail/SendPost.php:
--------------------------------------------------------------------------------
1 | transportBuilder = $transportBuilder;
39 | $this->storeManager = $storeManager;
40 | $this->customer = $customer;
41 |
42 | parent::__construct($context);
43 | }
44 |
45 | /**
46 | * Resend mail
47 | *
48 | * @return void|$this
49 | */
50 | public function execute()
51 | {
52 | // Get request data
53 | $mailId = $this->_request->getParam('id');
54 | $recalculate = $this->_request->getParam('resend_type');
55 |
56 | // Email address to re-send mail
57 | $email = $this->_request->getParam('email');
58 |
59 | /** @var \Shockwavemk\Mail\Base\Model\Mail $mail */
60 | $mail = $this->_objectManager->get('Shockwavemk\Mail\Base\Model\Mail');
61 | $mail->load($mailId);
62 |
63 | /** @noinspection IsEmptyFunctionUsageInspection */
64 | if(empty($mailId) || empty($mail->getId())) {
65 |
66 | $redirectUrl = $this->_buildUrl(
67 | 'customer/mail/edit',
68 | ['_secure' => true, 'id' => $mailId]
69 | );
70 |
71 | $this->messageManager->addException(new \Exception(
72 | __('Mail can not be loaded.')),
73 | __('Mail can not be loaded.')
74 | );
75 |
76 | return $this->resultRedirectFactory
77 | ->create()
78 | ->setUrl(
79 | $this->_redirect->error($redirectUrl)
80 | );
81 | }
82 |
83 | try
84 | {
85 | // Get information of parent mail
86 |
87 | /** @var MessageInterface $parentMessage */
88 | $parentMessage = $mail->getMessage();
89 |
90 | /** @var AttachmentInterface[] $parentAttachments */
91 | $parentAttachments = $mail->getAttachments();
92 |
93 | $parentCustomerId = $mail->getCustomerId();
94 |
95 | // Set parent id to allow parent links in frontend
96 | $mail->setParentId(
97 | $mail->getId()
98 | );
99 |
100 | // On given $email
101 | if(!empty($email)) {
102 | $recipients = [$email];
103 | } else {
104 | $recipients = $mail->getRecipients();
105 | }
106 |
107 | // Derive a transportBuilder from existing Mail
108 | $transportBuilder = $this->deriveTransportBuilderFromExistingMail($mail);
109 |
110 | $this->applyRecipientsOnTransportBuilder($recipients, $transportBuilder);
111 |
112 |
113 | /** @noinspection IsEmptyFunctionUsageInspection */
114 | if(!empty($recalculate) && $recalculate === 'recalculate') {
115 | $transport = $transportBuilder->getTransport();
116 | } else {
117 | $transport = $transportBuilder->getBackupTransport($parentMessage);
118 | }
119 |
120 | $transport->getMail()
121 | ->setAttachments($parentAttachments)
122 | ->setCustomerId($parentCustomerId)
123 | ;
124 |
125 | /** @var \Shockwavemk\Mail\Base\Model\Transports\TransportInterface $transport */
126 | $transport->sendMessage();
127 |
128 | $this->messageManager->addSuccess(__('Mail re-sent to customer.'));
129 |
130 | $url = $this->_buildUrl(
131 | 'customer/mail/edit',
132 | [
133 | '_secure' => true,
134 | 'id' => $transport->getMail()->getId()
135 | ]
136 | );
137 |
138 | return $this->resultRedirectFactory->create()->setUrl($this->_redirect->success($url));
139 |
140 | }
141 | catch (InputException $e)
142 | {
143 |
144 | $this->messageManager->addError($e->getMessage());
145 |
146 | foreach ($e->getErrors() as $error) {
147 |
148 | $this->messageManager->addError(
149 | $error->getMessage()
150 | );
151 |
152 | }
153 |
154 | }
155 | catch (\Exception $e)
156 | {
157 |
158 | $this->messageManager->addException(
159 | $e,
160 | __($e->getMessage())
161 | );
162 |
163 | }
164 |
165 | $redirectUrl = $this->_buildUrl(
166 | 'customer/mail/send',
167 | ['_secure' => true, 'id' => $mailId]
168 | );
169 |
170 | return $this->resultRedirectFactory->create()->setUrl(
171 | $this->_redirect->error($redirectUrl)
172 | );
173 | }
174 |
175 | /**
176 | * @param string $route
177 | * @param array $params
178 | * @return string
179 | */
180 | protected function _buildUrl($route = '', $params = [])
181 | {
182 | /** @var \Magento\Framework\UrlInterface $urlBuilder */
183 | $urlBuilder = $this->_objectManager->create('Magento\Framework\UrlInterface');
184 | return $urlBuilder->getUrl($route, $params);
185 | }
186 |
187 | /**
188 | * TODO
189 | *
190 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
191 | *
192 | * @return TransportBuilder
193 | * @throws \Magento\Framework\Exception\MailException
194 | */
195 | protected function deriveTransportBuilderFromExistingMail($mail)
196 | {
197 | return $this->transportBuilder
198 | ->setTemplateIdentifier($mail->getTemplateIdentifier())
199 | ->setTemplateOptions(['area' => Area::AREA_FRONTEND, 'store' => $mail->getStoreId()])
200 | ->setTemplateVars($mail->getVars())
201 | ->setFrom($mail->getSenderMail());
202 | }
203 |
204 | /**
205 | * @param $recipients
206 | * @param $transportBuilder
207 | */
208 | protected function applyRecipientsOnTransportBuilder($recipients, $transportBuilder)
209 | {
210 | // Some times magento does not save recipients as array
211 | if (is_array($recipients)) {
212 | foreach ($recipients as $recipient) {
213 | $transportBuilder->addTo(
214 | $recipient
215 | );
216 | }
217 | } else {
218 | $transportBuilder->addTo(
219 | $recipients
220 | );
221 | }
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/Model/Config.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $scopeConfig;
72 | }
73 |
74 | // Transport
75 |
76 | /**
77 | * @return mixed
78 | */
79 | public function getTransportType()
80 | {
81 | return $this->scopeConfig->getValue(self::XML_PATH_TYPE);
82 | }
83 |
84 | /**
85 | * @return mixed
86 | */
87 | public function getTransportTypes()
88 | {
89 | return $this->scopeConfig->getValue(self::XML_PATH_TRANSPORT_TYPES);
90 | }
91 |
92 | /**
93 | * @return null
94 | */
95 | public function getTransportClassName()
96 | {
97 | $className = null;
98 |
99 | $type = $this->getTransportType();
100 | $types = $this->getTransportTypes();
101 |
102 | if(!empty($types[$type]))
103 | {
104 | $typeConfig = $types[$type];
105 |
106 | $className = $typeConfig['class'];
107 | }
108 |
109 | return $className;
110 | }
111 |
112 | // Storage
113 |
114 | /**
115 | * This path is used to store (temporary) files on local server
116 | * e.g. /pub/media/emails
117 | *
118 | * @return mixed
119 | */
120 | public function getHostSpoolerFolderPath()
121 | {
122 | return $this->scopeConfig->getValue(self::XML_PATH_SPOOLER_FOLDER_PATH);
123 | }
124 |
125 | /**
126 | * A configurable limit how often a write access is tried on local file system on write
127 | *
128 | * @return mixed
129 | */
130 | public function getHostRetryLimit()
131 | {
132 | return $this->scopeConfig->getValue(self::XML_PATH_SPOOLER_RETRY_LIMIT);
133 | }
134 |
135 | /**
136 | * Returns string class name of storage class
137 | *
138 | * @return string
139 | */
140 | public function getStorageType()
141 | {
142 | return $this->scopeConfig->getValue(self::XML_PATH_STORAGE);
143 | }
144 |
145 | /**
146 | * @return string[]
147 | */
148 | public function getStorageTypes()
149 | {
150 | return $this->scopeConfig->getValue(self::XML_PATH_STORAGE_TYPES);
151 | }
152 |
153 | /**
154 | * @return null
155 | */
156 | public function getStorageClassName()
157 | {
158 | $type = $this->getStorageType();
159 | $types = $this->getStorageTypes();
160 |
161 | if(empty($typeConfig = $types[$type]))
162 | {
163 | return null;
164 | }
165 |
166 | return $typeConfig['class'];
167 | }
168 |
169 | /**
170 | * If test mode enabled, emails are built and stored, not sent
171 | *
172 | * @return bool
173 | */
174 | public function getTestMode()
175 | {
176 | return $this->scopeConfig->getValue(self::XML_PATH_MAIL_TEST_MODE);
177 | }
178 |
179 | /**
180 | * Depending on mail transport vendor, all links in outgoing messages are replaced by tracking links
181 | *
182 | * @return bool
183 | */
184 | public function getTrackingEnabled()
185 | {
186 | return $this->scopeConfig->getValue(self::XML_PATH_MAIL_TRACKING_ENABLED);
187 | }
188 |
189 | /**
190 | * @return bool
191 | */
192 | public function getTrackingClicksEnabled()
193 | {
194 | return $this->scopeConfig->getValue(self::XML_PATH_MAIL_TRACKING_CLICKS_ENABLED);
195 | }
196 |
197 | /**
198 | * @return bool
199 | */
200 | public function getTrackingOpensEnabled()
201 | {
202 | return $this->scopeConfig->getValue(self::XML_PATH_MAIL_TRACKING_OPENS_ENABLED);
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/Model/Config/Source/Storage.php:
--------------------------------------------------------------------------------
1 | config = $config;
19 | }
20 |
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function toOptionArray()
25 | {
26 | $selection = array();
27 | foreach ($this->config->getStorageTypes() as $storageType)
28 | {
29 | $selection[] = [
30 | 'label' => __($storageType['label']),
31 | 'value' => $storageType['value']
32 | ];
33 | }
34 |
35 | return $selection;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Model/Config/Source/Type.php:
--------------------------------------------------------------------------------
1 | config = $config;
19 | }
20 |
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function toOptionArray()
25 | {
26 | if(empty($this->config->getTransportTypes()))
27 | {
28 | return [
29 | ['label' => __('Disabled'), 'value' => 'disabled']
30 | ];
31 | }
32 |
33 | $selection = array();
34 | foreach ($this->config->getTransportTypes() as $transportType)
35 | {
36 | $selection[] = ['label' => __($transportType['label']), 'value' => $transportType['value']];
37 | }
38 |
39 | return $selection;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Model/Mail.php:
--------------------------------------------------------------------------------
1 | _storeManager = $storeManager;
172 | $this->_date = $date;
173 | $this->_mathRandom = $mathRandom;
174 | $this->_dateTime = $dateTime;
175 | $this->_manager = $manager;
176 | $this->_config = $config;
177 | $this->_storage = $storage;
178 | $this->_timeZone = $timezone;
179 |
180 | parent::__construct($context, $registry, $resource, $resourceCollection, $data);
181 | }
182 |
183 | /**
184 | * @return Storages\Base
185 | */
186 | public function getstorage()
187 | {
188 | return $this->_storage;
189 | }
190 |
191 | /**
192 | * Every mailing is associated to a 'real' message
193 | *
194 | * @return \Shockwavemk\Mail\Base\Model\Mail\Message|null
195 | * @throws \Magento\Framework\Exception\MailException
196 | */
197 | public function getMessage()
198 | {
199 | /** @noinspection IsEmptyFunctionUsageInspection */
200 | /** @noinspection PhpUndefinedMethodInspection */
201 | if (!empty(parent::getMessage())) {
202 | /** @noinspection PhpUndefinedMethodInspection */
203 | return parent::getMessage();
204 | }
205 |
206 | return $this->_storage->loadMessage(
207 | $this
208 | );
209 | }
210 |
211 | /**
212 | * @param \Shockwavemk\Mail\Base\Model\Mail\AttachmentInterface $attachment
213 | * @throws \Magento\Framework\Exception\MailException
214 | */
215 | public function addAttachment($attachment)
216 | {
217 | /** @var \Shockwavemk\Mail\Base\Model\Mail\AttachmentInterface[] $currentAttachments */
218 | $attachments = $this->getAttachments();
219 | $attachment->setMail($this);
220 |
221 | $attachments[$attachment->getFileName()] = $attachment;
222 |
223 | /** @noinspection PhpUndefinedMethodInspection */
224 | $this->setAttachments($attachments);
225 | }
226 |
227 | /**
228 | * A mail can have zero to n associated attachments.
229 | * An Attachment is a meta description to get information and access to binary data
230 | *
231 | * @return \Shockwavemk\Mail\Base\Model\Mail\Attachment[] Attachments
232 | * @throws \Magento\Framework\Exception\MailException
233 | */
234 | public function getAttachments()
235 | {
236 | /** @noinspection IsEmptyFunctionUsageInspection */
237 | /** @noinspection PhpUndefinedMethodInspection */
238 | if (!empty(parent::getAttachments())) {
239 | /** @noinspection PhpUndefinedMethodInspection */
240 | return parent::getAttachments();
241 | }
242 |
243 | $attachments = $this->_storage->getAttachments($this);
244 |
245 | /** @noinspection PhpUndefinedMethodInspection */
246 | $this->setAttachments($attachments);
247 |
248 | return $attachments;
249 | }
250 |
251 | /**
252 | * Get the recipients as string array
253 | * Recipients are stored as json in database
254 | *
255 | * @return string[]
256 | */
257 | public function getRecipients()
258 | {
259 | $value = $this->getData('recipients');
260 | return json_decode($value, true);
261 | }
262 |
263 | /**
264 | * @param array $value
265 | * @return \Shockwavemk\Mail\Base\Model\Mail
266 | */
267 | public function setSenderMail(array $value)
268 | {
269 | $value = json_encode($value);
270 | $this->setData('sender_mail', $value);
271 | return $this;
272 | }
273 |
274 | /**
275 | * @return mixed
276 | */
277 | public function getSenderMail()
278 | {
279 | $value = $this->getData('sender_mail');
280 | /** @noinspection IsEmptyFunctionUsageInspection */
281 | if (!empty($decoded = json_decode($value, true))) {
282 | return $decoded;
283 | }
284 | return '';
285 | }
286 |
287 | /**
288 | * @param array $value
289 | * @return $this
290 | */
291 | public function setVars(array $value)
292 | {
293 | /** @noinspection IsEmptyFunctionUsageInspection */
294 | if (!empty($value)) {
295 | $value = $this->convertMagentoModelsToPointer($value);
296 | }
297 |
298 | $value = json_encode($value);
299 | $this->setData('vars', $value);
300 | return $this;
301 | }
302 |
303 | /**
304 | * @param $mailValues
305 | * @return array
306 | */
307 | protected function convertMagentoModelsToPointer($mailValues)
308 | {
309 | $newMailValues = [];
310 |
311 | foreach ($mailValues as $key => $value) {
312 | if (is_subclass_of($value, 'Magento\Framework\Model\AbstractModel')) {
313 | /** @var \Magento\Framework\Model\AbstractModel $value */
314 | $value = array(
315 | 'entity_id' => $value->getId(),
316 | 'class' => get_class($value)
317 | );
318 | }
319 | /** @noinspection IsEmptyFunctionUsageInspection */
320 | elseif(method_exists($value, 'getData') && !empty($value->getData())) {
321 | $valueData = $value->getData();
322 |
323 | /** @noinspection IsEmptyFunctionUsageInspection */
324 | if(method_exists($value, 'getId') && !empty($value->getId())) {
325 | $valueData['entity_id'] = $value->getId();
326 | }
327 |
328 | $valueData['class'] = get_class($value);
329 | $value = $valueData;
330 | }
331 |
332 | $newMailValues[$key] = $value;
333 | }
334 |
335 | return $newMailValues;
336 | }
337 |
338 | /**
339 | * @return array|mixed
340 | */
341 | public function getVars()
342 | {
343 | $value = $this->getData('vars');
344 |
345 | /** @noinspection IsEmptyFunctionUsageInspection */
346 | if (!empty($value)) {
347 | $value = json_decode($value, true);
348 | $value = $this->convertPointerToMagentoModels($value);
349 | }
350 |
351 | return $value;
352 | }
353 |
354 | /**
355 | * Recover magento models from stored class/ids tuple
356 | *
357 | * @param $mailValues
358 | * @return \Magento\Framework\Model\AbstractModel[]|array
359 | * @throws \Magento\Framework\Exception\LocalizedException
360 | */
361 | protected function convertPointerToMagentoModels($mailValues)
362 | {
363 | $newMailValues = [];
364 |
365 | foreach ($mailValues as $key => $value) {
366 | if (is_array($value)
367 | && !empty($value['class']) && !empty($value['entity_id'])
368 | ) {
369 | $newMailValues[$key] = $this->createAbstractModelFromValue($value);
370 | }
371 | /** @noinspection NotOptimalIfConditionsInspection */
372 | elseif(is_array($value) && !empty($value['class'])) {
373 | $newMailValues[$key] = $this->createObjectFromValue($value);
374 | }
375 | else {
376 | $newMailValues[$key] = $value;
377 | }
378 | }
379 |
380 | return $newMailValues;
381 | }
382 |
383 | /**
384 | * Options are stored as json string in database
385 | *
386 | * @param array $value
387 | * @return $this
388 | */
389 | public function setOptions(array $value)
390 | {
391 | $value = json_encode($value);
392 | $this->setData('options', $value);
393 | return $this;
394 | }
395 |
396 | /**
397 | * Options are stored as json string in database
398 | *
399 | * @return mixed
400 | */
401 | public function getOptions()
402 | {
403 | $value = $this->getData('options');
404 | return json_decode($value, true);
405 | }
406 |
407 | /**
408 | * Set tags used for mail
409 | * Tags are stored as json string in database
410 | *
411 | * @param $value
412 | * @return $this
413 | */
414 | public function setTags($value)
415 | {
416 | $value = json_encode($value);
417 | $this->setData('tags', $value);
418 | return $this;
419 | }
420 |
421 | /**
422 | * Get tags used for mail
423 | * Tags are stored as json string in database
424 | *
425 | * @return mixed
426 | */
427 | public function getTags()
428 | {
429 | $value = $this->getData('tags');
430 | /** @noinspection IsEmptyFunctionUsageInspection */
431 | if (!empty($decoded = json_decode($value, true))) {
432 | return $decoded;
433 | }
434 | return [];
435 | }
436 |
437 | /**
438 | * Update mail data with information created while transport init
439 | *
440 | * @param \Shockwavemk\Mail\Base\Model\Transports\TransportInterface $transport
441 | * @return \Shockwavemk\Mail\Base\Model\Mail
442 | */
443 | public function updateWithTransport($transport)
444 | {
445 | /** @noinspection PhpUndefinedMethodInspection */
446 | /** @noinspection PhpUndefinedMethodInspection */
447 | $this
448 | ->setSubject($transport->getMessage()->getSubject())
449 | ->setMessage($transport->getMessage())
450 | ->setRecipients($transport->getMessage()->getRecipients())
451 | ->setId(null);
452 |
453 | return $this;
454 | }
455 |
456 | /**
457 | * Set recipients of mail
458 | *
459 | * @param string[] $value
460 | * @return \Shockwavemk\Mail\Base\Model\Mail
461 | */
462 | public function setRecipients(array $value)
463 | {
464 | $value = json_encode($value);
465 | $this->setData('recipients', $value);
466 | return $this;
467 | }
468 |
469 | /**
470 | * Set reverse link on each attached entity
471 | *
472 | * @var $attachments AttachmentInterface[]
473 | */
474 | public function setAttachments($attachments) {
475 | /** @var AttachmentInterface $attachment */
476 | foreach ($attachments as $attachment) {
477 | $attachment->setMail($this);
478 | }
479 |
480 | $this->setData('attachments', $attachments);
481 |
482 | return $this;
483 | }
484 |
485 | /**
486 | * Save mail to db, message and attachments to storage
487 | *
488 | * @throws \Magento\Framework\Exception\MailException
489 | * @throws \Exception
490 | */
491 | public function save()
492 | {
493 | parent::save();
494 |
495 | $this->_storage->saveMessage($this);
496 |
497 | $this->_storage->saveAttachments($this);
498 |
499 | $this->_storage->saveMail($this);
500 | }
501 |
502 | /**
503 | * Specify data which should be serialized to JSON
504 | * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
505 | * @return mixed data which can be serialized by json_encode,
506 | * @throws \Magento\Framework\Exception\MailException
507 | * which is a value of any type other than a resource.
508 | * @since 5.4.0
509 | */
510 | public function jsonSerialize()
511 | {
512 | $data = $this->getData();
513 | $attachmentsSerialized = [];
514 |
515 | /** @noinspection IsEmptyFunctionUsageInspection */
516 | if (!empty($this->getAttachments())) {
517 | foreach ($this->getAttachments() as $attachment) {
518 | $attachmentsSerialized[] = $attachment->jsonSerialize();
519 | }
520 | }
521 |
522 | $data['attachments'] = $attachmentsSerialized;
523 |
524 | return $data;
525 | }
526 |
527 | /**
528 | * @return string
529 | */
530 | public function getDeliveryTime()
531 | {
532 | $deliveryTime = $this->getData('delivery_time');
533 | $dateTime = $this->_timeZone->date($deliveryTime);
534 |
535 | return $dateTime->format('D, d M Y H:i:s O');
536 | }
537 |
538 | /**
539 | * @return string
540 | */
541 | public function getSentAt()
542 | {
543 | $sentAt = $this->getData('sent_at');
544 | $dateTime = $this->_timeZone->date($sentAt);
545 |
546 | return $dateTime->format('D, d M Y H:i:s O');
547 | }
548 |
549 | /**
550 | * @return string
551 | */
552 | public function getTestMode()
553 | {
554 | /** @noinspection IsEmptyFunctionUsageInspection */
555 | if (empty($value = $this->getData('test_mode'))) {
556 | $value = $this->_config->getTestMode();
557 | $this->setData('test_mode', $value);
558 | }
559 |
560 | return $value;
561 | }
562 |
563 | /**
564 | * @return string
565 | */
566 | public function getTrackingEnabled()
567 | {
568 | /** @noinspection IsEmptyFunctionUsageInspection */
569 | if (empty($value = $this->getData('tracking_enabled'))) {
570 | $value = $this->_config->getTrackingEnabled();
571 | $this->setData('tracking_enabled', $value);
572 | }
573 |
574 | return $value;
575 | }
576 |
577 | /**
578 | * @return string
579 | */
580 | public function getTrackingClicksEnabled()
581 | {
582 | /** @noinspection IsEmptyFunctionUsageInspection */
583 | if (empty($value = $this->getData('tracking_clicks_enabled'))) {
584 | $value = $this->_config->getTrackingClicksEnabled();
585 | $this->setData('tracking_clicks_enabled', $value);
586 | }
587 |
588 | return $value;
589 | }
590 |
591 | /**
592 | * @return string
593 | */
594 | public function getTrackingOpensEnabled()
595 | {
596 | /** @noinspection IsEmptyFunctionUsageInspection */
597 | if (empty($value = $this->getData('tracking_opens_enabled'))) {
598 | $value = $this->_config->getTrackingOpensEnabled();
599 | $this->setData('tracking_opens_enabled', $value);
600 | }
601 |
602 | return $value;
603 | }
604 |
605 | /**
606 | * @param array $items
607 | * @param \Magento\Eav\Model\Entity\Collection\AbstractCollection $collection
608 | * @return \Magento\Eav\Model\Entity\Collection\AbstractCollection
609 | * @throws \Magento\Framework\Exception\LocalizedException
610 | */
611 | protected function convertValueArrayToCollectionItems($items, $collection)
612 | {
613 | /** @var \Magento\Rma\Model\ResourceModel\Item\Collection $collection */
614 | foreach ($items as $item) {
615 | $collection->addFilter('entity_id', $item['entity_id'], 'or');
616 | }
617 |
618 | $collection->load();
619 |
620 | return $collection;
621 | }
622 |
623 | /**
624 | * @param $value
625 | * @return mixed
626 | */
627 | protected function createObjectFromValue($value)
628 | {
629 | $object = $this->_manager->get($value['class']);
630 | unset($value['class']);
631 |
632 | // if object is a collection
633 | if (!empty($value)
634 | && is_subclass_of($object, '\Magento\Eav\Model\Entity\Collection\AbstractCollection')
635 | ) {
636 | $this->convertValueArrayToCollectionItems($value, $object);
637 | return $object;
638 | }
639 | return $object;
640 | }
641 |
642 | /**
643 | * @param $value
644 | * @return mixed
645 | */
646 | protected function createAbstractModelFromValue($value)
647 | {
648 | /** @var \Magento\Framework\Model\AbstractModel $value */
649 | $newValue = $this->_manager->get($value['class']);
650 | $newValue->load($value['entity_id']);
651 | return $newValue;
652 | }
653 | }
654 |
--------------------------------------------------------------------------------
/Model/Mail/Attachment.php:
--------------------------------------------------------------------------------
1 | storage = $storage;
60 | }
61 |
62 | /**
63 | * Return all data, except binary
64 | *
65 | * @return array
66 | */
67 | public function jsonSerialize()
68 | {
69 | $data = $this->getData();
70 | $data['binary'] = '';
71 | $data['mail'] = $this->getMail()->getId();
72 |
73 | return $data;
74 | }
75 |
76 | /**
77 | * Returns a mime part converted attachment
78 | *
79 | * @return Zend_Mime_Part
80 | */
81 | public function toMimePart()
82 | {
83 | /** @var Zend_Mime_Part $attachmentMimePart */
84 | $attachmentMimePart = new Zend_Mime_Part($this->getBinary());
85 |
86 | $attachmentMimePart->type = $this->getMimeType();
87 | $attachmentMimePart->disposition = $this->getDisposition();
88 | $attachmentMimePart->encoding = $this->getEncoding();
89 | $attachmentMimePart->filename = $this->getFileName();
90 |
91 | return $attachmentMimePart;
92 | }
93 |
94 | /**
95 | * Returns binary data of a single attachment file
96 | *
97 | * @return string binary data of attachment file
98 | * @throws \Magento\Framework\Exception\MailException
99 | */
100 | public function getBinary()
101 | {
102 | /** @noinspection IsEmptyFunctionUsageInspection */
103 | if (empty($this->getData('binary'))) {
104 |
105 | $attachmentBinary = file_get_contents(
106 | $this->getFilePath()
107 | );
108 |
109 | $this->setData(
110 | 'binary',
111 | $attachmentBinary
112 | );
113 | }
114 |
115 | return $this->getData('binary');
116 | }
117 |
118 | public function getMail()
119 | {
120 | return $this->getData('mail');
121 | }
122 |
123 | /**
124 | *
125 | *
126 | * @return string
127 | */
128 | public function getFilePath()
129 | {
130 | /** @noinspection IsEmptyFunctionUsageInspection */
131 | if (empty($this->getData('file_path'))) {
132 | $this->setData('file_path',
133 | self::ZEND_MAIL_ATTACHMENT_DEFAULT_FILENAME
134 | );
135 | }
136 |
137 | return $this->getData('file_path');
138 | }
139 |
140 | /**
141 | *
142 | *
143 | * @return string
144 | */
145 | public function getMimeType()
146 | {
147 | /** @noinspection IsEmptyFunctionUsageInspection */
148 | if (empty($this->getData('mime_type'))) {
149 | $this->setData('mime_type',
150 | self::ZEND_MAIL_ATTACHMENT_DEFAULT_TYPE
151 | );
152 | }
153 |
154 | return $this->getData('mime_type');
155 | }
156 |
157 | /**
158 | * Returns current setting of how to integrate attachment into message
159 | *
160 | * @return string
161 | */
162 | public function getDisposition()
163 | {
164 | /** @noinspection IsEmptyFunctionUsageInspection */
165 | if (empty($this->getData('disposition'))) {
166 | $this->setData(
167 | 'disposition',
168 | Zend_Mime::DISPOSITION_ATTACHMENT
169 | );
170 | }
171 |
172 | return $this->getData('disposition');
173 | }
174 |
175 | public function getEncoding()
176 | {
177 | /** @noinspection IsEmptyFunctionUsageInspection */
178 | if (empty($this->getData('encoding'))) {
179 | $this->setData(
180 | 'encoding',
181 | Zend_Mime::ENCODING_BASE64
182 | );
183 | }
184 | return $this->getData('encoding');
185 | }
186 |
187 | /**
188 | *
189 | *
190 | * @return string
191 | */
192 | public function getFileName()
193 | {
194 | if (empty($this->getData('file_name'))) {
195 | return basename($this->getFilePath());
196 | }
197 |
198 | return $this->getData('file_name');
199 | }
200 |
201 | /**
202 | * @param string $value
203 | * @return $this
204 | */
205 | public function setFileName($value)
206 | {
207 | $this->setData('file_name', $value);
208 | return $this;
209 | }
210 |
211 | /**
212 | * @param string
213 | * @return $this
214 | */
215 | public function setBinary($value)
216 | {
217 | $this->setData('binary', $value);
218 | return $this;
219 | }
220 |
221 | /**
222 | * @param string $value
223 | * @return $this
224 | */
225 | public function setDisposition($value)
226 | {
227 | $this->setData('disposition', $value);
228 | return $this;
229 | }
230 |
231 | /**
232 | * @param string $value
233 | * @return $this
234 | */
235 | public function setEncoding($value)
236 | {
237 | $this->setData('encoding', $value);
238 | return $this;
239 | }
240 |
241 | /**
242 | * @param string $value
243 | * @return $this
244 | */
245 | public function setHash($value)
246 | {
247 | $this->setData('hash', $value);
248 | return $this;
249 | }
250 |
251 | /**
252 | * @param string $value
253 | * @return $this
254 | */
255 | public function setMimeType($value)
256 | {
257 | $this->setData('mime_type', $value);
258 | return $this;
259 | }
260 |
261 | /**
262 | * @param string $value
263 | * @return $this
264 | */
265 | public function setFilePath($value)
266 | {
267 | $this->setData('file_path', $value);
268 | return $this;
269 | }
270 |
271 | /**
272 | * @param string $value
273 | * @return $this
274 | */
275 | public function setSize($value)
276 | {
277 | $this->setData('size', $value);
278 | return $this;
279 | }
280 |
281 | /**
282 | * @param string $value
283 | * @return $this
284 | */
285 | public function setUrl($value)
286 | {
287 | $this->setData('url', $value);
288 | return $this;
289 | }
290 |
291 | /**
292 | * @param string
293 | * @return $this
294 | */
295 | public function setMail($value)
296 | {
297 | $this->setData('mail', $value);
298 | return $this;
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/Model/Mail/AttachmentCollection.php:
--------------------------------------------------------------------------------
1 | json_encode,
16 | * which is a value of any type other than a resource.
17 | * @since 5.4.0
18 | */
19 | public function jsonSerialize()
20 | {
21 | return array(
22 | 'type' => $this->getType(),
23 | 'bodyText' => $this->getBodyText(),
24 | 'bodyHtml' => $this->getBodyHtml(),
25 | 'recipients' => $this->getRecipients(),
26 | 'from' => $this->getFrom(),
27 | 'subject' => $this->getSubject(),
28 | 'text' => quoted_printable_decode($this->getBodyText(true)),
29 | 'html' => quoted_printable_decode($this->getBodyHtml(true))
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Model/Mail/MessageInterface.php:
--------------------------------------------------------------------------------
1 | _init('shockwavemk_mail_entity', 'entity_id');
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function save(\Magento\Framework\Model\AbstractModel $object)
38 | {
39 | $object->setHasDataChanges(true);
40 | return parent::save($object);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Model/ResourceModel/Mail/Collection.php:
--------------------------------------------------------------------------------
1 | _objectManager = \Magento\Framework\App\ObjectManager::getInstance();
23 | parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
24 | }
25 |
26 | /**
27 | * Initialize resource
28 | *
29 | * @return void
30 | */
31 | protected function _construct()
32 | {
33 | $this->_init('Shockwavemk\Mail\Base\Model\Mail', 'Shockwavemk\Mail\Base\Model\ResourceModel\Mail');
34 | }
35 |
36 | /**
37 | * Init collection select
38 | *
39 | * @return $this
40 | */
41 | protected function _initSelect()
42 | {
43 | $request = $this->_objectManager->get('Magento\Framework\App\RequestInterface');
44 | $this->getSelect()->from(['main_table' => $this->getMainTable()]);
45 |
46 | if(!empty($customerId = $request->getParam('id'))) {
47 | $this->getSelect()->where('customer_id = ?', $customerId);
48 | }
49 |
50 | return $this;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Model/Simulation/Config.php:
--------------------------------------------------------------------------------
1 | scopeConfig = $scopeConfig;
31 | }
32 |
33 | public function getValue($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null)
34 | {
35 | return $this->scopeConfig->getValue($path, $scopeType, $scopeCode);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Model/Storages/Base.php:
--------------------------------------------------------------------------------
1 | _storage = $manager->get(
32 | $config->getStorageClassName()
33 | );
34 |
35 | } catch(\Exception $e) {
36 |
37 | throw new MailException(
38 | new Phrase($e->getMessage()),
39 | $e)
40 | ;
41 |
42 | }
43 | }
44 |
45 | /**
46 | * Send a mail using this transport
47 | *
48 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
49 | * @return $id
50 | * @throws MailException
51 | *
52 | */
53 | public function saveMessage($mail)
54 | {
55 | try {
56 |
57 | return $this->_storage->saveMessage($mail);
58 |
59 | } catch (\Exception $e) {
60 |
61 | throw new MailException(
62 | new Phrase($e->getMessage()),
63 | $e)
64 | ;
65 |
66 | }
67 | }
68 |
69 | /**
70 | * Send a mail using this transport
71 | *
72 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
73 | *
74 | * @return \Shockwavemk\Mail\Base\Model\Mail\Message
75 | * @throws MailException
76 | */
77 | public function loadMessage($mail)
78 | {
79 | try {
80 |
81 | return $this->_storage->loadMessage($mail);
82 |
83 | } catch (\Exception $e) {
84 |
85 | throw new MailException(
86 | new Phrase($e->getMessage()),
87 | $e)
88 | ;
89 |
90 | }
91 | }
92 |
93 | /**
94 | * TODO
95 | *
96 | * @param AttachmentInterface $attachment
97 | * @return string $id
98 | * @throws MailException
99 | */
100 | public function saveAttachment($attachment)
101 | {
102 | try {
103 |
104 | return $this->_storage->saveAttachment($attachment);
105 |
106 | } catch (\Exception $e) {
107 |
108 | throw new MailException(
109 | new Phrase($e->getMessage()),
110 | $e)
111 | ;
112 |
113 | }
114 | }
115 |
116 | /**
117 | * TODO
118 | *
119 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
120 | *
121 | * @return \Shockwavemk\Mail\Base\Model\Mail\AttachmentInterface[]
122 | * @throws MailException
123 | */
124 | public function getAttachments($mail)
125 | {
126 | try {
127 |
128 | return $this->_storage->getAttachments($mail);
129 |
130 | } catch (\Exception $e) {
131 |
132 | throw new MailException(
133 | new Phrase($e->getMessage()),
134 | $e)
135 | ;
136 |
137 | }
138 | }
139 |
140 | /**
141 | * TODO
142 | *
143 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
144 | * @return $this
145 | * @throws MailException
146 | */
147 | public function saveAttachments($mail)
148 | {
149 | try {
150 | return $this->_storage->saveAttachments($mail);
151 |
152 | } catch (\Exception $e) {
153 |
154 | throw new MailException(
155 | new Phrase($e->getMessage()),
156 | $e)
157 | ;
158 |
159 | }
160 | }
161 |
162 | /**
163 | * Use the concrete storage implementation to save the mail to storage
164 | *
165 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
166 | *
167 | * @return $id
168 | * @throws MailException
169 | */
170 | public function saveMail($mail)
171 | {
172 | try {
173 |
174 | return $this->_storage->saveMail($mail);
175 |
176 | } catch (\Exception $e) {
177 |
178 | throw new MailException(
179 | new Phrase($e->getMessage()),
180 | $e)
181 | ;
182 |
183 | }
184 | }
185 |
186 | /**
187 | * Use the concrete storage implementation to load the mail to storage
188 | *
189 | * @return \Shockwavemk\Mail\Base\Model\Mail
190 | * @throws MailException
191 | */
192 | public function loadMail($id)
193 | {
194 | try {
195 |
196 | return $this->_storage->loadMail($id);
197 |
198 | } catch (\Exception $e) {
199 |
200 | throw new MailException(
201 | new Phrase($e->getMessage()),
202 | $e)
203 | ;
204 |
205 | }
206 | }
207 |
208 | /**
209 | * TODO
210 | *
211 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
212 | * @param string $path
213 | * @return AttachmentInterface
214 | * @throws MailException
215 | */
216 | public function loadAttachment($mail, $path)
217 | {
218 | try {
219 |
220 | return $this->_storage->loadAttachment($mail, $path);
221 |
222 | } catch (\Exception $e) {
223 |
224 | throw new MailException(
225 | new Phrase($e->getMessage()),
226 | $e)
227 | ;
228 |
229 | }
230 | }
231 |
232 | /**
233 | * TODO
234 | *
235 | * @return string
236 | * @throws MailException
237 | */
238 | public function getMailFolderPathById($mailId)
239 | {
240 | try {
241 |
242 | return $this->_storage->getMailFolderPathById($mailId);
243 |
244 | } catch (\Exception $e) {
245 |
246 | throw new MailException(
247 | new Phrase($e->getMessage()),
248 | $e)
249 | ;
250 |
251 | }
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/Model/Storages/DefaultStorage.php:
--------------------------------------------------------------------------------
1 | _config = $config;
50 | $this->_objectManager = $objectManager;
51 | }
52 |
53 | /**
54 | * Save file to spool path
55 | *
56 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
57 | * @return $this
58 | * @throws \Magento\Framework\Exception\MailException
59 | * @throws \Magento\Framework\Exception\FileSystemException
60 | * @throws \Exception
61 | */
62 | public function saveMessage($mail)
63 | {
64 | // convert message to html
65 | $messageHtml = quoted_printable_decode(
66 | $mail->getMessage()->getBodyHtml(true)
67 | );
68 |
69 | $this->createFolder(
70 | $this->getMailFolderPathById($mail->getId())
71 | );
72 |
73 | // try to store message to filesystem
74 | $this->storeFile(
75 | $messageHtml,
76 | $this->getMailFolderPathById($mail->getId()) .
77 | DIRECTORY_SEPARATOR . self::MESSAGE_HTML_FILE_NAME
78 | );
79 |
80 | // convert message to json
81 | $messageJson = json_encode($mail->getMessage());
82 |
83 | // try to store message to filesystem
84 | $this->storeFile(
85 | $messageJson,
86 | $this->getMailFolderPathById($mail->getId()) .
87 | DIRECTORY_SEPARATOR . self::MESSAGE_FILE_NAME
88 | );
89 |
90 | return $this;
91 | }
92 |
93 | /**
94 | * Create a folder path, if folder does not exist
95 | *
96 | * @param $folderPath
97 | * @return $this
98 | * @throws \Magento\Framework\Exception\FileSystemException
99 | */
100 | protected function createFolder($folderPath)
101 | {
102 | if(!@mkdir($folderPath,0777,true) && !is_dir($folderPath))
103 | {
104 | throw new FileSystemException(
105 | new Phrase('Folder can not be created, but does not exist.')
106 | );
107 | }
108 |
109 | return $this;
110 | }
111 |
112 | /**
113 | * Returns a string concat of magento config value for email spool path and mail id
114 | * e.g. pub/media/emails/
115 | *
116 | * @param $mailId
117 | * @return string e.g. pub/media/emails/
118 | */
119 | public function getMailFolderPathById($mailId)
120 | {
121 | // store message in temporary file system spooler
122 | $hostTempFolderPath = $this->_config->getHostSpoolerFolderPath();
123 |
124 | return $hostTempFolderPath . DIRECTORY_SEPARATOR . $mailId;
125 | }
126 |
127 | /**
128 | * Stores string data at a given path
129 | *
130 | * @param $data
131 | * @param $filePath
132 | * @return bool
133 | * @throws \Magento\Framework\Exception\FileSystemException
134 | * @throws \Exception
135 | */
136 | protected function storeFile($data, $filePath)
137 | {
138 | // create a folder for message if needed
139 | if (!is_dir(dirname($filePath))) {
140 | $this->createFolder(dirname($filePath));
141 | }
142 |
143 | /** @noinspection LoopWhichDoesNotLoopInspection */
144 | for ($i = 0; $i < $this->_config->getHostRetryLimit(); $i++) {
145 | /* We try an exclusive creation of the file.
146 | * This is an atomic operation, it avoid locking mechanism
147 | */
148 | $fp = @fopen($filePath, 'x');
149 |
150 | if($fp !== false) {
151 | if (false === fwrite($fp, $data)) {
152 | return false;
153 | }
154 |
155 | fclose($fp);
156 |
157 | return $filePath;
158 | }
159 | }
160 |
161 | throw new FileSystemException(
162 | new Phrase('Unable to create a file for enqueuing Message')
163 | );
164 | }
165 |
166 | /**
167 | * Restore a message from filesystem
168 | *
169 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
170 | * @return \Shockwavemk\Mail\Base\Model\Mail\Message $message
171 | * @throws \Exception
172 | * @throws \Zend_Mail_Exception
173 | */
174 | public function loadMessage($mail)
175 | {
176 | $localFilePath = $this->getMailFolderPathById($mail->getId()) .
177 | DIRECTORY_SEPARATOR . self::MESSAGE_FILE_NAME;
178 |
179 | $messageJson = $this->restoreFile($localFilePath);
180 |
181 | if (empty($messageJson)) {
182 | $messageJson = '{}';
183 | }
184 |
185 | $messageData = json_decode($messageJson);
186 |
187 | /** @var \Shockwavemk\Mail\Base\Model\Mail\Message $message */
188 | $message = $this->_objectManager->create('Shockwavemk\Mail\Base\Model\Mail\Message');
189 |
190 | if (!empty($messageData->type)) {
191 | $message->setType($messageData->type);
192 | }
193 |
194 | if (!empty($messageData->txt)) {
195 | $message->setBodyText($messageData->txt);
196 | }
197 |
198 | if (!empty($messageData->html)) {
199 | $message->setBodyHtml($messageData->html);
200 | }
201 |
202 | if (!empty($messageData->from)) {
203 | $message->setFrom($messageData->from);
204 | }
205 |
206 | if (!empty($messageData->subject)) {
207 | $message->setSubject($messageData->subject);
208 | }
209 |
210 | if (!empty($messageData->recipients)) {
211 | foreach ($messageData->recipients as $recipient) {
212 | $message->addTo($recipient);
213 | }
214 | }
215 |
216 | return $message;
217 | }
218 |
219 | /**
220 | * Load binary file data from a given file path
221 | *
222 | * @param $filePath
223 | * @return null|string
224 | */
225 | private function restoreFile($filePath)
226 | {
227 | try {
228 | for ($i = 0; $i < $this->_config->getHostRetryLimit(); ++$i) {
229 | /* We try an exclusive creation of the file. This is an atomic operation, it avoid locking mechanism */
230 | @fopen($filePath, 'x');
231 |
232 | if (false === $fileData = file_get_contents($filePath)) {
233 | return null;
234 | }
235 | return $fileData;
236 | }
237 | } catch (\Exception $e) {
238 | return null;
239 | }
240 |
241 | return null;
242 | }
243 |
244 | /**
245 | * Convert a mail object to json and store it at mail folder path
246 | *
247 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
248 | * @return bool
249 | * @throws \Magento\Framework\Exception\FileSystemException
250 | * @throws \Exception
251 | */
252 | public function saveMail($mail)
253 | {
254 | // first save file to spool path
255 | // to avoid exceptions on external storage provider connection
256 |
257 | // convert message to json
258 | $mailJson = json_encode($mail);
259 |
260 | $this->createFolder(
261 | $this->getMailFolderPathById($mail->getId())
262 | );
263 |
264 | // try to store message to filesystem
265 | return $this->storeFile(
266 | $mailJson,
267 | $this->getMailFolderPathById($mail->getId()) .
268 | DIRECTORY_SEPARATOR . self::MAIL_FILE_NAME
269 | );
270 | }
271 |
272 | /**
273 | * Loads json file from storage and converts/parses it to a new mail object
274 | *
275 | * @param int $id folder name / id eg. pub/media/emails/66
276 | * @return \Shockwavemk\Mail\Base\Model\Mail
277 | * @throws \Exception
278 | */
279 | public function loadMail($id)
280 | {
281 | /** @var \Shockwavemk\Mail\Base\Model\Mail $mail */
282 | $mail = $this->_objectManager->create('Shockwavemk\Mail\Base\Model\Mail');
283 |
284 | $mail->setId($id);
285 |
286 | $localFilePath = $this->getMailFolderPathById(
287 | $mail->getId()) .
288 | DIRECTORY_SEPARATOR . self::MAIL_FILE_NAME;
289 |
290 | $mailJson = $this->restoreFile($localFilePath);
291 |
292 | /** @noinspection IsEmptyFunctionUsageInspection */
293 | if (empty($mailJson)) {
294 | $mailJson = '{}';
295 | }
296 |
297 | /** @noinspection IsEmptyFunctionUsageInspection */
298 | if (!empty($mailData = json_decode($mailJson, true))) {
299 | foreach ($mailData as $key => $value) {
300 | $mail->setData($key, $value);
301 | }
302 | }
303 |
304 | return $mail;
305 | }
306 |
307 | /**
308 | * Load binary data from storage provider
309 | *
310 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
311 | * @param string $path
312 | * @return string
313 | */
314 | public function loadAttachment($mail, $path)
315 | {
316 | $attachmentFolder = DIRECTORY_SEPARATOR . self::ATTACHMENT_PATH;
317 |
318 | $localFilePath = $this->getMailFolderPathById($mail->getId()) .
319 | $attachmentFolder . $path;
320 |
321 | return $this->restoreFile($localFilePath);
322 | }
323 |
324 | /**
325 | * Returns attachments for a given mail
326 | *
327 | * \Shockwavemk\Mail\Base\Model\Mail $mail
328 | *
329 | * @return \Shockwavemk\Mail\Base\Model\Mail\Attachment[]
330 | */
331 | public function getAttachments($mail)
332 | {
333 | /** @noinspection IsEmptyFunctionUsageInspection */
334 | if(empty($mail->getId())) {
335 | return [];
336 | }
337 |
338 | $folderFileList = $this->getMailLocalFolderFileList($mail);
339 |
340 | $attachments = [];
341 |
342 | foreach ($folderFileList as $filePath => $fileMetaData) {
343 |
344 | $filePath = $fileMetaData['path'];
345 |
346 | /** @var \Shockwavemk\Mail\Base\Model\Mail\Attachment $attachment */
347 | $attachment = $this->_objectManager
348 | ->create('\Shockwavemk\Mail\Base\Model\Mail\Attachment');
349 |
350 | $attachment->setFilePath($filePath);
351 | $attachment->setMail($mail);
352 |
353 | // transfer all meta data into attachment object
354 | foreach ($fileMetaData as $attributeKey => $attributeValue) {
355 | $attachment->setData($attributeKey, $attributeValue);
356 | }
357 |
358 | $attachments[$filePath] = $attachment;
359 | }
360 |
361 | return $attachments;
362 | }
363 |
364 | /**
365 | * Get file list for a given mail
366 | *
367 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
368 | * @return array
369 | */
370 | protected function getMailLocalFolderFileList($mail)
371 | {
372 | $spoolFolder = $this->_config->getHostSpoolerFolderPath() .
373 | DIRECTORY_SEPARATOR .
374 | $mail->getId() .
375 | DIRECTORY_SEPARATOR .
376 | self::ATTACHMENT_PATH;
377 |
378 | // create a folder for attachments if needed
379 | if (!is_dir($spoolFolder)) {
380 | $this->createFolder($spoolFolder);
381 | }
382 |
383 | /** @var RecursiveIteratorIterator $objects */
384 | $objects = new RecursiveIteratorIterator(
385 | new RecursiveDirectoryIterator($spoolFolder),
386 | RecursiveIteratorIterator::LEAVES_ONLY,
387 | FilesystemIterator::SKIP_DOTS
388 | );
389 |
390 | /** @var array $files */
391 | $files = [];
392 |
393 | /**
394 | * @var string $name
395 | * @var SplFileObject $object
396 | */
397 | foreach ($objects as $path => $object) {
398 | /** @noinspection TypeUnsafeComparisonInspection */
399 | /** @noinspection NotOptimalIfConditionsInspection */
400 | if ($object->getFilename() != '.' && $object->getFilename() != '..') {
401 | $filePath = str_replace($spoolFolder, '', $path);
402 | $file = [
403 | 'name' => $object->getFilename(),
404 | 'path' => $path
405 | ];
406 | $files[$filePath] = $file;
407 | }
408 | }
409 |
410 | return $files;
411 | }
412 |
413 | /**
414 | * Save all attachments of a given mail
415 | *
416 | * @param \Shockwavemk\Mail\Base\Model\Mail $mail
417 | * @return $this
418 | * @throws \Magento\Framework\Exception\FileSystemException
419 | * @throws \Magento\Framework\Exception\MailException
420 | * @throws \Exception
421 | */
422 | public function saveAttachments($mail)
423 | {
424 | /** @var \Shockwavemk\Mail\Base\Model\Mail\Attachment[] $attachments */
425 | $attachments = $mail->getAttachments();
426 |
427 | foreach ($attachments as $attachment) {
428 | $this->saveAttachment($attachment);
429 | }
430 |
431 | return $this;
432 | }
433 |
434 | /**
435 | * Save an attachment binary to a file in host temp folder
436 | *
437 | * @param \Shockwavemk\Mail\Base\Model\Mail\Attachment $attachment
438 | * @return int $id
439 | * @throws \Magento\Framework\Exception\FileSystemException
440 | * @throws \Magento\Framework\Exception\MailException
441 | * @throws \Exception
442 | */
443 | public function saveAttachment($attachment)
444 | {
445 | $binaryData = $attachment->getBinary();
446 | /** @var \Shockwavemk\Mail\Base\Model\Mail $mail */
447 | $mail = $attachment->getMail();
448 |
449 | $folderPath = $this->getMailFolderPathById($mail->getId()) .
450 | DIRECTORY_SEPARATOR .
451 | self::ATTACHMENT_PATH;
452 |
453 | // create a folder for message if needed
454 | $this->createFolder($folderPath);
455 |
456 | // try to store message to filesystem
457 | $filePath = $this->storeFile(
458 | $binaryData,
459 | $this->getMailFolderPathById($mail->getId()) .
460 | DIRECTORY_SEPARATOR .
461 | self::ATTACHMENT_PATH .
462 | DIRECTORY_SEPARATOR .
463 | basename($attachment->getFilePath())
464 | );
465 |
466 | $attachment->setFilePath($filePath);
467 | }
468 |
469 | /**
470 | * Get Folder list for host spooler folder path
471 | *
472 | * @return string[]
473 | */
474 | public function getRootLocalFolderFileList()
475 | {
476 | $spoolFolder = $this->_config->getHostSpoolerFolderPath();
477 |
478 | /** @var IteratorIterator $objects */
479 | $objects = new IteratorIterator(
480 | new DirectoryIterator($spoolFolder)
481 | );
482 |
483 | /** @var array $files */
484 | $files = [];
485 |
486 | /**
487 | * @var string $name
488 | * @var SplFileObject $object
489 | */
490 | foreach ($objects as $path => $object) {
491 | /** @noinspection TypeUnsafeComparisonInspection */
492 | /** @noinspection NotOptimalIfConditionsInspection */
493 | if ($object->getFilename() != '.' && $object->getFilename() != '..') {
494 | /** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
495 | $shortFilePath = str_replace($spoolFolder, '', $object->getPathName());
496 |
497 | /** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
498 | $files[] = [
499 | 'name' => $object->getFilename(),
500 | 'localPath' => $object->getPathName(),
501 | 'remotePath' => $shortFilePath,
502 | 'modified' => $object->getMTime()
503 | ];
504 | }
505 | }
506 |
507 | return $files;
508 | }
509 |
510 | /**
511 | * Get list of files in a local file path
512 | *
513 | * @param $localPath
514 | * @return string[]
515 | */
516 | public function getLocalFileListForPath($localPath)
517 | {
518 | $files = [];
519 | $objects = new RecursiveIteratorIterator(
520 | new RecursiveDirectoryIterator($localPath),
521 | RecursiveIteratorIterator::LEAVES_ONLY,
522 | FilesystemIterator::SKIP_DOTS
523 | );
524 |
525 | /**
526 | * @var string $path
527 | * @var SplFileObject $object
528 | */
529 | foreach ($objects as $path => $object) {
530 | /** @noinspection TypeUnsafeComparisonInspection */
531 | /** @noinspection NotOptimalIfConditionsInspection */
532 | if ($object->getFilename() != '.' && $object->getFilename() != '..') {
533 | $hostTempFolderPath = $this->_config->getHostSpoolerFolderPath();
534 |
535 | /** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
536 | $remoteFilePath = str_replace($hostTempFolderPath, '/', $object->getPathName());
537 |
538 |
539 | /** @noinspection PhpMethodOrClassCallIsNotCaseSensitiveInspection */
540 | $file = [
541 | 'name' => $object->getFilename(),
542 | 'localPath' => $object->getPathName(),
543 | 'remotePath' => $remoteFilePath,
544 | 'modified' => $object->getMTime()
545 | ];
546 |
547 | $files[$object->getFilename()] = $file;
548 | }
549 | }
550 |
551 | return $files;
552 | }
553 |
554 | /**
555 | * Deletes all local stored files
556 | *
557 | * @param $localPath
558 | * @return \Shockwavemk\Mail\Base\Model\Storages\DefaultStorage
559 | */
560 | public function deleteLocalFiles($localPath)
561 | {
562 | $it = new RecursiveDirectoryIterator(
563 | $localPath,
564 | RecursiveDirectoryIterator::SKIP_DOTS
565 | );
566 |
567 | $files = new RecursiveIteratorIterator(
568 | $it,
569 | RecursiveIteratorIterator::CHILD_FIRST
570 | );
571 |
572 | foreach ($files as $file) {
573 | if ($file->isDir()) {
574 | rmdir($file->getRealPath());
575 | } else {
576 | unlink($file->getRealPath());
577 | }
578 | }
579 |
580 | rmdir($localPath);
581 | }
582 | }
583 |
--------------------------------------------------------------------------------
/Model/Storages/StorageInterface.php:
--------------------------------------------------------------------------------
1 | areaList = $areaList;
56 | }
57 |
58 | /**
59 | * Get mail transport
60 | *
61 | * @return \Shockwavemk\Mail\Base\Model\Transports\TransportInterface
62 | */
63 | public function getTransport()
64 | {
65 | $this->prepareMessage();
66 |
67 | return $this->createTransport();
68 | }
69 |
70 | /**
71 | * Prepare message
72 | *
73 | * @return $this
74 | * @throws \Zend_Mail_Exception
75 | */
76 | protected function prepareMessage()
77 | {
78 | $template = $this->getTemplate();
79 |
80 | $types = [
81 | TemplateTypesInterface::TYPE_TEXT => MessageInterface::TYPE_TEXT,
82 | TemplateTypesInterface::TYPE_HTML => MessageInterface::TYPE_HTML,
83 | ];
84 |
85 | // Bugfix for not translated cron scheduled mails
86 | $areaObject = $this->areaList->getArea($template->getDesignConfig()->getArea());
87 | $areaObject->load(\Magento\Framework\App\Area::PART_TRANSLATE);
88 |
89 | $body = $template->processTemplate();
90 | $this->message->setMessageType($types[$template->getType()])
91 | ->setBody($body)
92 | ->setSubject($template->getSubject());
93 |
94 | return $this;
95 | }
96 |
97 | /**
98 | * @return \Shockwavemk\Mail\Base\Model\Transports\TransportInterface
99 | */
100 | public function createTransport()
101 | {
102 | // Verify to get a clean mail instance
103 | $this->setMail(null);
104 |
105 | // Transfer TransportData to Mail
106 | $this->updateMailWithTransportData();
107 |
108 | /** @var \Shockwavemk\Mail\Base\Model\Transports\TransportInterface $mailTransport */
109 | $mailTransport = $this->mailTransportFactory
110 | ->create(
111 | ['message' => clone $this->message]
112 | );
113 |
114 | $mailTransport->setMail($this->getMail());
115 |
116 | // Cleanup of mail and message model used in this transport
117 | $this->reset();
118 |
119 | return $mailTransport;
120 | }
121 |
122 | /**
123 | * @return \Shockwavemk\Mail\Base\Model\Mail
124 | */
125 | public function updateMailWithTransportData()
126 | {
127 | $this->getMail()
128 | ->setSubject($this->message->getSubject())
129 | ->setTemplateIdentifier($this->templateIdentifier)
130 | ->setTemplateModel($this->templateModel)
131 | ->setVars($this->templateVars)
132 | ->setOptions($this->templateOptions)
133 | ->setCustomerId($this->getCustomerId())
134 | ->setStoreId($this->getStoreId())
135 | ->setLanguageCode($this->getLanguageCode())
136 | ->setSenderMail($this->getSenderMail());
137 |
138 | return $this;
139 | }
140 |
141 | /**
142 | * Get mail
143 | *
144 | * @return \Shockwavemk\Mail\Base\Model\Mail
145 | * @throws \Magento\Framework\Exception\MailException
146 | */
147 | public function getMail()
148 | {
149 | /** @noinspection IsEmptyFunctionUsageInspection */
150 | if (empty($this->_mail)) {
151 | $this->_mail = $this->objectManager
152 | ->create('Shockwavemk\Mail\Base\Model\Mail');
153 |
154 | /** @var \Shockwavemk\Mail\Base\Model\Mail\AttachmentCollection $attachmentCollection */
155 | $attachmentCollection = $this->objectManager
156 | ->get('Shockwavemk\Mail\Base\Model\Mail\AttachmentCollection');
157 |
158 | foreach ($attachmentCollection as $attachment) {
159 | $this->_mail->addAttachment($attachment);
160 | }
161 | }
162 |
163 | return $this->_mail;
164 | }
165 |
166 | /**
167 | * @param $mail \Shockwavemk\Mail\Base\Model\Mail
168 | * @return $this \Shockwavemk\Mail\Base\Model\Template\TransportBuilder
169 | */
170 | public function setMail($mail)
171 | {
172 | $this->_mail = $mail;
173 | return $this;
174 | }
175 |
176 | /**
177 | * @return int|null
178 | */
179 | public function getCustomerId()
180 | {
181 | if (empty($this->_customerId)) {
182 |
183 | if (!empty($this->templateVars['customer'])) {
184 | /** @var \Magento\Customer\Model\Customer $customer */
185 | $customer = $this->templateVars['customer'];
186 | return $customer->getId();
187 | }
188 |
189 | if (!empty($this->templateVars['order'])) {
190 | /** @var \Magento\Sales\Model\Order $order */
191 | $order = $this->templateVars['order'];
192 | return $order->getCustomerId();
193 | }
194 |
195 | if (!empty($this->templateVars['rma'])) {
196 | /** @var \Magento\Rma\Model\Rma $rma */
197 | $rma = $this->templateVars['rma'];
198 | return !empty($rma->getCustomerId()) ? $rma->getCustomerId() : $rma->getOrder()->getCustomerId();
199 | }
200 | }
201 |
202 | return $this->_customerId;
203 | }
204 |
205 | /**
206 | * @return int|null
207 | */
208 | public function getStoreId()
209 | {
210 | if (empty($this->_storeId)) {
211 |
212 | if (!empty($this->templateVars['store'])) {
213 | /** @var \Magento\Store\Model\Store $store */
214 | $store = $this->templateVars['store'];
215 | return $store->getId();
216 | }
217 | }
218 |
219 | return $this->_storeId;
220 | }
221 |
222 | /**
223 | * @return null|string
224 | */
225 | public function getLanguageCode()
226 | {
227 | if (empty($this->_languageCode)) {
228 |
229 | /** @var \Magento\Framework\Locale\Resolver $resolver */
230 | $resolver = $this->objectManager->get('Magento\Framework\Locale\Resolver');
231 | $resolver->emulate($this->getStoreId());
232 |
233 | return $resolver->getLocale();
234 | }
235 |
236 | return $this->_languageCode;
237 | }
238 |
239 | /**
240 | * @return mixed
241 | */
242 | public function getSenderMail()
243 | {
244 | return $this->_sender;
245 | }
246 |
247 | /**
248 | * Get mail transport with a backup of a existing messageString
249 | *
250 | * @param \Shockwavemk\Mail\Base\Model\Mail\MessageInterface $message
251 | * @return \Shockwavemk\Mail\Base\Model\Transports\TransportInterface
252 | */
253 | public function getBackupTransport($message)
254 | {
255 | $this->prepareMessage();
256 |
257 | // replace message
258 | $this->message = $message;
259 |
260 | return $this->createTransport();
261 | }
262 |
263 | /**
264 | * Set mail from address
265 | *
266 | * @param string|array $from
267 | * @return $this
268 | * @throws \Magento\Framework\Exception\MailException
269 | */
270 | public function setFrom($from)
271 | {
272 | $result = $this->_senderResolver->resolve($from, $this->getStoreId());
273 | $this->setSenderMail($result);
274 | $this->message->setFrom($result['email'], $result['name']);
275 |
276 | return $this;
277 | }
278 |
279 | /**
280 | * @param mixed $sender
281 | */
282 | public function setSenderMail($sender)
283 | {
284 | $this->_sender = $sender;
285 | }
286 |
287 | /**
288 | * @return array
289 | */
290 | public function getTemplateOptions()
291 | {
292 | return $this->templateOptions;
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/Model/Transports/Base.php:
--------------------------------------------------------------------------------
1 | _message = $message;
47 | $transportClassName = $config->getTransportClassName();
48 | $this->_transport = $manager->create(
49 | $transportClassName,
50 | ['message' => $message]
51 | );
52 |
53 | } catch (\Exception $e) {
54 | throw new MailException(
55 | new Phrase($e->getMessage()),
56 | $e);
57 | }
58 | }
59 |
60 | /**
61 | * Send a mail using this transport
62 | *
63 | * @return Base
64 | * @throws MailException
65 | */
66 | public function sendMessage()
67 | {
68 | try {
69 | // First: Send message with given transport
70 | $this->_transport->sendMessage();
71 |
72 | // Second: Create a mail instance to store
73 | $mail = $this->getMail();
74 | $mail->updateWithTransport($this->_transport);
75 | $mail->save();
76 |
77 | } catch (\Exception $e) {
78 | throw new MailException(
79 | new Phrase($e->getMessage()),
80 | $e
81 | );
82 | }
83 |
84 | return $this;
85 | }
86 |
87 | /**
88 | * Get the mail object of this transport
89 | *
90 | * @return Mail
91 | */
92 | public function getMail()
93 | {
94 | return $this->_transport->getMail();
95 | }
96 |
97 | /**
98 | * Associate a mail object with this transport
99 | * Mail object is used to store transport information and sent message
100 | *
101 | * @param Mail $mail
102 | * @return TransportInterface
103 | */
104 | public function setMail($mail)
105 | {
106 | $this->_transport->setMail($mail);
107 | return $this;
108 | }
109 |
110 | /**
111 | * Get message associated with this transport
112 | *
113 | * @return \Shockwavemk\Mail\Base\Model\Mail\MessageInterface
114 | */
115 | public function getMessage()
116 | {
117 | return $this->_transport->getMessage();
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Model/Transports/TransportInterface.php:
--------------------------------------------------------------------------------
1 | objectManager = $objectManager;
170 | $this->documentConfig = $documentConfig;
171 |
172 | // Dynamic typed build a collection for attachments for later usage
173 |
174 | // Should be extended from \Magento\Framework\Data\Collection
175 | $this->attachmentCollection = $this->objectManager->get(
176 | 'Shockwavemk\Mail\Base\Model\Mail\AttachmentCollectionInterface'
177 | );
178 | }
179 |
180 |
181 |
182 | /**
183 | * Handles asynchronous email sending
184 | *
185 | * @return void
186 | * @throws \RuntimeException
187 | * @throws \Exception
188 | */
189 | public function sendEmails()
190 | {
191 | /** @var \Magento\Sales\Model\AbstractModel $item */
192 | foreach ($this->entityCollection->getItems() as $item) {
193 |
194 | // add this code
195 |
196 | // Create a new attachment
197 |
198 | if (!is_null($attachmentFilePath)) {
199 | /** @var \Shockwavemk\Mail\Base\Model\Mail\AttachmentInterface|\Magento\Framework\DataObject $attachment */
200 | $attachment = $this->objectManager
201 | ->create('Shockwavemk\Mail\Base\Model\Mail\AttachmentInterface');
202 |
203 | // Do not transfer binary data to mail entity at this point: The mailer can handle file reading on its own
204 | $attachment->setFilePath($attachmentFilePath);
205 |
206 | // Add attachment to attachment collection
207 | // Let the mailer later decide how to handle them
208 | $this->attachmentCollection->addItem($attachment);
209 | }
210 |
211 | }
212 |
213 | }
214 |
215 | ```
216 |
217 |
218 |
219 |
220 |
--------------------------------------------------------------------------------
/Setup/InstallSchema.php:
--------------------------------------------------------------------------------
1 | startSetup();
26 |
27 | $installer->endSetup();
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Setup/UpgradeSchema.php:
--------------------------------------------------------------------------------
1 | startSetup();
14 | if (version_compare($context->getVersion(), '1.0.1') < 0) {
15 | $this->createMailMainTable($setup);
16 | }
17 |
18 | $setup->endSetup();
19 | }
20 |
21 | /**
22 | * @param $installer
23 | */
24 | protected function createMailMainTable(SetupInterface $installer)
25 | {
26 | $table = $installer->getConnection()->newTable(
27 | $installer->getTable('shockwavemk_mail_entity')
28 | )->addColumn(
29 | 'entity_id',
30 | \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
31 | null,
32 | ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
33 | 'Mail ID'
34 | )->addColumn(
35 | 'customer_id',
36 | \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
37 | null,
38 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
39 | 'Customer ID'
40 | )->addColumn(
41 | 'template_identifier',
42 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
43 | 255,
44 | ['nullable' => false],
45 | 'Template Identifier'
46 | )->addColumn(
47 | 'subject',
48 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
49 | 255,
50 | ['nullable' => false],
51 | 'Subject'
52 | )->addColumn(
53 | 'template_model',
54 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
55 | 255,
56 | ['nullable' => false],
57 | 'Template Model'
58 | )->addColumn(
59 | 'store_id',
60 | \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
61 | null,
62 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
63 | 'Store ID'
64 | )->addColumn(
65 | 'sent',
66 | \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
67 | null,
68 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
69 | 'Successful sent (0 or 1)'
70 | )->addColumn(
71 | 'sent_at',
72 | \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
73 | null,
74 | [],
75 | 'Sent at date'
76 | )->addColumn(
77 | 'recipients',
78 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
79 | \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
80 | ['nullable' => false],
81 | 'Recipients'
82 | )->addColumn(
83 | 'language_code',
84 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
85 | 255,
86 | ['nullable' => false],
87 | 'Language Code'
88 | )->addColumn(
89 | 'sender_mail',
90 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
91 | 255,
92 | ['nullable' => false],
93 | 'Sender Mail'
94 | )->addColumn(
95 | 'vars',
96 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
97 | \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
98 | ['nullable' => false],
99 | 'Vars'
100 | )->addColumn(
101 | 'options',
102 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
103 | \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
104 | ['nullable' => false],
105 | 'Options'
106 | )->addColumn(
107 | 'recipient_variables',
108 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
109 | \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
110 | ['nullable' => false],
111 | 'Recipient variables'
112 | )->addColumn(
113 | 'delivery_time',
114 | \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP,
115 | null,
116 | [],
117 | 'Delivery time'
118 | )->addColumn(
119 | 'tags',
120 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
121 | \Magento\Framework\DB\Ddl\Table::MAX_TEXT_SIZE,
122 | ['nullable' => false],
123 | 'Tags'
124 | )->addColumn(
125 | 'test_mode',
126 | \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
127 | null,
128 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
129 | 'Test mode'
130 | )->addColumn(
131 | 'tracking_enabled',
132 | \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
133 | null,
134 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
135 | 'Tracking enabled'
136 | )->addColumn(
137 | 'tracking_clicks_enabled',
138 | \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
139 | null,
140 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
141 | 'Tracking Clicks Enabled'
142 | )->addColumn(
143 | 'tracking_opens_enabled',
144 | \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT,
145 | null,
146 | ['unsigned' => true, 'nullable' => false, 'default' => '0'],
147 | 'Tracking Opens Enabled'
148 | )->addColumn(
149 | 'transport_id',
150 | \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
151 | 255,
152 | ['nullable' => true],
153 | 'Transport Id'
154 | )->addIndex(
155 | $installer->getIdxName('mail', 'sent'),
156 | 'sent'
157 | )->addIndex(
158 | $installer->getIdxName('mail', 'sent_at'),
159 | 'sent_at'
160 | )->addIndex(
161 | $installer->getIdxName('mail', 'customer_id'),
162 | 'customer_id'
163 | )->setComment(
164 | 'Mail main table'
165 | );
166 | $installer->getConnection()->createTable($table);
167 | }
168 | }
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shockwavemk/magento2-module-mail",
3 | "description": "Base module for extending Magento2 mail features",
4 | "require": {
5 | },
6 | "type": "magento2-module",
7 | "license": [
8 | "Apache-2.0"
9 | ],
10 | "autoload": {
11 | "files": [
12 | "registration.php"
13 | ],
14 | "psr-4": {
15 | "Shockwavemk\\Mail\\Base\\": ""
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/docs/enhanced-magento2-customer-detail-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/enhanced-magento2-customer-detail-menu.png
--------------------------------------------------------------------------------
/docs/local-storeage-for-mails-as-json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/local-storeage-for-mails-as-json.png
--------------------------------------------------------------------------------
/docs/magento2-config-mail-enhancement.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-config-mail-enhancement.png
--------------------------------------------------------------------------------
/docs/magento2-customer-mail-transactional-review-detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-customer-mail-transactional-review-detail.png
--------------------------------------------------------------------------------
/docs/magento2-customer-mail-transactional-review-with-mail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-customer-mail-transactional-review-with-mail.png
--------------------------------------------------------------------------------
/docs/magento2-customer-mail-transactional-review.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-customer-mail-transactional-review.png
--------------------------------------------------------------------------------
/docs/magento2-customer-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-customer-menu.png
--------------------------------------------------------------------------------
/docs/magento2-guest-email-review-and-resending.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-guest-email-review-and-resending.jpg
--------------------------------------------------------------------------------
/docs/magento2-mail-resending-and-recalculation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-mail-resending-and-recalculation.png
--------------------------------------------------------------------------------
/docs/magento2-mail-stored-as-json.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockwavemk/magento2-module-mail/22929cfe0fd78770783487994cd53333a41c3d4b/docs/magento2-mail-stored-as-json.png
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/etc/adminhtml/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/etc/adminhtml/routes.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/etc/adminhtml/system.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Shockwavemk\Mail\Base\Model\Config\Source\Type
15 |
16 |
17 |
18 | Shockwavemk\Mail\Base\Model\Config\Source\Storage
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | Magento\Config\Model\Config\Source\Yesno
37 |
38 |
39 |
40 |
41 | Magento\Config\Model\Config\Source\Yesno
42 |
43 |
44 |
45 |
46 | Magento\Config\Model\Config\Source\Yesno
47 |
48 |
49 |
50 |
51 | Magento\Config\Model\Config\Source\Yesno
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/etc/cache.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/etc/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | default
13 | default
14 | 100
15 | 3
16 | /var/www/html/pub/media/emails
17 | 5
18 |
19 |
20 |
21 |
22 |
23 |
24 | default
25 | \Magento\Framework\Mail\Transport
26 |
27 |
28 |
29 |
30 |
31 | default
32 | \Shockwavemk\Mail\Base\Model\Storages\DefaultStorage
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/etc/module.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/i18n/en_US.csv:
--------------------------------------------------------------------------------
1 | TODO,TODO
--------------------------------------------------------------------------------
/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/customer_index_mail.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 | Shockwavemk\Mail\Base\Model\ResourceModel\Mail\Collection
13 | mailGrid
14 | true
15 | added_at
16 | DESC
17 | 1
18 |
19 | 1
20 |
21 |
22 |
23 |
24 | No Items Found
25 |
26 |
27 |
28 | Subject
29 | subject
30 | text
31 | subject
32 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
33 | col-name
34 | col-name
35 |
36 |
37 |
38 |
39 | Template
40 | template_identifier
41 | text
42 | template_identifier
43 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Template
44 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
45 |
46 |
47 |
48 |
49 | Store
50 | store_id
51 | store
52 |
53 |
54 |
55 |
56 | Sent at
57 | sent_at
58 | 1
59 | datetime
60 | sent_at
61 |
62 |
63 |
64 |
65 | Sent
66 | sent
67 | number
68 | sent
69 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Sent
70 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
71 |
72 |
73 |
74 |
75 | Recipients
76 | recipients
77 | longtext
78 | recipients
79 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Recipients
80 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
81 |
82 |
83 |
84 |
85 | Language
86 | language_code
87 | text
88 | language_code
89 |
90 |
91 |
92 |
93 | Actions
94 | action
95 | text
96 | entity_id
97 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Action
98 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
99 |
100 |
101 |
102 |
103 |
104 | mailGridJsObject
105 | Shockwavemk_Mail_Base::customer/edit/tab/mail.phtml
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/customer_mail_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/customer_mail_overview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/customer_mail_send.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/view/adminhtml/layout/overview_block.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 | Shockwavemk\Mail\Base\Model\ResourceModel\Mail\Collection
14 | mailGrid
15 | added_at
16 | DESC
17 | 1
18 |
19 |
20 |
21 | No Items Found
22 |
23 |
24 |
25 | Customer
26 | customer_id
27 | text
28 | customer_id
29 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Customer
30 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
31 |
32 |
33 |
34 |
35 | Subject
36 | subject
37 | text
38 | subject
39 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
40 | col-name
41 | col-name
42 |
43 |
44 |
45 |
46 | Template
47 | template_identifier
48 | text
49 | template_identifier
50 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Template
51 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
52 |
53 |
54 |
55 |
56 | Store
57 | store_id
58 | store
59 |
60 |
61 |
62 |
63 | Sent at
64 | sent_at
65 | 1
66 | datetime
67 | sent_at
68 |
69 |
70 |
71 |
72 | Sent
73 | sent
74 | number
75 | sent
76 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Sent
77 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
78 |
79 |
80 |
81 |
82 | Recipients
83 | recipients
84 | longtext
85 | recipients
86 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Recipients
87 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
88 |
89 |
90 |
91 |
92 | Language
93 | language_code
94 | text
95 | language_code
96 |
97 |
98 |
99 |
100 | Actions
101 | action
102 | text
103 | entity_id
104 | Shockwavemk\Mail\Base\Block\Adminhtml\Edit\Tab\Mail\Grid\Renderer\Action
105 | Shockwavemk\Mail\Base\Block\Adminhtml\Widget\Grid\Column\Filter\Text
106 |
107 |
108 |
109 |
110 |
111 | mailGridJsObject
112 | Shockwavemk_Mail_Base::customer/edit/tab/mail.phtml
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/customer/edit/tab/mail.phtml:
--------------------------------------------------------------------------------
1 |
13 |
52 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/customer/mail/edit/info.phtml:
--------------------------------------------------------------------------------
1 |
198 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/customer/mail/edit/view.phtml:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/view/adminhtml/templates/customer/mail/send/form.phtml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------