[СДЕЛАЙ САМ] Login + HybridAuth + АjaxForm
Всем привет, пишу это прежде всего для себя, чтобы не забыть что и как я делал. Прежде всего нам понадобится установить AjaxForm, FormIt, Login и HybridAuth. Тем кто никогда не настраивал регистрацию и авторизацию через Login обязательно прочитать серию статей на эту тему здесь itchief.ru/modx/login-registration. Если дополнения стоят и общий механизм понятен можно продолжать.
Насколько я понял HybridAuth после получения разрешений от пользователя отправляет его на адрес указанный в callback_uri, но проблема в том, что это может быть только Главная страница, поэтому я внёс небольшие изменения в файл core/components/hybridauth/model/hybridauth/hybridauth.class.php, для того чтобы можно бы указывать любую страницу сайта для редиректа.
Примерно на 133 строке есть код
Вот тут $config['callback'] хранится тот самый url, который нам и надо изменить. Для этого надо создать системную настройку, я назвал её ha_redirect_id, которая будет хранить id ресурса для редиректа. Затем мы добавляем в исходный код проверку задана эта настройка или нет. Если нет, то редирект будет на главную, если задана, то редирект будет на ту страницу, id которой хранится тут ha_redirect_id.
Перед тем как начать начать настройку нужно создать группу пользователей в которую мы будем записывать новых пользователей. Как это сделать написано тут itchief.ru/modx/login-registration. Теперь, когда группа пользователей создана, перейдем к настройке регистрации. За основу была взята эта заметка modx.pro/help/19551. Вызов сниппета выглядит так
Страница для подтверждения регистрации содержит стандартный сниппет ConfirmRegister
Сначала вызываем сниппет custAuth через AjaxForm
Ну и напоследок в модалке с формой авторbзации добавляем вызов HybridAuth
Насколько я понял HybridAuth после получения разрешений от пользователя отправляет его на адрес указанный в callback_uri, но проблема в том, что это может быть только Главная страница, поэтому я внёс небольшие изменения в файл core/components/hybridauth/model/hybridauth/hybridauth.class.php, для того чтобы можно бы указывать любую страницу сайта для редиректа.
Примерно на 133 строке есть код
try {
$config['callback'] = $this->modx->getOption('site_url') . '?hauth.done=' . $provider;
$this->adapters[$provider] = new $class($config);
}catch (Exception $e) {
$this->exceptionHandler($e);
}
Вот тут $config['callback'] хранится тот самый url, который нам и надо изменить. Для этого надо создать системную настройку, я назвал её ha_redirect_id, которая будет хранить id ресурса для редиректа. Затем мы добавляем в исходный код проверку задана эта настройка или нет. Если нет, то редирект будет на главную, если задана, то редирект будет на ту страницу, id которой хранится тут ha_redirect_id.
try {
$redirectID = $this->modx->getOption('ha_redirect_id');
if($redirectID){
$resource = $this->modx->getObject('modResource', (int)$redirectID);
$redirectResource = $this->modx->makeUrl($redirectID, $resource->context_key, '', 'full');
$config['callback'] = $redirectResource. '/?hauth.done=' . $provider;
}else{
$config['callback'] = $this->modx->getOption('site_url') . '?hauth.done=' . $provider;
}
$this->adapters[$provider] = new $class($config);
} catch (Exception $e) {
$this->exceptionHandler($e);
}
Перед тем как начать начать настройку нужно создать группу пользователей в которую мы будем записывать новых пользователей. Как это сделать написано тут itchief.ru/modx/login-registration. Теперь, когда группа пользователей создана, перейдем к настройке регистрации. За основу была взята эта заметка modx.pro/help/19551. Вызов сниппета выглядит так
{'!AjaxForm' | snippet:[
'snippet' =>'custRegister' //имя сниппета в котором будет происходить регистрация
'form' =>'@FILE chunks/forms/regForm.html' // чанк с формой
'activationEmailTpl' =>'lgnActivateEmailTpl' // чанк письма со ссылкой для подтверждения почты
'activationEmailSubject' =>'Подтверждение регистрации' // тема письма
'activationResourceId' => '30' // id ресурса на котором будет происходить подтверждение регистрации
'usergroups' =>'1' // группа в которую добавляем пользователя
'usernameField' =>'email' //имя поля в котором содержится username
'passwordField' =>'password' // имя поля с паролем
'validate'=>'nospam:blank,
password:required:minLength=^8^,
password_confirm:password_confirm=^password^,
fullname:required:minLength=^2^,
acceptRules2:required,
usergroups:required,
email:required:email', //список полей для валидации
'placeholderPrefix' =>'reg.' //префикс плейсхолдеров
]}
Чанк с формой регистрации выглядит так<form id="registrationForm" action="{$_modx->resource.id | url}" method="post">
<input type="hidden" name="nospam">
<input type="hidden" name="usergroups">
<input type="hidden" name="fullname">
<div class="box-field">
<div class="box-field__input">
<input type="text" class="form-control" name="name"
placeholder="Имя (обязательно)">
<label class="error error_fullname" ></label>
</div>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="text" class="form-control" name="lastname"
placeholder="Фамилия">
</div>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="email" class="form-control" name="email"
placeholder="Email (обязательно)">
<label class="error error_email"></label>
</div>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="tel" class="form-control js-mask-tel" name="phone"
placeholder="Телефон">
</div>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="password" class="form-control" name="password"
placeholder="Пароль (обязательно)">
<label class="error error_password"></label>
</div>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="password" class="form-control" name="password_confirm"
placeholder="Пароль (обязательно)">
<label class="error error_password_confirm"></label>
</div>
</div>
<div class="radio-registration-title">Вы регистрируетесь как:</div>
<div class="radio-registration flex-wrap justify-content-between">
<div class="radio-registration__item mr-0">
<label class="container">
<span class="box-field__label">Франчайзи</span>
<input type="radio" name="usergroup" value="3">
<span class="checkmark"></span>
</label>
</div>
<div class="radio-registration__item mr-0">
<label class="container">
<span class="box-field__label">Франчайзер</span>
<input type="radio" name="usergroup" value="2">
<span class="checkmark"></span>
</label>
</div>
<div class="radio-registration__item mr-0">
<label class="container">
<span class="box-field__label">Другое</span>
<input type="radio" name="usergroup" value="1">
<span class="checkmark"></span>
</label>
</div>
<div class="error error_usergroups w-100"></div>
</div>
<div class="popup-button">
<input type="submit" class="btn btn_popup" name="signup-btn" form="registrationForm" value="Регистрация">
</div>
<div class="box-field box-field_checkbox box-field_checkbox-popup flex-wrap">
<div class="error error_acceptRules2 w-100"></div>
<label class="checkbox-element checkbox-element_2">
<input type="checkbox" name="acceptRules2">
<span class="check-label">
<span class="check"></span>
<span class="check-text">
Регистрируясь и совершая вход, Вы принимаете условия
<a href="#" target="_blank">Политика конфиденциальности</a> и
<a href="#" target="_blank">Оферты на оказание услуг и сервисов.</a>
</span>
</span>
</label>
</div>
</form>
Здесь стоит обратить внимание на классы error_имя_поля они используются в js. Сниппет custRegister содержит такой код<?php
/*
Слияние массивов нужно чтобы пользователь мог сам
выбрать в качестве кого он будет зарегистрирован,
т.е. в какую группу пользователей попадет
*/
$scriptProperties = array_merge($scriptProperties, $_POST);
$result = $modx->runSnippet('Register', $scriptProperties); //вызываем стандартный сниппет компонента Login
foreach($modx->placeholders as $key => $ph){
if(strpos($key, $scriptProperties[placeholderPrefix].'error.') === 0){
$placeholders[$key] = $ph; //проверяем есть ли в результате работы ошибки
}
}
if(count($placeholders)){
return $AjaxForm->error('В форме содержатся ошибки', array('error' => $placeholders));
} else {
/*
'success' => '#successReg' - может быть и просто true,
но мне удобнее было передавать id модального окна которое надо закрыть;
'email' - это я вставляю в текст сообщения об успехе регистрации
*/
return $AjaxForm->success('Данные отправлены', array('success' => '#successReg', 'email' => $_POST['email']));
}
Ответ от сервера получаем в стандартном событии, которое идёт в комплекте с AjaxForm$(document).on('af_complete', function (event, response) {
if (!response.data.success) {
for (let key in response.data.error) {
let keys = key.split('.');
$(response.form).find('.error_' + keys[2]).html(response.data.error[key]); // вывод ошибок
}
} else {
/* здесь js-код который выполнится, если всё прошло хорошо */
console.log(response.data);
}
response.message = ''; // отключаем показ стандартных всплывающих уведомлений
});
Страница для подтверждения регистрации содержит стандартный сниппет ConfirmRegister
{'!ConfirmRegister' | snippet:[
'authenticate'=>'1',
'redirectTo'=>'1',
'errorPage'=>'1'
]}
Описание параметром смотрите в документации docs.modx.com/current/en/extras/login/login.confirmregister. На этом с регистрацией всё, переходим к авторизации. Тут я применил костыль в виде FormIt, поскольку так и не понял какие параметры надо передать в сниппет Login, чтобы он нормально отработал, но давайте по порядку.Сначала вызываем сниппет custAuth через AjaxForm
{'!AjaxForm' | snippet:[
'snippet' =>'custAuth',
'form' =>'@FILE chunks/forms/authForm.html', // чанк с формой
'loginResourceId' =>'33', // id личного кабинета
'validate'=>'password:required,
username:required,
acceptRules2:required',
'placeholderPrefix' =>'auth.' // префикс нужен для js
]}
Валидация здесь по проще, проверяем чтобы что-то ввели в поля. Чанк с формой ниже<form id="loginForm">
<div class="error error_msg"></div> // блок для вывода ошибок авторизации
<div class="box-field">
<div class="box-field__input">
<input type="text" class="form-control" name="username" placeholder="Логин или Email">
</div>
<label class="error error_username"></label>
</div>
<div class="box-field">
<div class="box-field__input">
<input type="password" class="form-control" name="password" placeholder="Пароль">
</div>
<label class="error error_password"></label>
</div>
<div class="popup-button">
<input type="submit" class="btn btn_popup" value="Войти">
</div>
<div class="box-field box-field_checkbox box-field_checkbox-popup flex-wrap">
<div class="error error_acceptRules2 w-100"></div>
<label class="checkbox-element checkbox-element_2">
<input type="checkbox" name="acceptRules2">
<span class="check-label">
<span class="check"></span>
<span class="check-text">
Регистрируясь и совершая вход, Вы принимаете условия
<a href="#" target="_blank">Политика конфиденциальности</a> и
<a href="#" target="_blank">Оферты на оказание услуг и сервисов.</a>
</span>
</span>
</label>
</div>
</form>
И собственно сам сниппет custAuth<?php
/*
Вызов FormIt это костыль для валидации полей, можно и без него,
но тогда не получится проверить с помощью AjaxForm стоит ли галочка
о согласии на обработку персональных данных, например.
*/
$result = $modx->runSnippet('FormIt', $scriptProperties);
foreach($modx->placeholders as $key => $ph){
if(strpos($key, $scriptProperties[placeholderPrefix].'error.') === 0){
$placeholders[$key] = $ph;
}
}
if (!count($placeholders)) {
$loginResponse = $modx->runProcessor('/security/login', $_POST); /* стандартный процессор modx,
важно чтобы названия полей соответствовали таковым в профиле пользователя в админке.
Список полей можно посмотреть тут http://www.webapplex.ru/rabota-s-polzovatelyami,-modx-api,-xpdo
или в БД или в схеме*/
if(!$loginResponse->response['success']){
$placeholders['auth.error.msg'] = '<span class="error">'.$loginResponse->response['message'].'</span>';
return $AjaxForm->error('В форме содержатся ошибки', array('error' => $placeholders));
}else{
$modx->user = $modx->getObject('modUser', array('username' => $_POST['username']));
$resource = $modx->getObject('modResource', $scriptProperties['loginResourceId']);
return $AjaxForm->success('Данные отправлены', array('success' => '#successAuth', 'url' => $modx->makeUrl($scriptProperties['loginResourceId'], $resource->get('context_key'), '', 'full' )));
}
}else{
return $AjaxForm->error('Form has errors', array('error' => $placeholders));
}
Переадресацию в кабинет можно сделать на js или не делать вообще если не нужно, я не делал, js-код выше. Дальше нужно будет дать возможность сбросить пароль, если пользователь его забыл. Для этого опять вызываем AjaxForm{'!AjaxForm' | snippet:[
'snippet' =>'custReset',
'form' =>'@FILE chunks/forms/resetForm.html',
'resetResourceId' => 32, // ресурс для активации нового пароля.
'emailTpl' => 'mylgnForgotPassEmail'
'emailSubject' => 'Восстановление пароля'
'validate'=>'email:email:required,acceptRules2:required',
'placeholderPrefix' =>'reset.'
]}
Чанки письма и формы любые на ваш вкус. Сниппет custReset выглядит так$scriptProperties = array_merge($scriptProperties, $_POST);
$result = $modx->runSnippet('FormIt', $scriptProperties);
foreach($modx->placeholders as $key => $ph){
if(strpos($key, 'reset.error.') === 0){
$placeholders[$key] = $ph;
}
}
if(!count($placeholders)){
$user = $modx->getObject('modUser', array('username' => $_POST['email']));
if($user){
$modx->runSnippet('ForgotPassword', $scriptProperties);
return $AjaxForm->success('Данные отправлены', array('success' => '#successReset', 'email' => $_POST['email']));
}else{
$placeholders['reset.error.msg'] = '<span class="error">Пользователь не найден. Проверьте корректность email.</span>';
return $AjaxForm->error('В форме содержатся ошибки', array('error' => $placeholders));
}
}else{
return $AjaxForm->error('В форме содержатся ошибки', array('error' => $placeholders));
}
Здесь всё аналогично предыдущим сниппетам. FormIt для проверки полей, а дальше просто вызываем ForgotPassword, который сбрасывает пароль и отправляет на почту письмо со ссылкой для активации нового пароля. Также я добавил проверку на существование пользователя.Ну и напоследок в модалке с формой авторbзации добавляем вызов HybridAuth
{'!HybridAuth' | snippet: [
'providerTpl' => '@FILE chunks/hybridauth/socialsAuthItem.html',
'loginTpl' => '@INLINE {$providers}'
]}
А в личном кабинете вызываем haProfile для того, чтобы можно было привязать аккаунты.{'!haProfile' | snippet:[
'profileTpl' => '@INLINE {$providers}',
'providerTpl' => '@FILE chunks/hybridauth/socialsAuthItem.html',
'activeProviderTpl' => '@FILE chunks/hybridauth/socialsAuthItemActive.html',
]}
Google требует отправлять приложение на проверку, причём при любых настройках api. На mailru звездец полный вот ссылка на нужную страницу api.mail.ru/sites/my/782604 или по запросу в Яндексе «mail.ru oauth api» первая ссылка ведёт на документацию из неё можно перейти к регистрации. И ещё все сниппеты компонента Login в чанках синтаксис fenom не признают от слова совсем, имейте в виду, кто не знал. Вроде всё. Будут вопросы или замечания, милости прошу в комментарии, надеюсь кому-то этот опус пригодится.
Поблагодарить автора
Отправить деньги
Комментарии: 3
Спасибо за статью. Надеюсь поможет.
Хорошая статья, особенно учитывая, что из магазина пропал компонент AjaxLogin. Экономит время.
Маленькая ошибка — при вызове сниппета custRegister пропущены запятые.
Маленькая ошибка — при вызове сниппета custRegister пропущены запятые.
А это разве влияет на работу?
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.