├── .gitattributes
├── .github
├── FUNDING.yml
├── issue_template.md
└── workflows
│ └── php.yml
├── Block
└── Adminhtml
│ ├── System
│ └── Config
│ │ ├── Form
│ │ ├── Composer
│ │ │ └── Version.php
│ │ ├── Field
│ │ │ └── Link.php
│ │ └── Module
│ │ │ └── Version.php
│ │ └── ValidateConfigButton.php
│ └── ValidateConfig.php
├── Controller
└── Adminhtml
│ └── Validateconfig
│ └── Index.php
├── Helper
└── Data.php
├── Mail
└── Smtp.php
├── Model
├── Config
│ └── Source
│ │ └── Authtype.php
├── Email.php
└── Store.php
├── Plugin
└── Mail
│ └── TransportPlugin.php
├── README.md
├── composer.json
├── etc
├── acl.xml
├── adminhtml
│ ├── routes.xml
│ └── system.xml
├── config.xml
├── di.xml
├── email_templates.xml
└── module.xml
├── i18n
└── en_US.csv
├── registration.php
└── view
└── adminhtml
├── email
├── magento_email_test.html
└── zend_email_test.html
├── layout
└── magepalsmtp_validateconfig_index.xml
├── requirejs-config.js
├── templates
├── system
│ └── config
│ │ └── validateConfigButton.phtml
└── validateConfig
│ └── result.phtml
└── web
├── css
└── styles.less
└── js
└── validate-config.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.phtml linguist-language=PHP
2 | *.html linguist-language=PHP
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | #github: [srenon]
4 | custom: ['https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=github%20sponsor']
5 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | #### Magento version #:
10 |
11 | #### Edition (EE, CE, OS, etc):
12 |
13 | #### Expected behavior:
14 |
15 | #### Actual behavior:
16 |
17 | #### Steps to reproduce:
18 |
19 | #### Preconditions
20 |
21 |
22 |
23 |
24 |
40 |
--------------------------------------------------------------------------------
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHPCS
2 | on: [push]
3 | jobs:
4 | build:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@master
8 | - name: PHPCS
9 | run: docker run --rm -v $PWD:/code:ro domw/phpcs phpcs --colors --standard=Magento2 --report=full,summary,gitblame --extensions=php,phtml ./
10 | - name: compatibility
11 | run: docker run --rm -v $PWD:/code:ro domw/phpcompatibility phpcs --standard=PHPCompatibility --runtime-set testVersion 5.6-7.4 --colors --warning-severity=0 --report=full,summary --extensions=php,phtml ./
12 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/Composer/Version.php:
--------------------------------------------------------------------------------
1 | deploymentConfig = $deploymentConfig;
52 | $this->componentRegistrar = $componentRegistrar;
53 | $this->readFactory = $readFactory;
54 | parent::__construct($context, $data);
55 | }
56 |
57 | /**
58 | * Render button
59 | *
60 | * @param AbstractElement $element
61 | * @return string
62 | */
63 | public function render(AbstractElement $element)
64 | {
65 | // Remove scope label
66 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
67 | return parent::render($element);
68 | }
69 |
70 | /**
71 | * Return element html
72 | *
73 | * @param AbstractElement $element
74 | * @return string
75 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
76 | */
77 | protected function _getElementHtml(AbstractElement $element)
78 | {
79 | return 'v' . $this->getVersion();
80 | }
81 |
82 | /**
83 | * Get Module version number
84 | *
85 | * @return string
86 | */
87 | public function getVersion()
88 | {
89 | return $this->getComposerVersion($this->getModuleName());
90 | }
91 |
92 | /**
93 | * Get module composer version
94 | *
95 | * @param $moduleName
96 | * @return string
97 | */
98 | public function getComposerVersion($moduleName)
99 | {
100 | $path = $this->componentRegistrar->getPath(
101 | ComponentRegistrar::MODULE,
102 | $moduleName
103 | );
104 |
105 | try {
106 | $directoryRead = $this->readFactory->create($path);
107 | $composerJsonData = $directoryRead->readFile('composer.json');
108 |
109 | if ($composerJsonData) {
110 | $data = json_decode($composerJsonData);
111 | return !empty($data->version) ? $data->version : __('Unknown');
112 | }
113 | } catch (Exception $e) {
114 | return 'Unknown';
115 | }
116 |
117 | return 'Unknown';
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/Field/Link.php:
--------------------------------------------------------------------------------
1 | unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
27 | return parent::render($element);
28 | }
29 |
30 | /**
31 | * Return element html
32 | *
33 | * @param AbstractElement $element
34 | * @return string
35 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
36 | */
37 | protected function _getElementHtml(AbstractElement $element)
38 | {
39 | return sprintf(
40 | '%s',
41 | rtrim($this->_urlBuilder->getUrl('adminhtml/system_config/edit/section/system'), '/'),
42 | __('Stores > Configuration > Advanced > System > SMTP Configuration and Settings')
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/Form/Module/Version.php:
--------------------------------------------------------------------------------
1 | _moduleList = $moduleList;
36 | }
37 |
38 | /**
39 | * Render button
40 | *
41 | * @param AbstractElement $element
42 | * @return string
43 | * @throws LocalizedException
44 | */
45 | public function render(AbstractElement $element)
46 | {
47 | // Remove scope label
48 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
49 | return parent::render($element);
50 | }
51 |
52 | /**
53 | * Return element html
54 | *
55 | * @param AbstractElement $element
56 | * @return string
57 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
58 | */
59 | protected function _getElementHtml(AbstractElement $element)
60 | {
61 | return 'v' . $this->getVersion();
62 | }
63 |
64 | /**
65 | * Get Module version number
66 | *
67 | * @return string
68 | */
69 | public function getVersion()
70 | {
71 | $moduleInfo = $this->_moduleList->getOne($this->getModuleName());
72 | return $moduleInfo['setup_version'];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Block/Adminhtml/System/Config/ValidateConfigButton.php:
--------------------------------------------------------------------------------
1 | _urlBuilder = $context->getUrlBuilder();
34 | parent::__construct($context, $data);
35 | }
36 |
37 | /**
38 | * Set template
39 | *
40 | * @return void
41 | */
42 | protected function _construct()
43 | {
44 | parent::_construct();
45 | $this->setTemplate('MagePal_GmailSmtpApp::system/config/validateConfigButton.phtml');
46 | }
47 |
48 | /**
49 | * Generate button html
50 | *
51 | * @return string
52 | * @throws LocalizedException
53 | */
54 | public function getButtonHtml()
55 | {
56 | $button = $this->getLayout()->createBlock(
57 | Button::class
58 | )->setData(
59 | [
60 | 'id' => 'gmailsmtpapp_debug_result_button',
61 | 'label' => __('Send Test Email')
62 | ]
63 | );
64 |
65 | return $button->toHtml();
66 | }
67 |
68 | public function getAdminUrl()
69 | {
70 | return $this->_urlBuilder->getUrl(
71 | 'magepalsmtp/validateconfig',
72 | ['store' => $this->_request->getParam('store')]
73 | );
74 | }
75 |
76 | /**
77 | * Render button
78 | *
79 | * @param AbstractElement $element
80 | * @return string
81 | */
82 | public function render(AbstractElement $element)
83 | {
84 | // Remove scope label
85 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue();
86 | return parent::render($element);
87 | }
88 |
89 | /**
90 | * Return element html
91 | *
92 | * @param AbstractElement $element
93 | * @return string
94 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
95 | */
96 | protected function _getElementHtml(AbstractElement $element)
97 | {
98 | return $this->_toHtml();
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Block/Adminhtml/ValidateConfig.php:
--------------------------------------------------------------------------------
1 | '',
62 | 'name' => '',
63 | 'auth' => '',
64 | 'ssl' => '',
65 | 'smtphost' => '',
66 | 'smtpport' => '',
67 | 'username' => '',
68 | 'password' => '',
69 | 'set_reply_to' => '',
70 | 'set_from' => '',
71 | 'set_return_path' => '',
72 | 'return_path_email' => '',
73 | 'custom_from_email' => '',
74 | 'email' => '',
75 | 'from_email' => ''
76 | ];
77 |
78 | /**
79 | * @var EmailAddress
80 | */
81 | protected $emailAddressValidator;
82 |
83 | /**
84 | * EmailTest constructor.
85 | * @param Context $context
86 | * @param Data $dataHelper
87 | * @param Email $email
88 | * @param EmailAddress $emailAddressValidator
89 | * @param array $data
90 | */
91 | public function __construct(
92 | Context $context,
93 | Data $dataHelper,
94 | Email $email,
95 | EmailAddress $emailAddressValidator,
96 | array $data = []
97 | ) {
98 | parent::__construct($context, $data);
99 | $this->_dataHelper = $dataHelper;
100 | $this->_email = $email;
101 | $this->emailAddressValidator = $emailAddressValidator;
102 |
103 | $this->init();
104 | }
105 |
106 | /**
107 | * @param $id
108 | * @return $this
109 | */
110 | public function setStoreId($id)
111 | {
112 | $this->storeId = $id;
113 | return $this;
114 | }
115 |
116 | /**
117 | * @return string|null
118 | */
119 | public function getStoreId()
120 | {
121 | return $this->storeId;
122 | }
123 |
124 | /**
125 | * @param null $key
126 | * @return array|mixed|string
127 | */
128 | public function getConfig($key = null)
129 | {
130 | if ($key === null) {
131 | return $this->configFields;
132 | } elseif (!array_key_exists($key, $this->configFields)) {
133 | return '';
134 | } else {
135 | return $this->configFields[$key];
136 | }
137 | }
138 |
139 | /**
140 | * @param string $key
141 | * @param string $value
142 | * @return array|mixed|string
143 | */
144 | public function setConfig($key, $value = null)
145 | {
146 | if (array_key_exists($key, $this->configFields)) {
147 | $this->configFields[$key] = $value;
148 | }
149 |
150 | return $this;
151 | }
152 |
153 | /**
154 | * Load default config if config is lock using "bin/magento config:set"
155 | */
156 | public function loadDefaultConfig()
157 | {
158 | $request = $this->getRequest();
159 | $formPostArray = (array) $request->getPost();
160 |
161 | if ($request->getParam('website', false)) {
162 | $scopeCode = $request->getParam('website');
163 | $scopeType = ScopeInterface::SCOPE_WEBSITE;
164 | } elseif ($request->getParam('store', false)) {
165 | $scopeCode = $request->getParam('store');
166 | $scopeType = ScopeInterface::SCOPE_STORE;
167 | } else {
168 | $scopeCode = null;
169 | $scopeType = ScopeInterface::SCOPE_STORE;
170 | }
171 |
172 | $fields = array_keys($this->configFields);
173 | foreach ($fields as $field) {
174 | if (!array_key_exists($field, $formPostArray)) {
175 | $this->setConfig($field, $this->_dataHelper->getConfigValue($field, $scopeType, $scopeCode));
176 | } else {
177 | $this->setConfig($field, $request->getPost($field));
178 | }
179 | }
180 |
181 | //if password mask (6 stars)
182 | if ($this->getConfig('password') === '******') {
183 | $password = $this->_dataHelper->getConfigPassword($scopeType, $scopeCode);
184 | $this->setConfig('password', $password);
185 | }
186 |
187 | return $this;
188 | }
189 |
190 | /**
191 | * @return void
192 | */
193 | protected function init()
194 | {
195 | $request = $this->getRequest();
196 |
197 | $this->loadDefaultConfig();
198 |
199 | $this->toAddress = $this->getConfig('email') ? $this->getConfig('email') : $this->getConfig('username');
200 |
201 | $this->fromAddress = trim((string) $this->getConfig('from_email'));
202 |
203 | if (!$this->emailAddressValidator->isValid($this->fromAddress)) {
204 | $this->fromAddress = $this->toAddress;
205 | }
206 |
207 | $this->hash = time() . '.' . rand(300000, 900000);
208 | }
209 |
210 | /**
211 | * @return array
212 | */
213 | public function verify()
214 | {
215 | $settings = [
216 | 'server_email' => 'validateServerEmailSetting',
217 | 'magento_email_setting' => 'validateMagentoEmailStatus',
218 | 'module_email_setting' => 'validateModuleEmailStatus',
219 | 'magento_email' => 'validateMagentoEmailSetting'
220 | ];
221 |
222 | $result = $this->error();
223 | $hasError = false;
224 |
225 | foreach ($settings as $functionName) {
226 | $result = $this->$functionName();
227 |
228 | if (array_key_exists('has_error', $result)) {
229 | if ($result['has_error'] === true) {
230 | $hasError = true;
231 | break;
232 | }
233 | } else {
234 | $hasError = true;
235 | $result = $this->error(true, 'MP103 - Unknown Error');
236 | break;
237 | }
238 | }
239 |
240 | if (!$hasError) {
241 | $result['msg'] = __('Please check your email') . ' ' . $this->toAddress . ' ' .
242 | __('and flush your Magento cache');
243 | }
244 |
245 | return [$result];
246 | }
247 |
248 | /**
249 | * @return array
250 | * @throws \Magento\Framework\Exception\NoSuchEntityException
251 | */
252 | protected function validateServerEmailSetting()
253 | {
254 | $request = $this->getRequest();
255 |
256 | $username = $this->getConfig('username');
257 | $password = $this->getConfig('password');
258 |
259 | $auth = strtolower($this->getConfig('auth'));
260 |
261 | //if default view
262 | //see https://github.com/magento/magento2/issues/3019
263 | if (!$request->getParam('store', false)) {
264 | if ($auth != 'none' && (empty($username) || empty($password))) {
265 | return $this->error(
266 | true,
267 | __('Please enter a valid username/password')
268 | );
269 | }
270 | }
271 |
272 | $name = 'Test from MagePal SMTP';
273 | $from = trim((string) $this->getConfig('from_email'));
274 | $from = filter_var($from, FILTER_VALIDATE_EMAIL) ? $from : $username;
275 | $this->fromAddress = filter_var($username, FILTER_VALIDATE_EMAIL) ? $username : $from;
276 | $htmlBody = $this->_email->setTemplateVars(['hash' => $this->hash])->getEmailBody();
277 |
278 | $message = new SymfonyEmail();
279 | $message->to(new Address($this->toAddress, 'MagePal SMTP'))
280 | ->from(new Address($this->fromAddress, $name))
281 | ->subject('Hello from MagePal SMTP (1 of 2)')
282 | ->html($htmlBody);
283 |
284 | $result = $this->error();
285 |
286 | try {
287 | $transport = $this->getMailTransportSmtp();
288 | $transport->send($message);
289 | } catch (Exception $e) {
290 | $result = $this->error(true, __($e->getMessage()));
291 | }
292 |
293 | return $result;
294 | }
295 |
296 | public function getMailTransportSmtp()
297 | {
298 | $username = $this->getConfig('username');
299 | $password = $this->getConfig('password');
300 | $auth = strtolower($this->getConfig('auth'));
301 | $name = $this->getConfig('name');
302 | $host = $this->getConfig('smtphost');
303 | $port = $this->getConfig('smtpport');
304 | $ssl = $this->getConfig('ssl');
305 |
306 | $tls = ($auth !== 'none') && ($ssl !== 'starttls');
307 |
308 | /** @var SymfonyTransportInterface $transport */
309 | $transport = new EsmtpTransport($host, $port, $tls);
310 |
311 | if ($auth != 'none') {
312 | if ($username) {
313 | $transport->setUsername($username);
314 | }
315 |
316 | if ($password) {
317 | $transport->setPassword($password);
318 | }
319 | }
320 |
321 | switch ($auth) {
322 | case 'plain':
323 | $transport->setAuthenticators([new PlainAuthenticator()]);
324 | break;
325 | case 'login':
326 | $transport->setAuthenticators([new LoginAuthenticator()]);
327 | break;
328 | case 'none':
329 | break;
330 | default:
331 | throw new \InvalidArgumentException('Invalid authentication type: ' . $auth);
332 | }
333 |
334 | return $transport;
335 | }
336 |
337 | /**
338 | * @return array
339 | */
340 | protected function validateMagentoEmailSetting()
341 | {
342 | $result = $this->error();
343 |
344 | $this->_dataHelper->setTestMode(true);
345 | $this->_dataHelper->setStoreId($this->getStoreId());
346 | $this->_dataHelper->setTestConfig($this->getConfig());
347 |
348 | try {
349 | $this->_email
350 | ->setTemplateVars(['hash' => $this->hash])
351 | ->send(
352 | ['email' => $this->fromAddress, 'name' => 'Test from MagePal SMTP'],
353 | ['email' => $this->toAddress, 'name' => "MagePal SMTP"]
354 | );
355 | } catch (Exception $e) {
356 | $result = $this->error(true, __($e->getMessage()));
357 | }
358 |
359 | $this->_dataHelper->setTestMode(false);
360 |
361 | return $result;
362 | }
363 |
364 | /**
365 | * @return array
366 | */
367 | public function validateMagentoEmailStatus()
368 | {
369 | $result = $this->error();
370 | // system_smtp_disable
371 |
372 | if ($this->_dataHelper->getScopeConfigValue('system/smtp/disable')) {
373 | $result = $this->error(
374 | true,
375 | __('"Disable Email Communications" is set is "Yes", please set to "NO" in "Mail Sending Setting"')
376 | );
377 | }
378 |
379 | return $result;
380 | }
381 |
382 | /**
383 | * @return array
384 | */
385 | public function validateModuleEmailStatus()
386 | {
387 | $result = $this->error();
388 |
389 | if (!$this->getConfig('active')) {
390 | $result = $this->error(
391 | true,
392 | __('SMTP module is not enabled')
393 | );
394 | }
395 |
396 | return $result;
397 | }
398 |
399 | /**
400 | * Format error msg
401 | * @param string $s
402 | * @return string
403 | */
404 | public function formatErrorMsg($s)
405 | {
406 | return preg_replace(
407 | '@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@',
408 | '$1',
409 | nl2br($s)
410 | );
411 | }
412 |
413 | /**
414 | * @param bool $hasError
415 | * @param string $msg
416 | * @return array
417 | */
418 | public function error($hasError = false, $msg = '')
419 | {
420 | return [
421 | 'has_error' => (bool) $hasError,
422 | 'msg' => (string) $msg
423 | ];
424 | }
425 | }
426 |
--------------------------------------------------------------------------------
/Controller/Adminhtml/Validateconfig/Index.php:
--------------------------------------------------------------------------------
1 | resultFactory->create(ResultFactory::TYPE_LAYOUT);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Helper/Data.php:
--------------------------------------------------------------------------------
1 | testConfig;
34 | } elseif (!array_key_exists($key, $this->testConfig)) {
35 | return '';
36 | } else {
37 | return $this->testConfig[$key];
38 | }
39 | }
40 |
41 | /**
42 | * @param null $fields
43 | * @return $this
44 | */
45 | public function setTestConfig($fields)
46 | {
47 | $this->testConfig = (array) $fields;
48 | return $this;
49 | }
50 |
51 | /**
52 | * @param null $store_id
53 | * @return bool
54 | */
55 | public function isActive($scopeType = null, $scopeCode = null)
56 | {
57 | //use global store
58 | if ($scopeType === null) {
59 | $scopeType = ScopeInterface::SCOPE_STORE;
60 | }
61 |
62 | //use global store id
63 | if ($scopeCode === null && $this->getStoreId() > 0) {
64 | $scopeCode = $this->getStoreId();
65 | }
66 |
67 | return $this->scopeConfig->isSetFlag(
68 | 'system/gmailsmtpapp/active',
69 | $scopeType,
70 | $scopeCode
71 | );
72 | }
73 |
74 | /**
75 | * Get local client name
76 | *
77 | * @param null $scopeType
78 | * @param null $scopeCode
79 | * @return string
80 | */
81 | public function getConfigName($scopeType = null, $scopeCode = null)
82 | {
83 | return $this->getConfigValue('name', $scopeType, $scopeCode);
84 | }
85 |
86 | /**
87 | * Get system config password
88 | *
89 | * @param null $scopeType
90 | * @param null $scopeCode
91 | * @return string
92 | */
93 | public function getConfigPassword($scopeType = null, $scopeCode = null)
94 | {
95 | return $this->getConfigValue('password', $scopeType, $scopeCode);
96 | }
97 |
98 | /**
99 | * Get system config username
100 | *
101 | * @param null $scopeType
102 | * @param null $scopeCode
103 | * @return string
104 | */
105 | public function getConfigUsername($scopeType = null, $scopeCode = null)
106 | {
107 | return $this->getConfigValue('username', $scopeType, $scopeCode);
108 | }
109 |
110 | /**
111 | * Get system config auth
112 | *
113 | * @param null $scopeType
114 | * @param null $scopeCode
115 | * @return string
116 | */
117 | public function getConfigAuth($scopeType = null, $scopeCode = null)
118 | {
119 | return $this->getConfigValue('auth', $scopeType, $scopeCode);
120 | }
121 |
122 | /**
123 | * Get system config ssl
124 | *
125 | * @param null $scopeType
126 | * @param null $scopeCode
127 | * @return string
128 | */
129 | public function getConfigSsl($scopeType = null, $scopeCode = null)
130 | {
131 | return $this->getConfigValue('ssl', $scopeType, $scopeCode);
132 | }
133 |
134 | /**
135 | * Get system config host
136 | *
137 | * @param null $scopeType
138 | * @param null $scopeCode
139 | * @return string
140 | */
141 | public function getConfigSmtpHost($scopeType = null, $scopeCode = null)
142 | {
143 | return $this->getConfigValue('smtphost', $scopeType, $scopeCode);
144 | }
145 |
146 | /**
147 | * Get system config port
148 | *
149 | * @param null $scopeType
150 | * @param null $scopeCode
151 | * @return string
152 | */
153 | public function getConfigSmtpPort($scopeType = null, $scopeCode = null)
154 | {
155 | return $this->getConfigValue('smtpport', $scopeType, $scopeCode);
156 | }
157 |
158 | /**
159 | * Get system config reply to
160 | *
161 | * @param null $scopeType
162 | * @param null $scopeCode
163 | * @return bool
164 | */
165 | public function getConfigSetReplyTo($scopeType = null, $scopeCode = null)
166 | {
167 | //use global store
168 | if ($scopeType === null) {
169 | $scopeType = ScopeInterface::SCOPE_STORE;
170 | }
171 |
172 | //use global store id
173 | if ($scopeCode === null && $this->getStoreId() > 0) {
174 | $scopeCode = $this->getStoreId();
175 | }
176 |
177 | return $this->scopeConfig->isSetFlag(
178 | 'system/gmailsmtpapp/set_reply_to',
179 | $scopeType,
180 | $scopeCode
181 | );
182 | }
183 |
184 | /**
185 | * Get system config set return path
186 | *
187 | * @param null $scopeType
188 | * @param null $scopeCode
189 | * @return int
190 | */
191 | public function getConfigSetReturnPath($scopeType = null, $scopeCode = null)
192 | {
193 | return (int) $this->getConfigValue('set_return_path', $scopeType, $scopeCode);
194 | }
195 |
196 | /**
197 | * Get system config return path email
198 | *
199 | * @param null $scopeType
200 | * @param null $scopeCode
201 | * @return string
202 | */
203 | public function getConfigReturnPathEmail($scopeType = null, $scopeCode = null)
204 | {
205 | return $this->getConfigValue('return_path_email', $scopeType, $scopeCode);
206 | }
207 |
208 | /**
209 | * Get system config from
210 | *
211 | * @param null $scopeType
212 | * @param null $scopeCode
213 | * @return string
214 | */
215 | public function getConfigSetFrom($scopeType = null, $scopeCode = null)
216 | {
217 | return (int) $this->getConfigValue('set_from', $scopeType, $scopeCode);
218 | }
219 |
220 | /**
221 | * Get system config from
222 | *
223 | * @param null $scopeType
224 | * @param null $scopeCode
225 | * @return string
226 | */
227 | public function getConfigCustomFromEmail($scopeType = null, $scopeCode = null)
228 | {
229 | return $this->getConfigValue('custom_from_email', $scopeType, $scopeCode);
230 | }
231 |
232 | /**
233 | * Get system config
234 | *
235 | * @param String $path path
236 | * @param null $scopeType
237 | * @param null $scopeCode
238 | * @return string
239 | */
240 | public function getConfigValue($path, $scopeType = null, $scopeCode = null)
241 | {
242 | //send test mail
243 | if ($this->isTestMode()) {
244 | return $this->getTestConfig($path);
245 | }
246 |
247 | //return value from core config
248 | return $this->getScopeConfigValue(
249 | "system/gmailsmtpapp/{$path}",
250 | $scopeType,
251 | $scopeCode
252 | );
253 | }
254 |
255 | /**
256 | * @param String path
257 | * @param null $scopeType
258 | * @param null $scopeCode
259 | * @return mixed
260 | */
261 | public function getScopeConfigValue($path, $scopeType = null, $scopeCode = null)
262 | {
263 | //use global store id
264 | if ($scopeType === null) {
265 | $scopeType = ScopeInterface::SCOPE_STORE;
266 | }
267 |
268 | //use global store id
269 | if ($scopeCode === null && $this->getStoreId() > 0) {
270 | $scopeCode = $this->getStoreId();
271 | }
272 |
273 | //return value from core config
274 | return $this->scopeConfig->getValue(
275 | $path,
276 | $scopeType,
277 | $scopeCode
278 | );
279 | }
280 |
281 | /**
282 | * @return int/null
283 | */
284 | public function getStoreId()
285 | {
286 | return $this->storeId;
287 | }
288 |
289 | /**
290 | * @param int/null $storeId
291 | */
292 | public function setStoreId($storeId = null)
293 | {
294 | $this->storeId = $storeId;
295 | return $this;
296 | }
297 |
298 | /**
299 | * @return bool
300 | */
301 | public function isTestMode()
302 | {
303 | return (bool) $this->testMode;
304 | }
305 |
306 | /**
307 | * @param bool $testMode
308 | * @return Data
309 | */
310 | public function setTestMode($testMode)
311 | {
312 | $this->testMode = (bool) $testMode;
313 | return $this;
314 | }
315 | }
316 |
--------------------------------------------------------------------------------
/Mail/Smtp.php:
--------------------------------------------------------------------------------
1 | dataHelper = $dataHelper;
47 | $this->storeModel = $storeModel;
48 | }
49 |
50 | /**
51 | * @param Data $dataHelper
52 | * @return Smtp
53 | */
54 | public function setDataHelper(Data $dataHelper)
55 | {
56 | $this->dataHelper = $dataHelper;
57 | return $this;
58 | }
59 |
60 | /**
61 | * @param Store $storeModel
62 | * @return Smtp
63 | */
64 | public function setStoreModel(Store $storeModel)
65 | {
66 | $this->storeModel = $storeModel;
67 | return $this;
68 | }
69 |
70 | /**
71 | * @param SymfonyMessage $message
72 | * @throws MailException
73 | */
74 | public function sendSmtpMessage(
75 | $message
76 | ) {
77 | $dataHelper = $this->dataHelper;
78 | $dataHelper->setStoreId($this->storeModel->getStoreId());
79 |
80 | $this->setReplyToPath($message);
81 | $this->setSender($message);
82 |
83 | try {
84 | $host = $dataHelper->getConfigSmtpHost();
85 | $port = $dataHelper->getConfigSmtpPort();
86 | $username = $dataHelper->getConfigUsername();
87 | $password = $dataHelper->getConfigPassword();
88 | $auth = strtolower($dataHelper->getConfigAuth());
89 | $ssl = $dataHelper->getConfigSsl();
90 |
91 | $tls = ($auth !== 'none') && ($ssl !== 'starttls');
92 |
93 | /** @var SymfonyTransportInterface $transport */
94 | $transport = new EsmtpTransport($host, $port, $tls);
95 |
96 | if ($username) {
97 | $transport->setUsername($username);
98 | }
99 |
100 | if ($password) {
101 | $transport->setPassword($password);
102 | }
103 |
104 | switch ($auth) {
105 | case 'plain':
106 | $transport->setAuthenticators([new PlainAuthenticator()]);
107 | break;
108 | case 'login':
109 | $transport->setAuthenticators([new LoginAuthenticator()]);
110 | break;
111 | case 'none':
112 | break;
113 | default:
114 | throw new \InvalidArgumentException('Invalid authentication type: ' . $auth);
115 | }
116 |
117 |
118 | $mailer = new Mailer($transport);
119 | $mailer->send($message);
120 |
121 | } catch (Exception $e) {
122 | throw new MailException(
123 | new Phrase($e->getMessage()),
124 | $e
125 | );
126 | }
127 | }
128 |
129 | /**
130 | *
131 | * @param SymfonyMessage $message
132 | */
133 | protected function setSender($message)
134 | {
135 | $dataHelper = $this->dataHelper;
136 | $messageFromAddress = $this->getMessageFromAddressObject($message);
137 |
138 | //Set from address
139 | switch ($dataHelper->getConfigSetFrom()) {
140 | case 1:
141 | $setFromEmail = $messageFromAddress;
142 | break;
143 | case 2:
144 | $setFromEmail = $dataHelper->getConfigCustomFromEmail();
145 | break;
146 | default:
147 | $setFromEmail = null;
148 | break;
149 | }
150 |
151 | if ($setFromEmail !== null && $dataHelper->getConfigSetFrom()) {
152 | if ($setFromEmail instanceof Address) {
153 | $message->getHeaders()->addMailboxListHeader('Sender', [$setFromEmail]);
154 | } elseif (!empty($setFromEmail)) {
155 | $name = $messageFromAddress instanceof Address ? $messageFromAddress->getName() : $setFromEmail;
156 |
157 | $message->getHeaders()->addMailboxListHeader(
158 | 'Sender',
159 | [new Address($setFromEmail, $name)]
160 | );
161 | }
162 | }
163 | }
164 |
165 | /**
166 | * @param SymfonyMessage $message
167 | */
168 | protected function setReplyToPath($message)
169 | {
170 | $dataHelper = $this->dataHelper;
171 | $messageFromAddress = $this->getMessageFromAddressObject($message);
172 | /*
173 | * Set reply-to path
174 | * 0 = No
175 | * 1 = From
176 | * 2 = Custom
177 | */
178 | switch ($dataHelper->getConfigSetReturnPath()) {
179 | case 1:
180 | $returnPathEmail = $messageFromAddress;
181 | break;
182 | case 2:
183 | $returnPathEmail = $dataHelper->getConfigReturnPathEmail();
184 | break;
185 | default:
186 | $returnPathEmail = null;
187 | break;
188 | }
189 |
190 | if (empty($message->getHeaders()->get('reply-to')?->getAddresses()) && $dataHelper->getConfigSetReplyTo()) {
191 | if ($returnPathEmail instanceof Address) {
192 | $message->getHeaders()->addMailboxListHeader('reply-to', [$returnPathEmail]);
193 | } elseif (!empty($returnPathEmail)) {
194 | $name = $messageFromAddress instanceof Address ? $messageFromAddress->getName() : $returnPathEmail;
195 |
196 | $message->getHeaders()->addMailboxListHeader(
197 | 'reply-to',
198 | [new Address($returnPathEmail, $name)]
199 | );
200 | }
201 | }
202 | }
203 |
204 | /**
205 | *
206 | * @param SymfonyMessage $message
207 | * @return null|Address
208 | */
209 | protected function getMessageFromAddressObject($message)
210 | {
211 | if (!empty($fromAddresses = $message->getHeaders()->get('From')?->getAddresses())) {
212 | reset($fromAddresses);
213 | return current($fromAddresses);
214 | }
215 |
216 | return null;
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/Model/Config/Source/Authtype.php:
--------------------------------------------------------------------------------
1 | 'none', 'label' => __('None')],
20 | ['value' => 'ssl', 'label' => 'SSL'],
21 | ['value' => 'tls', 'label' => 'TLS'],
22 | ['value' => 'starttls', 'label' => 'STARTTLS']
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Model/Email.php:
--------------------------------------------------------------------------------
1 | dataHelper = $dataHelper;
96 | $this->templateFactory = $templateFactory;
97 | $this->scopeConfig = $scopeConfig;
98 | $this->storeManager = $storeManager;
99 | $this->inlineTranslation = $inlineTranslation;
100 | $this->_transportBuilder = $transportBuilder;
101 | }
102 |
103 | /**
104 | * @param Mixed $senderInfo
105 | * @param Mixed $receiverInfo
106 | * @return $this
107 | * @throws NoSuchEntityException
108 | */
109 | public function generateTemplate($senderInfo, $receiverInfo)
110 | {
111 | $templateId = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_MAGENTO_TEST);
112 | $this->getTransportBuilder()
113 | ->setTemplateIdentifier($templateId)
114 | ->setTemplateOptions(
115 | [
116 | 'area' => Area::AREA_ADMINHTML,
117 | 'store' => $this->getStore()->getId(),
118 | ]
119 | )
120 | ->setTemplateVars($this->templateVars)
121 | ->setFrom($senderInfo)
122 | ->addTo($receiverInfo['email'], $receiverInfo['name']);
123 |
124 | return $this;
125 | }
126 |
127 | /**
128 | * @param $senderInfo
129 | * @param $receiverInfo
130 | * @throws MailException
131 | * @throws NoSuchEntityException
132 | * @throws LocalizedException
133 | */
134 | public function send($senderInfo, $receiverInfo)
135 | {
136 | $this->inlineTranslation->suspend();
137 | $this->generateTemplate($senderInfo, $receiverInfo);
138 | $transport = $this->_transportBuilder->getTransport();
139 | $result = $transport->sendMessage();
140 | $this->inlineTranslation->resume();
141 |
142 | return $result;
143 | }
144 |
145 | /**
146 | * @return TemplateInterface
147 | * @throws NoSuchEntityException
148 | */
149 | protected function getTemplate()
150 | {
151 | $this->setTemplateOptions(
152 | [
153 | 'area' => Area::AREA_ADMINHTML,
154 | 'store' => $this->getStore()->getId(),
155 | ]
156 | );
157 |
158 | $templateIdentifier = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_ZEND_TEST);
159 |
160 | return $this->templateFactory->get($templateIdentifier, $this->templateModel)
161 | ->setVars($this->templateVars)
162 | ->setOptions($this->templateOptions);
163 | }
164 |
165 | /**
166 | * @return mixed
167 | * @throws NoSuchEntityException
168 | */
169 | public function getEmailBody()
170 | {
171 | return $this->getTemplate()->processTemplate();
172 | }
173 |
174 | /**
175 | * Return template id according to store
176 | *
177 | * @param $xmlPath
178 | * @return mixed
179 | * @throws NoSuchEntityException
180 | */
181 | public function getTemplateId($xmlPath)
182 | {
183 | return $this->getConfigValue($xmlPath, $this->getStore()->getStoreId());
184 | }
185 |
186 | /**
187 | * Return store configuration value of your template field that which id you set for template
188 | *
189 | * @param string $path
190 | * @param int $storeId
191 | * @return mixed
192 | */
193 | protected function getConfigValue($path, $storeId)
194 | {
195 | return $this->scopeConfig->getValue(
196 | $path,
197 | ScopeInterface::SCOPE_STORE,
198 | $storeId
199 | );
200 | }
201 |
202 | /**
203 | * Return store
204 | *
205 | * @return StoreInterface
206 | * @throws NoSuchEntityException
207 | */
208 | public function getStore()
209 | {
210 | return $this->storeManager->getStore();
211 | }
212 |
213 | /**
214 | * @param mixed $templateVars
215 | * @return Email
216 | */
217 | public function setTemplateVars($templateVars)
218 | {
219 | $this->templateVars = (array) $templateVars;
220 | return $this;
221 | }
222 |
223 | /**
224 | * @param mixed $templateOptions
225 | * @return Email
226 | */
227 | public function setTemplateOptions($templateOptions)
228 | {
229 | $this->templateOptions = (array) $templateOptions;
230 | return $this;
231 | }
232 |
233 | /**
234 | * Set template model
235 | *
236 | * @param string $templateModel
237 | * @return $this
238 | */
239 | public function setTemplateModel($templateModel)
240 | {
241 | $this->templateModel = $templateModel;
242 | return $this;
243 | }
244 |
245 | /**
246 | * @return TransportBuilder
247 | */
248 | public function getTransportBuilder()
249 | {
250 | return $this->_transportBuilder;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/Model/Store.php:
--------------------------------------------------------------------------------
1 | store_id;
26 | }
27 |
28 | /**
29 | * @param $store_id
30 | * @return $this
31 | */
32 | public function setStoreId($store_id)
33 | {
34 | $this->store_id = $store_id;
35 | return $this;
36 | }
37 |
38 | /**
39 | * @return string|array
40 | */
41 | public function getFrom()
42 | {
43 | return $this->from;
44 | }
45 |
46 | /**
47 | * @param string|array $from
48 | * @return $this
49 | */
50 | public function setFrom($from)
51 | {
52 | $this->from = $from;
53 | return $this;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Plugin/Mail/TransportPlugin.php:
--------------------------------------------------------------------------------
1 | dataHelper = $dataHelper;
48 | $this->storeModel = $storeModel;
49 | $this->smtpFactory = $smtpFactory;
50 | }
51 |
52 | /**
53 | * @param TransportInterface $subject
54 | * @param Closure $proceed
55 | * @throws MailException
56 | */
57 | public function aroundSendMessage(
58 | TransportInterface $subject,
59 | Closure $proceed
60 | ) {
61 | if ($this->dataHelper->isActive()) {
62 | if (method_exists($subject, 'getStoreId')) {
63 | $this->storeModel->setStoreId($subject->getStoreId());
64 | }
65 |
66 | $message = $subject->getMessage()->getSymfonyMessage();
67 |
68 | if ($message instanceof SymfonyMessage) {
69 | /** @var Smtp $smtp */
70 | $smtp = $this->smtpFactory->create(
71 | ['dataHelper' => $this->dataHelper, 'storeModel' => $this->storeModel]
72 | );
73 | $smtp->sendSmtpMessage($message);
74 | } else {
75 | $proceed();
76 | }
77 | } else {
78 | $proceed();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Magento 2 SMTP Extension - Gmail, G Suite, Amazon SES, Office 365, Mailgun, SendGrid, Mandrill and other SMTP servers.
4 |
5 | [](https://www.magepal.com/magento2/extensions/custom-smtp.html)
6 | [](https://www.magepal.com/magento2/extensions/custom-smtp.html)
7 | [](https://www.magepal.com/magento2/extensions/custom-smtp.html)
8 | [](https://www.magepal.com/magento2/extensions/custom-smtp.html)
9 |
10 | ##### For Magento / Adobe Commerce 2.0.x, 2.1.x, 2.2.x, 2.3.x and 2.4.x
11 |
12 |
13 | ### OAuth SMTP Extension
14 | Tired of dealing with email password issues? Upgrade to our OAuth SMTP Extension for Magento 2 and enjoy secure, password-free email delivery. Learn more about our OAuth SMTP Extension and get started today!
15 |
16 | Configure Magento 2 / Adobe Commerce to send all transactional emails using Google App, Gmail, Amazon Simple Email Service (SES), Microsoft Office365 or any other SMTP servers.
17 |
18 |
19 |
20 | Sending transactional emails to customers is a vital part of running an e-commerce store. Our free custom Magento extension integrates with all major email service providers and third-party SMTP servers to reliably deliver messages to customers' inboxes.
21 |
22 | #### What is SMTP - Simple Mail Transfer Protocol
23 | SMTP or Simple Mail Transfer Protocol allows you to send emails from your Magento 2 store through a specific third-party mail SMTP server. For example, if you want to use your Gmail, Amazon, Microsoft or any other mail server account to send email from your Magento web store, all you need is to configure that mail server setting in our extension in Magento without having to do any complex server configuration.
24 |
25 |
26 |
27 | #### Why use a Custom SMTP Server with Magento
28 |
29 | By default, most hosting companies' mail servers are configured to send email from unauthorized senders which prevents emails from reliable delivered to recipients. Therefore, most Magento store owners struggle to limit the number of transactional emails that end up in clients' junk mail. Take full control of your email sending settings in Magento 2 and reduce sending emails to your valuable customers' junk mail folder. Emails are delivered instantaneously to their intended recipients without delays or get trap in the spam folder.
30 |
31 | Out of the box, Magento 2 doesn't provide the ability to specify your custom SMTP settings for outgoing emails using an external SMTP server. Using this extension bridges the gap and allows your Magento store to connect to your preferred email provider securely and easily.
32 |
33 | All you need is either an (i) free Gmail account, (ii) paid Google Apps account or any other SMTP service (i.e Amazon Simple Email Service / Amazon SES, Microsoft Office365).
34 |
35 | Learn more about our [custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Learn%20More) extension.
36 |
37 |
38 | > ### NOTE - Gmail / Google Account
39 | >To help keep your account secure, starting May 30, 2022, Google will no longer support the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password.
40 |
41 | >Please note this deadline does not apply to Google Workspace or Google Cloud Identity customers. The enforcement date for these customers will be announced on the Workspace blog at a later date.
42 |
43 | >For more information, please continue reading.
44 |
45 | >Special Note on Apple Device Sign-Ins. Users who have not recently signed into their Google Account using only username and password will be able to only make new sign in attempts using the Google account type starting from February 28, 2022. Existing users may continue to sign into their Google Account using their username and password until May 30, 2022.
46 |
47 |
48 | ### Benefits of using Gmail SMTP
49 | Since Google's, Gmail and G Suite SMTP server does not use Port 25, you'll reduce the probability that an ISP might block your email or flag it as SPAM. Also, all your emails sent from Magento will be searchable and backed-up in your email account on Google's servers.
50 |
51 | ### Features
52 | * Send emails through virtually any external SMTP server from your Magento store
53 | * Easily configure Magento 2 SMTP settings from within Magento2 store admin
54 | * Complete control of custom SMTP server settings: Hostname, Port, Username, Password, ...
55 | * Self-test option, which lets you verify your email credentials are correct before saving
56 | * Support Multi-store, configurable different email providers/accounts per store
57 | * Support secure SMTP servers: TLS / SSL, Plain-text, username/password, CRAM-MD5 authentication
58 | * Customize email headers: From / Reply-To / Return-Path
59 | * Easily disable/enable our extension from admin
60 | * Developer Friendly
61 | * Integrate with any third-party SMTP server
62 |
63 | Get more from your order confirmation emails by promoting other complementary products and services.
64 | Learn more about our new Enhanced Transactional Email extension.
65 |
66 | ### Documentation
67 |
68 | - [How to Install SMTP Magento 2 / Adobe Commerce Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#installation)
69 |
70 | - [How to setup Magento 2 / Adobe Commerce SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#configuration)
71 |
72 | - [How to debugging Magento 2 / Adobe Commerce SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#debug)
73 |
74 |
75 | ### SMTP Service Providers
76 | * Gmail
77 | * Google App
78 | * G Suite
79 | * Amazon Simple Email Service (SES)
80 | * Microsoft Office365
81 | * Outlook
82 | * SparkPost
83 | * GoDaddy
84 | * Mandrill
85 | * MailGun
86 | * SendGrid
87 | * Elastic Email
88 | * Hotmail
89 | * AOL Mail
90 | * Yahoo Mail
91 | * AT&T
92 | * Verizon
93 | * Postmark
94 | * O2 Mail
95 | * Zoho
96 | * Mailjet
97 | * Mail.com
98 | * Your Company SMTP Server
99 | * and many other SMTP servers
100 |
101 | ### How to Install Magento SMTP Extension
102 |
103 | ##### Using Composer (recommended)
104 |
105 | ```sh
106 | composer require magepal/magento2-gmailsmtpapp
107 | ```
108 |
109 | Contribution
110 | ---
111 | Want to contribute to this extension? The quickest way is to open a [pull request on GitHub](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
112 |
113 | Support
114 | ---
115 | If you encounter any problems or bugs, please open an issue on [GitHub](https://github.com/magepal/magento2-gmail-smtp-app/issues). For fast Premium Support visit our [Custom SMTP Extension](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Premium%20Support) product page for detail.
116 |
117 | Need help setting up or want to customize this extension to meet your business needs? Please email support@magepal.com and if we like your idea we will add this feature for free or at a discounted rate.
118 |
119 | Magento 2 Extensions
120 | ---
121 | - [Custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html)
122 | - [Catalog Hover Image for Adobe Commerce](https://www.magepal.com/magento2/extensions/catalog-hover-image-for-magento.html)
123 | - [Enhanced Success Page for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-success-page.html)
124 | - [Enhanced Transactional Emails for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html)
125 | - [Google Tag Manager](https://www.magepal.com/magento2/extensions/google-tag-manager.html)
126 | - [Enhanced E-commerce](https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html)
127 | - [Reindex](https://www.magepal.com/magento2/extensions/reindex.html)
128 | - [Custom Shipping Method](https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html)
129 | - [Preview Order Confirmation](https://www.magepal.com/magento2/extensions/preview-order-confirmation-page-for-magento-2.html)
130 | - [Guest to Customer](https://www.magepal.com/magento2/extensions/guest-to-customer.html)
131 | - [Admin Form Fields Manager](https://www.magepal.com/magento2/extensions/admin-form-fields-manager-for-magento-2.html)
132 | - [Customer Dashboard Links Manager](https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html)
133 | - [Lazy Loader](https://www.magepal.com/magento2/extensions/lazy-load.html)
134 | - [Order Confirmation Page Miscellaneous Scripts](https://www.magepal.com/magento2/extensions/order-confirmation-miscellaneous-scripts-for-magento-2.html)
135 | - [HTML Minifier for Magento2](https://www.magepal.com/magento2/extensions/html-minifier.html)
136 |
137 | © MagePal LLC. | [www.magepal.com](https://www.magepal.com)
138 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "magepal/magento2-gmailsmtpapp",
3 | "description":"Magento 2 SMTP Extension - Configure Magento 2 to send all transactional email using Gmail, G Suite, Amazon SES, Office360, Mailgun, SendGrid, Mandrill or any other SMTP servers",
4 | "keywords": [
5 | "magento 2",
6 | "gmail smtp",
7 | "google app",
8 | "magento2 email",
9 | "Amazon Simple Email Service",
10 | "Amazon SES",
11 | "magento2 smtp",
12 | "magento2 email setup",
13 | "send email magento2",
14 | "g suite",
15 | "how to configure magento email",
16 | "how to setup email magento2"
17 | ],
18 | "require": {
19 | "php": "~8.2.0||~8.3.0||~8.4.0",
20 | "magento/module-backend": "102.0.*",
21 | "magento/framework": "103.0.*",
22 | "magento/module-email": "101.1.8||101.1.8-p1||101.1.8-p2",
23 | "magepal/magento2-core":">1.1.0"
24 | },
25 | "type": "magento2-module",
26 | "version": "3.0.2",
27 | "license": [
28 | "proprietary"
29 | ],
30 | "homepage": "https://www.magepal.com/",
31 | "support": {
32 | "email": "support@magepal.com",
33 | "issues": "https://github.com/magepal/magento2-gmail-smtp-app/issues/"
34 | },
35 | "authors": [
36 | {
37 | "name": "Renon Stewart",
38 | "email": "renon@magepal.com",
39 | "homepage": "https://www.magepal.com/",
40 | "role": "Leader"
41 | }
42 | ],
43 | "autoload": {
44 | "files": [
45 | "registration.php"
46 | ],
47 | "psr-4": {
48 | "MagePal\\GmailSmtpApp\\": ""
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/etc/acl.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
36 | ]]>
37 |
65 | Tired of dealing with email password issues? Upgrade to our OAuth SMTP Extension for Magento 2 and enjoy secure, password-free email delivery. Learn more about our OAuth SMTP Extension and get started today!
66 |
68 | ]]>
69 |
212 |
213 |
214 |
215 |
216 |
233 |
217 |
218 |
219 |
220 |
232 |
221 |
230 |
231 |
222 |
229 |
223 |
224 |
225 |
226 |
227 |
228 |
234 |
235 |
367 |
236 |
237 |
238 |
239 |
366 |
240 |
364 |
365 |
241 |
363 |
242 |
243 |
244 |
251 |
252 |
253 |
254 |
255 |
362 |
256 |
258 |
259 |
260 |
261 |
262 | Congratulations! Your SMTP is setup correctly
263 |
267 |
268 |
264 |
266 | Get more MagePal extensions today! 265 |
269 |
288 |
289 |
290 |
297 |
298 |
299 |
300 |
301 |
270 |
287 |
302 |
304 |
305 |
306 |
307 |
308 |
309 | Help us by "starring" our extensions on github.com/MagePal
310 |
360 |
361 | Stay up to date by following us on
311 |
312 |
313 |
359 |
368 |
387 |
369 |
386 |
370 |
371 |
372 |
373 |
385 |
374 |
383 |
384 |
375 |
382 |
376 |
377 |
378 |
379 |
380 |
381 | |
388 |
212 |
213 |
214 |
215 |
216 |
233 |
217 |
218 |
219 |
220 |
232 |
221 |
230 |
231 |
222 |
229 |
223 |
224 |
225 |
226 |
227 |
228 |
234 |
235 |
366 |
236 |
237 |
238 |
239 |
365 |
240 |
363 |
364 |
241 |
362 |
242 |
243 |
244 |
251 |
252 |
253 |
254 |
361 |
255 |
257 |
258 |
259 |
260 |
261 | Congratulations! Your server is configured correctly.
262 |
266 |
267 |
263 |
265 | Get more MagePal extensions today! 264 |
268 |
287 |
288 |
289 |
296 |
297 |
298 |
299 |
300 |
269 |
286 |
301 |
303 |
304 |
305 |
306 |
307 |
308 | Help us by "starring" our extensions on github.com/MagePal
309 |
359 |
360 | Stay up to date by following us on:
310 |
311 |
312 |
358 |
367 |
386 |
368 |
385 |
369 |
370 |
371 |
372 |
384 |
373 |
382 |
383 |
374 |
381 |
375 |
376 |
377 |
378 |
379 |
380 | |
387 |
Congratulations, You Did It!
173 |= $block->escapeHtml($result['msg']) ?>
174 | 175 |181 | Enhanced E-commerce for Google Tag Manager 182 | | 183 ||
185 | Enhanced Success Page 186 | | 187 |188 | Enhanced Sales Email 189 | | 190 |
---|
Bummer!
204 |= $block->escapeHtml($block->formatErrorMsg($result['msg']), ['a']) ?>
206 |212 | Our affordable technical support include help with installation, usage, configuration, and troubleshooting 213 | our Free Open Source extensions. 214 |
215 |216 | Visit www.magepal.com to learn more about our 217 | Premium 30 Minutes Paid Support. 219 |
220 | 221 | 222 | -------------------------------------------------------------------------------- /view/adminhtml/web/css/styles.less: -------------------------------------------------------------------------------- 1 | svg { 2 | width: 50px; 3 | display: block; 4 | margin: 20px auto 0; 5 | } 6 | 7 | .path { 8 | stroke-dasharray: 1000; 9 | stroke-dashoffset: 0; 10 | &.circle { 11 | -webkit-animation: dash .9s ease-in-out; 12 | animation: dash .9s ease-in-out; 13 | } 14 | &.line { 15 | stroke-dashoffset: 1000; 16 | -webkit-animation: dash .9s .35s ease-in-out forwards; 17 | animation: dash .9s .35s ease-in-out forwards; 18 | } 19 | &.check { 20 | stroke-dashoffset: -100; 21 | -webkit-animation: dash-check .9s .35s ease-in-out forwards; 22 | animation: dash-check .9s .35s ease-in-out forwards; 23 | } 24 | } 25 | 26 | .modal-popup { 27 | p { 28 | text-align: center; 29 | margin: 20px 0 60px; 30 | font-size: 1.25em; 31 | &.success { 32 | color: #73AF55; 33 | } 34 | &.error { 35 | color: #D06079; 36 | } 37 | } 38 | } 39 | 40 | 41 | 42 | @-webkit-keyframes dash { 43 | 0% { 44 | stroke-dashoffset: 1000; 45 | } 46 | 100% { 47 | stroke-dashoffset: 0; 48 | } 49 | } 50 | 51 | @keyframes dash { 52 | 0% { 53 | stroke-dashoffset: 1000; 54 | } 55 | 100% { 56 | stroke-dashoffset: 0; 57 | } 58 | } 59 | 60 | @-webkit-keyframes dash-check { 61 | 0% { 62 | stroke-dashoffset: -100; 63 | } 64 | 100% { 65 | stroke-dashoffset: 900; 66 | } 67 | } 68 | 69 | @keyframes dash-check { 70 | 0% { 71 | stroke-dashoffset: -100; 72 | } 73 | 100% { 74 | stroke-dashoffset: 900; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/validate-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © MagePal LLC. All rights reserved. 3 | * See COPYING.txt for license details. 4 | * https://www.magepal.com | support@magepal.com 5 | */ 6 | 7 | define([ 8 | 'jquery', 9 | 'Magento_Ui/js/modal/alert' 10 | ], function ($, alert) { 11 | 12 | var formSubmit = function (config) { 13 | var postData = { 14 | form_key: FORM_KEY 15 | }; 16 | 17 | /** global var configForm **/ 18 | configForm.find('[id^=system_gmailsmtpapp]').find(':input').serializeArray().map(function (field) { 19 | var name = field.name.match(/groups\[gmailsmtpapp\]?(\[groups\]\[debug\])?\[fields\]\[(.*)\]\[value]/); 20 | 21 | /** 22 | * groups[gmailsmtpapp][groups][debug][fields][email][value] 23 | * groups[gmailsmtpapp][fields][password][value] 24 | */ 25 | 26 | if (name && name.length === 3) { 27 | postData[name[2]] = field.value; 28 | } 29 | }); 30 | 31 | $.ajax({ 32 | url: config.postUrl, 33 | type: 'post', 34 | dataType: 'html', 35 | data: postData, 36 | showLoader: true 37 | }).done(function (response) { 38 | if (typeof response === 'object') { 39 | if (response.error) { 40 | alert({ title: 'Error', content: response.message }); 41 | } else if (response.ajaxExpired) { 42 | window.location.href = response.ajaxRedirect; 43 | } 44 | } else { 45 | alert({ 46 | title:'', 47 | content:response, 48 | buttons: [] 49 | }); 50 | } 51 | }); 52 | }; 53 | 54 | return function (config, element) { 55 | $(element).on('click', function () { 56 | formSubmit(config); 57 | }); 58 | } 59 | }); 60 | --------------------------------------------------------------------------------