EditPost.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <?php
  2. /**
  3. *
  4. * Copyright © Magento, Inc. All rights reserved.
  5. * See COPYING.txt for license details.
  6. */
  7. namespace Magento\Customer\Controller\Account;
  8. use Magento\Customer\Api\Data\CustomerInterface;
  9. use Magento\Customer\Model\AddressRegistry;
  10. use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
  11. use Magento\Customer\Model\AuthenticationInterface;
  12. use Magento\Customer\Model\Customer\Mapper;
  13. use Magento\Customer\Model\EmailNotificationInterface;
  14. use Magento\Framework\App\CsrfAwareActionInterface;
  15. use Magento\Framework\App\ObjectManager;
  16. use Magento\Framework\App\Request\InvalidRequestException;
  17. use Magento\Framework\App\RequestInterface;
  18. use Magento\Framework\Controller\Result\Redirect;
  19. use Magento\Framework\Data\Form\FormKey\Validator;
  20. use Magento\Customer\Api\AccountManagementInterface;
  21. use Magento\Customer\Api\CustomerRepositoryInterface;
  22. use Magento\Customer\Model\CustomerExtractor;
  23. use Magento\Customer\Model\Session;
  24. use Magento\Framework\App\Action\Context;
  25. use Magento\Framework\Escaper;
  26. use Magento\Framework\Exception\InputException;
  27. use Magento\Framework\Exception\InvalidEmailOrPasswordException;
  28. use Magento\Framework\Exception\NoSuchEntityException;
  29. use Magento\Framework\Exception\State\UserLockedException;
  30. use Magento\Customer\Controller\AbstractAccount;
  31. use Magento\Framework\Phrase;
  32. /**
  33. * Class EditPost
  34. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  35. */
  36. class EditPost extends AbstractAccount implements CsrfAwareActionInterface, HttpPostActionInterface
  37. {
  38. /**
  39. * Form code for data extractor
  40. */
  41. const FORM_DATA_EXTRACTOR_CODE = 'customer_account_edit';
  42. /**
  43. * @var AccountManagementInterface
  44. */
  45. protected $customerAccountManagement;
  46. /**
  47. * @var CustomerRepositoryInterface
  48. */
  49. protected $customerRepository;
  50. /**
  51. * @var Validator
  52. */
  53. protected $formKeyValidator;
  54. /**
  55. * @var CustomerExtractor
  56. */
  57. protected $customerExtractor;
  58. /**
  59. * @var Session
  60. */
  61. protected $session;
  62. /**
  63. * @var \Magento\Customer\Model\EmailNotificationInterface
  64. */
  65. private $emailNotification;
  66. /**
  67. * @var AuthenticationInterface
  68. */
  69. private $authentication;
  70. /**
  71. * @var Mapper
  72. */
  73. private $customerMapper;
  74. /**
  75. * @var Escaper
  76. */
  77. private $escaper;
  78. /**
  79. * @var AddressRegistry
  80. */
  81. private $addressRegistry;
  82. /**
  83. * @param Context $context
  84. * @param Session $customerSession
  85. * @param AccountManagementInterface $customerAccountManagement
  86. * @param CustomerRepositoryInterface $customerRepository
  87. * @param Validator $formKeyValidator
  88. * @param CustomerExtractor $customerExtractor
  89. * @param Escaper|null $escaper
  90. * @param AddressRegistry|null $addressRegistry
  91. */
  92. public function __construct(
  93. Context $context,
  94. Session $customerSession,
  95. AccountManagementInterface $customerAccountManagement,
  96. CustomerRepositoryInterface $customerRepository,
  97. Validator $formKeyValidator,
  98. CustomerExtractor $customerExtractor,
  99. ?Escaper $escaper = null,
  100. AddressRegistry $addressRegistry = null
  101. ) {
  102. parent::__construct($context);
  103. $this->session = $customerSession;
  104. $this->customerAccountManagement = $customerAccountManagement;
  105. $this->customerRepository = $customerRepository;
  106. $this->formKeyValidator = $formKeyValidator;
  107. $this->customerExtractor = $customerExtractor;
  108. $this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class);
  109. $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
  110. }
  111. /**
  112. * Get authentication
  113. *
  114. * @return AuthenticationInterface
  115. */
  116. private function getAuthentication()
  117. {
  118. if (!($this->authentication instanceof AuthenticationInterface)) {
  119. return ObjectManager::getInstance()->get(
  120. \Magento\Customer\Model\AuthenticationInterface::class
  121. );
  122. } else {
  123. return $this->authentication;
  124. }
  125. }
  126. /**
  127. * Get email notification
  128. *
  129. * @return EmailNotificationInterface
  130. * @deprecated 100.1.0
  131. */
  132. private function getEmailNotification()
  133. {
  134. if (!($this->emailNotification instanceof EmailNotificationInterface)) {
  135. return ObjectManager::getInstance()->get(
  136. EmailNotificationInterface::class
  137. );
  138. } else {
  139. return $this->emailNotification;
  140. }
  141. }
  142. /**
  143. * @inheritDoc
  144. */
  145. public function createCsrfValidationException(
  146. RequestInterface $request
  147. ): ?InvalidRequestException {
  148. /** @var Redirect $resultRedirect */
  149. $resultRedirect = $this->resultRedirectFactory->create();
  150. $resultRedirect->setPath('*/*/edit');
  151. return new InvalidRequestException(
  152. $resultRedirect,
  153. [new Phrase('Invalid Form Key. Please refresh the page.')]
  154. );
  155. }
  156. /**
  157. * @inheritDoc
  158. */
  159. public function validateForCsrf(RequestInterface $request): ?bool
  160. {
  161. return null;
  162. }
  163. /**
  164. * Change customer email or password action
  165. *
  166. * @return \Magento\Framework\Controller\Result\Redirect
  167. */
  168. public function execute()
  169. {
  170. /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */
  171. $resultRedirect = $this->resultRedirectFactory->create();
  172. $validFormKey = $this->formKeyValidator->validate($this->getRequest());
  173. if ($validFormKey && $this->getRequest()->isPost()) {
  174. $currentCustomerDataObject = $this->getCustomerDataObject($this->session->getCustomerId());
  175. $customerCandidateDataObject = $this->populateNewCustomerDataObject(
  176. $this->_request,
  177. $currentCustomerDataObject
  178. );
  179. try {
  180. // whether a customer enabled change email option
  181. $this->processChangeEmailRequest($currentCustomerDataObject);
  182. // whether a customer enabled change password option
  183. $isPasswordChanged = $this->changeCustomerPassword($currentCustomerDataObject->getEmail());
  184. // No need to validate customer address while editing customer profile
  185. $this->disableAddressValidation($customerCandidateDataObject);
  186. $this->customerRepository->save($customerCandidateDataObject);
  187. $this->getEmailNotification()->credentialsChanged(
  188. $customerCandidateDataObject,
  189. $currentCustomerDataObject->getEmail(),
  190. $isPasswordChanged
  191. );
  192. $this->dispatchSuccessEvent($customerCandidateDataObject);
  193. $this->messageManager->addSuccess(__('You saved the account information.'));
  194. return $resultRedirect->setPath('customer/account');
  195. } catch (InvalidEmailOrPasswordException $e) {
  196. $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage()));
  197. } catch (UserLockedException $e) {
  198. $message = __(
  199. 'The account sign-in was incorrect or your account is disabled temporarily. '
  200. . 'Please wait and try again later.'
  201. );
  202. $this->session->logout();
  203. $this->session->start();
  204. $this->messageManager->addError($message);
  205. return $resultRedirect->setPath('customer/account/login');
  206. } catch (InputException $e) {
  207. $this->messageManager->addErrorMessage($this->escaper->escapeHtml($e->getMessage()));
  208. foreach ($e->getErrors() as $error) {
  209. $this->messageManager->addErrorMessage($this->escaper->escapeHtml($error->getMessage()));
  210. }
  211. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  212. $this->messageManager->addError($e->getMessage());
  213. } catch (\Exception $e) {
  214. $this->messageManager->addException($e, __('We can\'t save the customer.'));
  215. }
  216. $this->session->setCustomerFormData($this->getRequest()->getPostValue());
  217. }
  218. /** @var Redirect $resultRedirect */
  219. $resultRedirect = $this->resultRedirectFactory->create();
  220. $resultRedirect->setPath('*/*/edit');
  221. return $resultRedirect;
  222. }
  223. /**
  224. * Account editing action completed successfully event
  225. *
  226. * @param \Magento\Customer\Api\Data\CustomerInterface $customerCandidateDataObject
  227. * @return void
  228. */
  229. private function dispatchSuccessEvent(\Magento\Customer\Api\Data\CustomerInterface $customerCandidateDataObject)
  230. {
  231. $this->_eventManager->dispatch(
  232. 'customer_account_edited',
  233. ['email' => $customerCandidateDataObject->getEmail()]
  234. );
  235. }
  236. /**
  237. * Get customer data object
  238. *
  239. * @param int $customerId
  240. *
  241. * @return \Magento\Customer\Api\Data\CustomerInterface
  242. */
  243. private function getCustomerDataObject($customerId)
  244. {
  245. return $this->customerRepository->getById($customerId);
  246. }
  247. /**
  248. * Create Data Transfer Object of customer candidate
  249. *
  250. * @param \Magento\Framework\App\RequestInterface $inputData
  251. * @param \Magento\Customer\Api\Data\CustomerInterface $currentCustomerData
  252. * @return \Magento\Customer\Api\Data\CustomerInterface
  253. */
  254. private function populateNewCustomerDataObject(
  255. \Magento\Framework\App\RequestInterface $inputData,
  256. \Magento\Customer\Api\Data\CustomerInterface $currentCustomerData
  257. ) {
  258. $attributeValues = $this->getCustomerMapper()->toFlatArray($currentCustomerData);
  259. $customerDto = $this->customerExtractor->extract(
  260. self::FORM_DATA_EXTRACTOR_CODE,
  261. $inputData,
  262. $attributeValues
  263. );
  264. $customerDto->setId($currentCustomerData->getId());
  265. if (!$customerDto->getAddresses()) {
  266. $customerDto->setAddresses($currentCustomerData->getAddresses());
  267. }
  268. if (!$inputData->getParam('change_email')) {
  269. $customerDto->setEmail($currentCustomerData->getEmail());
  270. }
  271. return $customerDto;
  272. }
  273. /**
  274. * Change customer password
  275. *
  276. * @param string $email
  277. * @return boolean
  278. * @throws InvalidEmailOrPasswordException|InputException
  279. */
  280. protected function changeCustomerPassword($email)
  281. {
  282. $isPasswordChanged = false;
  283. if ($this->getRequest()->getParam('change_password')) {
  284. $currPass = $this->getRequest()->getPost('current_password');
  285. $newPass = $this->getRequest()->getPost('password');
  286. $confPass = $this->getRequest()->getPost('password_confirmation');
  287. if ($newPass != $confPass) {
  288. throw new InputException(__('Password confirmation doesn\'t match entered password.'));
  289. }
  290. $isPasswordChanged = $this->customerAccountManagement->changePassword($email, $currPass, $newPass);
  291. }
  292. return $isPasswordChanged;
  293. }
  294. /**
  295. * Process change email request
  296. *
  297. * @param \Magento\Customer\Api\Data\CustomerInterface $currentCustomerDataObject
  298. * @return void
  299. * @throws InvalidEmailOrPasswordException
  300. * @throws UserLockedException
  301. */
  302. private function processChangeEmailRequest(\Magento\Customer\Api\Data\CustomerInterface $currentCustomerDataObject)
  303. {
  304. if ($this->getRequest()->getParam('change_email')) {
  305. // authenticate user for changing email
  306. try {
  307. $this->getAuthentication()->authenticate(
  308. $currentCustomerDataObject->getId(),
  309. $this->getRequest()->getPost('current_password')
  310. );
  311. } catch (InvalidEmailOrPasswordException $e) {
  312. throw new InvalidEmailOrPasswordException(
  313. __("The password doesn't match this account. Verify the password and try again.")
  314. );
  315. }
  316. }
  317. }
  318. /**
  319. * Get Customer Mapper instance
  320. *
  321. * @return Mapper
  322. *
  323. * @deprecated 100.1.3
  324. */
  325. private function getCustomerMapper()
  326. {
  327. if ($this->customerMapper === null) {
  328. $this->customerMapper = ObjectManager::getInstance()->get(\Magento\Customer\Model\Customer\Mapper::class);
  329. }
  330. return $this->customerMapper;
  331. }
  332. /**
  333. * Disable Customer Address Validation
  334. *
  335. * @param CustomerInterface $customer
  336. * @throws NoSuchEntityException
  337. */
  338. private function disableAddressValidation($customer)
  339. {
  340. foreach ($customer->getAddresses() as $address) {
  341. $addressModel = $this->addressRegistry->retrieve($address->getId());
  342. $addressModel->setShouldIgnoreValidation(true);
  343. }
  344. }
  345. }