SendFriend.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\SendFriend\Model;
  7. use Magento\Framework\Exception\LocalizedException as CoreException;
  8. /**
  9. * SendFriend Log
  10. *
  11. * @method int getIp()
  12. * @method \Magento\SendFriend\Model\SendFriend setIp(int $value)
  13. * @method int getTime()
  14. * @method \Magento\SendFriend\Model\SendFriend setTime(int $value)
  15. *
  16. * @author Magento Core Team <core@magentocommerce.com>
  17. * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
  18. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  19. *
  20. * @api
  21. * @since 100.0.2
  22. */
  23. class SendFriend extends \Magento\Framework\Model\AbstractModel
  24. {
  25. /**
  26. * Recipient Names
  27. *
  28. * @var array
  29. */
  30. protected $_names = [];
  31. /**
  32. * Recipient Emails
  33. *
  34. * @var array
  35. */
  36. protected $_emails = [];
  37. /**
  38. * Sender data array
  39. *
  40. * @var \Magento\Framework\DataObject|array
  41. */
  42. protected $_sender = [];
  43. /**
  44. * Product Instance
  45. *
  46. * @var \Magento\Catalog\Model\Product
  47. */
  48. protected $_product;
  49. /**
  50. * Count of sent in last period
  51. *
  52. * @var int
  53. */
  54. protected $_sentCount;
  55. /**
  56. * Last values for Cookie
  57. *
  58. * @var string
  59. */
  60. protected $_lastCookieValue = [];
  61. /**
  62. * SendFriend data
  63. *
  64. * @var \Magento\SendFriend\Helper\Data
  65. */
  66. protected $_sendfriendData = null;
  67. /**
  68. * Catalog image
  69. *
  70. * @var \Magento\Catalog\Helper\Image
  71. */
  72. protected $_catalogImage = null;
  73. /**
  74. * @var \Magento\Framework\Mail\Template\TransportBuilder
  75. */
  76. protected $_transportBuilder;
  77. /**
  78. * @var \Magento\Store\Model\StoreManagerInterface
  79. */
  80. protected $_storeManager;
  81. /**
  82. * @var \Magento\Framework\Escaper
  83. */
  84. protected $_escaper;
  85. /**
  86. * @var \Magento\Framework\Translate\Inline\StateInterface
  87. */
  88. protected $inlineTranslation;
  89. /**
  90. * @var \Magento\Framework\Stdlib\CookieManagerInterface
  91. */
  92. protected $cookieManager;
  93. /**
  94. * @var \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress
  95. */
  96. protected $remoteAddress;
  97. /**
  98. * @param \Magento\Framework\Model\Context $context
  99. * @param \Magento\Framework\Registry $registry
  100. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  101. * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
  102. * @param \Magento\Catalog\Helper\Image $catalogImage
  103. * @param \Magento\SendFriend\Helper\Data $sendfriendData
  104. * @param \Magento\Framework\Escaper $escaper
  105. * @param \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress
  106. * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
  107. * @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
  108. * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
  109. * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
  110. * @param array $data
  111. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  112. */
  113. public function __construct(
  114. \Magento\Framework\Model\Context $context,
  115. \Magento\Framework\Registry $registry,
  116. \Magento\Store\Model\StoreManagerInterface $storeManager,
  117. \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
  118. \Magento\Catalog\Helper\Image $catalogImage,
  119. \Magento\SendFriend\Helper\Data $sendfriendData,
  120. \Magento\Framework\Escaper $escaper,
  121. \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress,
  122. \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
  123. \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
  124. \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
  125. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  126. array $data = []
  127. ) {
  128. $this->_storeManager = $storeManager;
  129. $this->_transportBuilder = $transportBuilder;
  130. $this->_catalogImage = $catalogImage;
  131. $this->_sendfriendData = $sendfriendData;
  132. $this->_escaper = $escaper;
  133. $this->remoteAddress = $remoteAddress;
  134. $this->cookieManager = $cookieManager;
  135. $this->inlineTranslation = $inlineTranslation;
  136. parent::__construct($context, $registry, $resource, $resourceCollection, $data);
  137. }
  138. /**
  139. * Initialize resource model
  140. *
  141. * @return void
  142. */
  143. protected function _construct()
  144. {
  145. $this->_init(\Magento\SendFriend\Model\ResourceModel\SendFriend::class);
  146. }
  147. /**
  148. * Sends email to recipients
  149. *
  150. * @return $this
  151. * @throws CoreException
  152. */
  153. public function send()
  154. {
  155. if ($this->isExceedLimit()) {
  156. throw new \Magento\Framework\Exception\LocalizedException(
  157. __('You\'ve met your limit of %1 sends in an hour.', $this->getMaxSendsToFriend())
  158. );
  159. }
  160. $this->inlineTranslation->suspend();
  161. $message = nl2br(htmlspecialchars($this->getSender()->getMessage()));
  162. $sender = [
  163. 'name' => $this->_escaper->escapeHtml($this->getSender()->getName()),
  164. 'email' => $this->_escaper->escapeHtml($this->getSender()->getEmail()),
  165. ];
  166. foreach ($this->getRecipients()->getEmails() as $k => $email) {
  167. $name = $this->getRecipients()->getNames($k);
  168. $this->_transportBuilder->setTemplateIdentifier(
  169. $this->_sendfriendData->getEmailTemplate()
  170. )->setTemplateOptions(
  171. [
  172. 'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
  173. 'store' => $this->_storeManager->getStore()->getId(),
  174. ]
  175. )->setFrom(
  176. $sender
  177. )->setTemplateVars(
  178. [
  179. 'name' => $name,
  180. 'email' => $email,
  181. 'product_name' => $this->getProduct()->getName(),
  182. 'product_url' => $this->getProduct()->getUrlInStore(),
  183. 'message' => $message,
  184. 'sender_name' => $sender['name'],
  185. 'sender_email' => $sender['email'],
  186. 'product_image' => $this->_catalogImage->init($this->getProduct(), 'sendfriend_small_image')
  187. ->getUrl(),
  188. ]
  189. )->addTo(
  190. $email,
  191. $name
  192. );
  193. $transport = $this->_transportBuilder->getTransport();
  194. $transport->sendMessage();
  195. }
  196. $this->inlineTranslation->resume();
  197. $this->_incrementSentCount();
  198. return $this;
  199. }
  200. /**
  201. * Validate Form data
  202. *
  203. * @return bool|string[]
  204. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  205. * @SuppressWarnings(PHPMD.NPathComplexity)
  206. */
  207. public function validate()
  208. {
  209. $errors = [];
  210. $name = $this->getSender()->getName();
  211. if (empty($name)) {
  212. $errors[] = __('Please enter a sender name.');
  213. }
  214. $email = $this->getSender()->getEmail();
  215. if (empty($email) || !\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) {
  216. $errors[] = __('Invalid Sender Email');
  217. }
  218. $message = $this->getSender()->getMessage();
  219. if (empty($message)) {
  220. $errors[] = __('Please enter a message.');
  221. }
  222. if (!$this->getRecipients()->getEmails()) {
  223. $errors[] = __('Please specify at least one recipient.');
  224. }
  225. // validate recipients email addresses
  226. foreach ($this->getRecipients()->getEmails() as $email) {
  227. if (!\Zend_Validate::is($email, \Magento\Framework\Validator\EmailAddress::class)) {
  228. $errors[] = __('Please enter a correct recipient email address.');
  229. break;
  230. }
  231. }
  232. $maxRecipients = $this->getMaxRecipients();
  233. if (count($this->getRecipients()->getEmails()) > $maxRecipients) {
  234. $errors[] = __('No more than %1 emails can be sent at a time.', $this->getMaxRecipients());
  235. }
  236. if (empty($errors)) {
  237. return true;
  238. }
  239. return $errors;
  240. }
  241. /**
  242. * Set Recipients
  243. *
  244. * @param array $recipients
  245. * @return $this
  246. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  247. */
  248. public function setRecipients($recipients)
  249. {
  250. // validate array
  251. if (!is_array(
  252. $recipients
  253. ) || !isset(
  254. $recipients['email']
  255. ) || !isset(
  256. $recipients['name']
  257. ) || !is_array(
  258. $recipients['email']
  259. ) || !is_array(
  260. $recipients['name']
  261. )
  262. ) {
  263. return $this;
  264. }
  265. $emails = [];
  266. $names = [];
  267. foreach ($recipients['email'] as $k => $email) {
  268. if (!isset($emails[$email]) && isset($recipients['name'][$k])) {
  269. $emails[$email] = true;
  270. $names[] = $recipients['name'][$k];
  271. }
  272. }
  273. if ($emails) {
  274. $emails = array_keys($emails);
  275. }
  276. return $this->setData(
  277. '_recipients',
  278. new \Magento\Framework\DataObject(['emails' => $emails, 'names' => $names])
  279. );
  280. }
  281. /**
  282. * Retrieve Recipients object
  283. *
  284. * @return \Magento\Framework\DataObject
  285. */
  286. public function getRecipients()
  287. {
  288. $recipients = $this->_getData('_recipients');
  289. if (!$recipients instanceof \Magento\Framework\DataObject) {
  290. $recipients = new \Magento\Framework\DataObject(['emails' => [], 'names' => []]);
  291. $this->setData('_recipients', $recipients);
  292. }
  293. return $recipients;
  294. }
  295. /**
  296. * Set product instance
  297. *
  298. * @param \Magento\Catalog\Model\Product $product
  299. * @return $this
  300. */
  301. public function setProduct($product)
  302. {
  303. return $this->setData('_product', $product);
  304. }
  305. /**
  306. * Retrieve Product instance
  307. *
  308. * @throws \Magento\Framework\Exception\LocalizedException
  309. * @return \Magento\Catalog\Model\Product
  310. */
  311. public function getProduct()
  312. {
  313. $product = $this->_getData('_product');
  314. if (!$product instanceof \Magento\Catalog\Model\Product) {
  315. throw new \Magento\Framework\Exception\LocalizedException(__('Please define a correct product instance.'));
  316. }
  317. return $product;
  318. }
  319. /**
  320. * Set Sender Information array
  321. *
  322. * @param array $sender
  323. * @return $this
  324. */
  325. public function setSender($sender)
  326. {
  327. if (!is_array($sender)) {
  328. __('Invalid Sender Information');
  329. }
  330. return $this->setData('_sender', new \Magento\Framework\DataObject($sender));
  331. }
  332. /**
  333. * Retrieve Sender Information Object
  334. *
  335. * @throws \Magento\Framework\Exception\LocalizedException
  336. * @return \Magento\Framework\DataObject
  337. */
  338. public function getSender()
  339. {
  340. $sender = $this->_getData('_sender');
  341. if (!$sender instanceof \Magento\Framework\DataObject) {
  342. throw new \Magento\Framework\Exception\LocalizedException(
  343. __('Please define the correct sender information.')
  344. );
  345. }
  346. return $sender;
  347. }
  348. /**
  349. * Get max allowed uses of "Send to Friend" function per hour
  350. *
  351. * @return integer
  352. */
  353. public function getMaxSendsToFriend()
  354. {
  355. return $this->_sendfriendData->getMaxEmailPerPeriod();
  356. }
  357. /**
  358. * Get max allowed recipients for "Send to a Friend" function
  359. *
  360. * @return integer
  361. */
  362. public function getMaxRecipients()
  363. {
  364. return $this->_sendfriendData->getMaxRecipients();
  365. }
  366. /**
  367. * Check if user is allowed to email product to a friend
  368. *
  369. * @return boolean
  370. */
  371. public function canEmailToFriend()
  372. {
  373. return $this->_sendfriendData->isEnabled();
  374. }
  375. /**
  376. * Check if user is exceed limit
  377. *
  378. * @return boolean
  379. */
  380. public function isExceedLimit()
  381. {
  382. return $this->getSentCount() >= $this->getMaxSendsToFriend();
  383. }
  384. /**
  385. * Return count of sent in last period
  386. *
  387. * @param bool $useCache - flag, is allow to use value of attribute of model if it is processed last time
  388. * @return int
  389. */
  390. public function getSentCount($useCache = true)
  391. {
  392. if ($useCache && $this->_sentCount !== null) {
  393. return $this->_sentCount;
  394. }
  395. switch ($this->_sendfriendData->getLimitBy()) {
  396. case \Magento\SendFriend\Helper\Data::CHECK_COOKIE:
  397. return $this->_sentCount = $this->_sentCountByCookies(false);
  398. case \Magento\SendFriend\Helper\Data::CHECK_IP:
  399. return $this->_sentCount = $this->_sentCountByIp(false);
  400. default:
  401. return 0;
  402. }
  403. }
  404. /**
  405. * Increase count of sent
  406. *
  407. * @return int
  408. */
  409. protected function _incrementSentCount()
  410. {
  411. switch ($this->_sendfriendData->getLimitBy()) {
  412. case \Magento\SendFriend\Helper\Data::CHECK_COOKIE:
  413. return $this->_sentCount = $this->_sentCountByCookies(true);
  414. case \Magento\SendFriend\Helper\Data::CHECK_IP:
  415. return $this->_sentCount = $this->_sentCountByIp(true);
  416. default:
  417. return 0;
  418. }
  419. }
  420. /**
  421. * Return count of sent in last period by cookie
  422. *
  423. * @param bool $increment - flag, increase count before return value
  424. * @return int
  425. */
  426. protected function _sentCountByCookies($increment = false)
  427. {
  428. $cookieName = $this->_sendfriendData->getCookieName();
  429. $time = time();
  430. $newTimes = [];
  431. if (isset($this->_lastCookieValue[$cookieName])) {
  432. $oldTimes = $this->_lastCookieValue[$cookieName];
  433. } else {
  434. $oldTimes = $this->cookieManager->getCookie($cookieName);
  435. }
  436. if ($oldTimes) {
  437. $oldTimes = explode(',', $oldTimes);
  438. foreach ($oldTimes as $oldTime) {
  439. $periodTime = $time - $this->_sendfriendData->getPeriod();
  440. if (is_numeric($oldTime) && $oldTime >= $periodTime) {
  441. $newTimes[] = $oldTime;
  442. }
  443. }
  444. }
  445. if ($increment) {
  446. $newTimes[] = $time;
  447. $newValue = implode(',', $newTimes);
  448. $this->cookieManager->setSensitiveCookie($cookieName, $newValue);
  449. $this->_lastCookieValue[$cookieName] = $newValue;
  450. }
  451. return count($newTimes);
  452. }
  453. /**
  454. * Return count of sent in last period by IP address
  455. *
  456. * @param bool $increment - flag, increase count before return value
  457. * @return int
  458. */
  459. protected function _sentCountByIp($increment = false)
  460. {
  461. $time = time();
  462. $period = $this->_sendfriendData->getPeriod();
  463. $websiteId = $this->_storeManager->getStore()->getWebsiteId();
  464. if ($increment) {
  465. // delete expired logs
  466. $this->_getResource()->deleteLogsBefore($time - $period);
  467. // add new item
  468. $this->_getResource()->addSendItem($this->remoteAddress->getRemoteAddress(true), $time, $websiteId);
  469. }
  470. return $this->_getResource()->getSendCount(
  471. $this,
  472. $this->remoteAddress->getRemoteAddress(true),
  473. time() - $period,
  474. $websiteId
  475. );
  476. }
  477. }