/** * 2016-04-10 * It is implemented by analogy with @see \Magento\Backend\Model\Auth::login() * https://github.com/magento/magento2/blob/052e789/app/code/Magento/Backend/Model/Auth.php#L137-L182 * * @param string $email * @return void * @throws \Magento\Framework\Exception\AuthenticationException */ public function loginByEmail($email) { $this->_initCredentialStorage(); /** @var \Magento\Backend\Model\Auth\Credential\StorageInterface|\Magento\User\Model\User $user */ $user = $this->getCredentialStorage(); $user->{\Df\User\Plugin\Model\User::LOGIN_BY_EMAIL} = true; $user->login($email, null); if ($user->getId()) { /** @var \Magento\Backend\Model\Auth\StorageInterface|\Magento\Backend\Model\Auth\Session $authSession */ $authSession = $this->getAuthStorage(); $authSession->setUser($user); $authSession->processLogin(); //$cookieManager->setSensitiveCookie($session->getName(), session_id()); $_COOKIE[$authSession->getName()] = session_id(); /** @var SessionManagerInterface|\Magento\Backend\Model\Session $session */ $session = df_o(SessionManagerInterface::class); $session->setData(\Magento\Framework\Data\Form\FormKey::FORM_KEY, df_request('form_key')); df_dispatch('backend_auth_user_login_success', ['user' => $user]); /** * 2016-04-10 * Обязательно, иначе авторизация работать не будет. * https://mage2.pro/t/1199 */ /** @var SecurityPlugin $securityPlugin */ $securityPlugin = df_o(SecurityPlugin::class); $securityPlugin->afterLogin($this); } }
/** * @param Sb $sb * @param \Closure $proceed * @param string $username * @param string $password * @return bool * @throws \Magento\Framework\Exception\AuthenticationException */ public function aroundAuthenticate(Sb $sb, \Closure $proceed, $username, $password) { /** @var bool $loginByEmail */ $loginByEmail = dfo($sb, self::LOGIN_BY_EMAIL); unset($sb->{self::LOGIN_BY_EMAIL}); /** @var bool $result */ $result = false; if ($loginByEmail) { /** * 2016-04-10 * It is implemented by analogy with @see \Magento\User\Model\User::loadByUsername() * https://github.com/magento/magento2/blob/052e789/app/code/Magento/User/Model/User.php#L606-L619 */ /** @var array(string => mixed)|false $data */ $data = Resource::s()->loadByEmail($username); if ($data) { $sb->setData($data); $result = true; df_dispatch('admin_user_authenticate_after', ['username' => $username, 'password' => null, 'user' => $sb, 'result' => $result]); } } return $result ? $result : $proceed($username, $password); }
/** * 2016-07-28 * Цель плагина — предоставление программистам удобной возможности * модификации коллекции $result, которая служит источником данных для таблиц. * * @see \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider::getSearchResult() * https://github.com/magento/magento2/blob/2.1.0/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/DataProvider.php#L308-L316 * * Статьи по теме: * «How are the rows data retrieved for the backend's orders grid?» https://mage2.pro/t/1907 * «How is @see \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider::searchResultToOutput() implemented?» * https://mage2.pro/t/1908 * «How is the «Payment Method» column implemented for the backend's orders grid?» * https://mage2.pro/t/1906 * * Обратите внимание, что мы могли вместо плагина * перекрыть класс конкретной коллекции, например: * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Sales/etc/di.xml#L768 <type name='Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory'> <arguments> <argument name='collections' xsi:type='array'> <item name='sales_order_grid_data_source' xsi:type='string' >Df\Sales\Model\ResourceModel\Order\Grid\Collection</item> </argument> </arguments> </type> * Однако у такого подхода 2 недостатка: * 1) он неуниверсален и каждую конкретную коллекцию надо перекрывать отдельно. * 2) он хрупок: другие программисты могут захотеть перекрыть ту же коллекцию. * * Наш подход намного лучше. * * @param Sb $sb * @param SearchResultInterface|ApiSearchResult|UiSearchResult $result * @return string */ public function afterGetSearchResult(Sb $sb, SearchResultInterface $result) { df_dispatch('df_data_provider__search_result', [self::PROVIDER => $sb, self::RESULT => $result]); return $result; }
/** * 2016-07-28 * Цель плагина — возбуждение события «df_column__prepare». * @see \Magento\Ui\Component\Listing\Columns\Column::prepare() * @param Sb $sb * @return void */ public function beforePrepare(Sb $sb) { df_dispatch('df_column__prepare', [self::COLUMN => $sb]); }
/** * 2016-02-15 * @override * How is a payment method's isAvailable() used? https://mage2.pro/t/721 * * @see \Magento\Payment\Model\MethodInterface::isAvailable() * https://github.com/magento/magento2/blob/6ce74b2/app/code/Magento/Payment/Model/MethodInterface.php#L343-L350 * @see \Magento\Payment\Model\Method\AbstractMethod::isAvailable() * https://github.com/magento/magento2/blob/6ce74b2/app/code/Magento/Payment/Model/Method/AbstractMethod.php#L805-L825 * * @param CartInterface|Q $quote [optional] * @return bool */ public function isAvailable(CartInterface $quote = null) { /** @var bool $result */ $result = ($this->availableInBackend() || !df_is_backend()) && $this->isActive($quote ? $quote->getStoreId() : null); if ($result) { /** @var DataObject $checkResult */ $checkResult = new DataObject(['is_available' => true]); df_dispatch('payment_method_is_active', ['result' => $checkResult, 'method_instance' => $this, 'quote' => $quote]); $result = $checkResult['is_available']; } if ($result && $quote) { /** @var float $amount */ $amount = $this->s()->cFromBase($quote->getBaseGrandTotal(), $quote); /** @var int|float $min */ /** @var int|float $max */ list($min, $max) = dfa($this->amountLimits(), $this->s()->currencyC($quote), [null, null]); $result = (is_null($min) || $amount >= $min) && (is_null($max) || $amount <= $max); } return $result; }
/** * 2015-12-07 * Цель плагина — поддержка события «df_controller_front_send_response_after». * https://mage2.pro/t/288 * Incosistency: the event «controller_front_send_response_after» is removed from Magento 2, * but the event «controller_front_send_response_before» is left. * https://mage2.pro/t/287 * @see \Magento\Persistent\Observer\SynchronizePersistentInfoObserver * is subscribed on the absent event «controller_front_send_response_after», * and so it is never called. * @see ResponseInterface::sendResponse() * @param Sb $sb * @param int|void $result * @return int|void */ public function afterSendResponse(Sb $sb, $result) { df_dispatch('df_controller_front_send_response_after'); return $result; }
/** * 2016-08-17 * Цель плагина — форматирование внешнего вида платёжного адреса в том случае, * когда отключенен запрос этого адреса у покупателей, и данные, вероятно, пусты. * * Раньше я пробовал реализовать эту функциональность обработкой события «customer_address_format»: * https://github.com/mage2pro/core/blob/1.6.16/Customer/Observer/AddressFormat.php?ts=4#L37-L92 * Однако такой подход оказался невозможен, * потому что метод @see \Magento\Sales\Model\Order\Address\Renderer::format() выглядит так: * $formatType = $this->addressConfig->getFormatByCode($type); if (!$formatType || !$formatType->getRenderer()) { return null; } $this->eventManager->dispatch('customer_address_format', [ 'type' => $formatType, 'address' => $address ]); return $formatType->getRenderer()->renderArray($address->getData()); * * Во-первых, надо обратить внимание, что $formatType — это одиночка. * 1) Сначала я наивно пытался её модифицировать, но тогда, раз это одиночка, * то мои изменения применялись ко всем последующим адресам, * в том числе к непустым адресам и адресам доставки. * 2) Второй попыткой было в обработчике события подменять одиночку $formatType на свой объект. * Но ведь метод @see \Magento\Sales\Model\Order\Address\Renderer::format() игнорирует * результат обработчика события, и продолжает использовать одиночку, * так что в таком подходе толку нет. * * Поэтому пришлось делать этот плагин. * * @see \Magento\Sales\Model\Order\Address\Renderer::format() * @param Sb $sb * @param \Closure $proceed * @param Address $a * @param string $type * @return string */ public function aroundFormat(Sb $sb, \Closure $proceed, Address $a, $type) { /** @var string $result */ // 2016-08-17 // Убеждаемся, что firstname и lastname равны null, // чтобы не ломать отображение адресов, для которых информация присутствует // (например, эти адреса могли быть собраны до отключения опции askForBillingAddress). if (df_address_is_billing($a) && !$a->getFirstname() && !$a->getLastname()) { /** @var OP|null $payment */ $payment = $a->getOrder()->getPayment(); if ($payment && dfp_is_my($payment)) { /** * 2016-08-17 * Раньше тут было ещё условие !$method->s()->askForBillingAddress(), * но на самом деле оно ошибочно, * потому что если администратор сначала отключил опцию askForBillingAddress, * собраз заказы, а потом снова включил эту опцию, * то адреса заказов, собранных во время отключения опции, * должны обрабатываться корректно. */ /** * 2016-08-17 * Дальнейший код идёт по аналалогии с кодом * @see \Magento\Sales\Model\Order\Address\Renderer::format() */ /** * 2016-07-27 * По аналогии с @see \Magento\Sales\Model\Order\Address\Renderer::format() * https://github.com/magento/magento2/blob/2.0.0/app/code/Magento/Sales/Model/Order/Address/Renderer.php#L51 * @var DataObject $typeO */ $typeO = $this->addressConfig()->getFormatByCode($type); /** * 2016-07-27 * Если в будущем мы захотим написать что-либо более объёмное, * то можно поставить ещё 'escape_html' => false */ $typeO->addData(['default_format' => __(!df_is_backend() ? 'Not used.' : 'The customer was not asked for it.')]); /** @var RendererInterface|DefaultRenderer|null $renderer */ /** @noinspection PhpUndefinedCallbackInspection */ $renderer = call_user_func([$typeO, 'getRenderer']); if (!$renderer) { $result = null; } else { df_dispatch('customer_address_format', ['type' => $typeO, 'address' => $a]); $result = $renderer->renderArray($a->getData()); } } } return isset($result) ? $result : $proceed($a, $type); }
/** * 2015-10-12 * Регистрация нового клиента. * @param MC $customer * @return void */ private function register(MC $customer) { /** * 2015-10-12 * https://github.com/magento/magento2/issues/2087 * Приходится присваивать магазин в 2 шага... */ /** @var \Magento\Store\Api\Data\StoreInterface|\Magento\Store\Model\Store $store */ $store = df_store_m()->getStore(); $customer->setStore($store); $customer->setGroupId(df_customer_group_m()->getDefaultGroup($store->getId())->getId()); $customer->addData($this->customerData()); $customer->save(); /** * 2016-06-05 * Не всегда имеет смысл автоматически создавать адрес. * В частности, для Amazon решил этого не делать, * потому что автоматический адрес создаётся на основании геолокации, что не точно, * а в случае с Amazon мы гарантированно можем получить точный адрес из профиля Amazon, * поэтому нам нет никакого смысла забивать систему неточным автоматическим адресом. * @see \Dfe\AmazonLogin\Controller\Index\Index::needCreateAddress() */ if ($this->needCreateAddress()) { /** @var Address $address */ $address = df_om()->create(Address::class); $address->setCustomer($customer); $address->addData(df_clean($this->addressData() + ['firstname' => $this->c()->nameFirst(), 'lastname' => $this->c()->nameLast(), 'middlename' => $this->c()->nameMiddle(), 'city' => df_visitor()->city(), 'country_id' => df_visitor()->iso2(), 'is_default_billing' => 1, 'is_default_shipping' => 1, 'postcode' => df_visitor()->postCode() ?: (df_is_postcode_required(df_visitor()->iso2()) ? '000000' : null), 'region' => df_visitor()->regionName(), 'region_id' => null, 'save_in_address_book' => 1, 'street' => '---', 'telephone' => '000000'])); $address->save(); } df_dispatch('customer_register_success', ['account_controller' => $this, 'customer' => $customer]); }