Office - авторизация и регистрация через sms c одной формы

Кейс:

Клиент очень хотел, чтобы у него на странице входа, как у служб доставки, было одно поле — номер телефона. И всё. То есть, если ты новый — ты регистрируешься, если уже юзер — входишь.

Если с авторизацией через email такое можно заставить работать из коробки, то с телефоном пришлось помудрить. Далее — пошаговое описание решения.

1. Контроллер
В папке core/components/office/controllers/ создаём свой класс-роутер authext.class.php. Он расширяет дефолтный Auth, и его задача определить, новый ты юзер или нет, и исходя из этого отправить тебя либо на авторизацию, либо на регистрацию. При чем, он должен учитывать, что если ты новый юзер, но уже передаешь код активации из sms — все равно должен быть отправлен на регистрацию, хотя объект пользователя уже создан. Это я проверяю через параметр active:
<?php
if (!class_exists('officeAuthController')) {
    include 'auth.class.php';
}

/**
 * Class officeAuthExtController
 */
class officeAuthExtController extends officeAuthController
{
    public function formLoginOrRegister (array $data) 
    {
        if (
            !$profile = $this->checkLogin($data) or !($profile instanceof modUserProfile) // нет такого профиля
            or (!$profile->User->active and !empty($data['phone_code'])) // профиль неактивен, а в запросе есть код из sms
        ) {
            $data['username'] = $this->office->checkPhone(@$data['mobilephone']);
            return $this->formRegister($data);
        } else {
            return $this->formLogin($data);
        }
    }
}
return 'officeAuthExtController';

2. Вызов сниппета и чанк формы
В вызове сниппета указываем свой контроллер, свой чанк входа, редирект на лк вместо чанка выхода, свой чанк сообщения о регистрации:
{'!office'|snippet:[
    'action' => 'authext',
    'groups' => 'Customer',
    'HybridAuth' => false,
    'tplLogin' => 'login',
    'tplLogout' => '@INLINE {$_modx->sendRedirect("office_profile_page_id"|option|url)}',
    'tplRegister' => 'register_sms'
]}

В чанке формы оставляем два поля: mobilephone и скрытый phone_code, а также указываем свой контроллер и его метод в поле action:
<div id="office-auth-form">
    <form action="{$_modx->resource.id|url}" method="post">
        <div class="form-group">
            <label style="display: block;">
                <input type="text" name="mobilephone" class="form-control" placeholder="Ваш телефон">
            </label>
        </div>
        <div class="form-group hidden">
            <label style="display: block;">
                <input type="text" name="phone_code" class="form-control" placeholder="Введите код" readonly>
            </label>
        </div>
        <div class="form-group">
            <input type="hidden" name="action" value="authExt/formLoginOrRegister">
            <input type="hidden" name="return" value="">
            <button type="submit" class="btn btn-primary">Выслать код</button>
        </div>
    </form>
</div>

В чанке sms о регистрации убираем информацию о пароле:
{var $lang = $_modx->config.cultureKey}
{if $code?}
    {if $lang == 'en'}
        You registered on {'site_name'|option}. Your activation code: {$code}.
    {else}
        Вы зарегистрировались на {'site_name'|option}. Ваш код активации: {$code}.
    {/if}
{/if}

3. Правка в JS
Теперь делаем так, чтобы при в воде номера на кнопке была надпись «Выслать код», а при вводе кода — «Войти». В настройку office_auth_frontend_js прописываем свой файл: [[++assets_url]]components/office/js/auth/custom.js. В него копируем код из default.js и слегка его модифицируем, строка (на данный момент) 54:
if (response.data['sms'] != undefined) {
    if (response.data['sms'] == 1) {
        $form.find('input').attr('readonly', true);
        $form.find('[name="phone_code"]').attr('readonly', false)
            .parents('.hidden').removeClass('hidden').addClass('not-hidden');
        $form.find('[type="submit"]').text('Войти');
    } else {
        $form.find('input').attr('readonly', false);
        $form.find('[name="phone_code"]').attr('readonly', true)
            .parents('.not-hidden').removeClass('not-hidden').addClass('hidden');
        $form.find('[type="submit"]').text('Выслать код');
    }
}

Профит.
Теперь пользователь в любом случае попадает на сайт, имея только номер телефона и одну sms, независимо от того, регистрировался он до этого или нет. Отличие только в тексте получаемой смски.

В моем случае еще требовалось, чтобы при регистрации в лк не были заполнены поля email и fullname (office их по умолчанию забивает фейковыми данными), пока пользователь сам туда что-то не впишет. Это решается редактированием настройки office_profile_required_fields (убираем оттуда всё, оставляем mobilephone) и плагина officeProfile (лучше его скопировать и работать с копией, а оригинал отключить) — в этом плагине комментируем примерно с 16 строки:
// if (!in_array('email', $required)) {
//     $required[] = 'email';
// }
Помимо этого, создаем свой плагин на событие OnUserBeforeSave, обманывающий родной процессор modx, который не разрешает юзеру не иметь email:
<?php
switch ($modx->event->name) {
    case 'OnUserBeforeSave':
        if ($mode == 'new') {
            $user->Profile->set('email', '');
            $user->Profile->set('fullname', '');
        }
        break;
}

Вуаля.
mngatoff
26 июля 2018, 16:18
modx.pro
34
3 902
+18

Комментарии: 9

Konstantin
26 июля 2018, 22:10
0
Я как-то по проще сделал, в коде не менял ничего. Вход и регистрация по коду из смс. Один минус – две вкладки, зато при обновлении офиса ничего не слетит.
    Здоров Александр
    26 июля 2018, 22:14
    0
    примеры кода в студию
      mngatoff
      26 июля 2018, 23:37
      +3
      1. статья имеет в названии условие «С ОДНОЙ ФОРМЫ», а не с двух вкладок. разница принципиальна. то что ты сделал и делать-то особо не надо — лишние поля вырезать из чанков только.
      2. а что, пардон, у меня слетит при обновлении?)
        Konstantin
        27 июля 2018, 07:10
        +2
        1.
        лишние поля вырезать из чанков только.
        Именно так я и сделал. И да, у меня два поля, и это минус.
        2.
        а что, пардон, у меня слетит при обновлении?)
        Если ничего, то я с удовольствием использую твой вариант, потому, что он очевидно круче.

        P.s. Если тебе написали комментарий не содержащий похвалы и оваций, не стоит сразу камнями кидаться, не обязательно тебя хотели как-то задеть.
          mngatoff
          27 июля 2018, 10:11
          +3
          это не камни, это глухая оборона, ибо обесценивается проделанный труд. извини за резкость, тоже не хотет ругаться.
          ничего не накроется. только плагин officeProfile надо будет заново деактивировать
            Konstantin
            27 июля 2018, 12:11
            0
            Что-то у меня не выходит. С формой в модальном окне не будет работать?
              mngatoff
              27 июля 2018, 12:36
              +1
              будет, но форма должна быть внутри div#office-auth-form
        Sergey (Sentinel)
        27 сентября 2020, 10:37
        0
        у office вместо логина или email можно войти и с телефоном?
        Просто мне пишет пользователь не найден, хотя телефон в mobilephone есть…
        Денис Мижеревич
        14 мая 2021, 11:21
        0
        Очень крутой кейс. Спасибо.
        Именно такое решение и искал.
        Бескрайне благодарен вам — для начинающих разработчиков, вроде меня, подобные примеры и инструкции очень ценны.
        Поставил на сайте, все отлично работает.
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          9