подгружаемое меню user для меню и все варианты его обработки

This commit is contained in:
2022-11-05 19:17:26 +03:00
parent bf5de5b25e
commit 86c720d946
8 changed files with 802 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
<script src="//www.google.com/recaptcha/api.js?hl=ru&" async defer></script>
<div class="g-recaptcha" data-sitekey="6Lf87gQTAAAAALmkG5ZsO0eJSvdSXcRvkxoPJCDB" data-type="image" data-theme="light"></div>

View File

@@ -0,0 +1,130 @@
{% load static %}{% comment %}
ШАБЛОН LOGIN-LOGOUT
Во избежании высоких нагрузок на сервер и для ускорения загрузки страницы
организована AJAX-подгрузка блока LOGIN-LOGOUT на уровне фронтенда (клиента).
Кеширование этого блока (и запросов с ним связанным) позволит снизить нагрузки.
В дальнейшем, в случае высоких нагрузок на сервис, возможна простая деградация
с помощью отключения этого блока. Также возможен перенос исполнения функционала
LOGIN-LOGOUT на отдельный сервер.
Даннеа Google reCAPTCHA: https://www.google.com/recaptcha/admin#site/319090428?setup
Публичный Ключ: 6Lf87gQTAAAAALmkG5ZsO0eJSvdSXcRvkxoPJCDB
Секретный ключ: 6Lf87gQTAAAAADlqsJQToiWqg7urOWPrbfG_9zJB
{% endcomment %}
<script type="text/javascript">
$.ajaxSetup({ cache: true , dataType: 'script' }); // включить кеширование подгружаемых JS-скриптов
$.getScript ( "{% static 'js/login_out.js' %}" ); // подгрузить JS-скрипт
</script>
{% if LOGGED_USER != "" %}<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-user"></span> {{ user.username|truncatechars:12 }} <b class="caret"></b></a>
<ul class="form-group form-group-sm dropdown-menu nav navbar-nav navbar-right" role="menu">
<form id="logout-form">{% csrf_token %}
<input type="hidden" name="status" value="" id="status">
<div class="input-group">
<a href="#">Настройки пользователя
<span class="glyphicon glyphicon-cog pull-right"></span></a>
</div>
<div class="input-group">
<a href="#">Статистика
<span class="glyphicon glyphicon-stats pull-right"></span></a>
</div>
<div class="input-group">
<a href="#">Отслеживается запросов
<small class="badge pull-right"> 2 </small></a>
</div>
<div class="input-group">
<a href="#">Новых предложений
<small class="badge pull-right"> 42 </small></a>
</div>
<div class="input-group" style="border:none;">
<button type="submit" class="btn btn-default btn-sm pull-right" name="logout" onclick="$('#status').val('logout');">Выйти&nbsp;<span class="glyphicon glyphicon-log-out"></span></button>
</div>
</form>
</ul>
{% else %}<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="in_ntr();" rel="nofollow"><span class="glyphicon glyphicon-user"></span> Вход <b class="caret"></b></a>
<ul class="form-group form-group-sm dropdown-menu nav navbar-nav navbar-right" role="menu">
<form id="login-reg-restore-form">{% csrf_token %}
<input type="hidden" name="status" value="" id="status">
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" class="form-control" id="username" name="username" placeholder="login" />
<span class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-username"></i>
</span>
</div>
<dIV class="collapse" id="ili">
<small class="input-group">или<br/></small>
</dIV>
<dIV class="collapse" id="mail">
<div class="input-group">
<span class="input-group-addon">@</span>
<input type="email" class="form-control" id="email" name="email" placeholder="you@email.ru" />
<span class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-email"></i>
</span>
</div>
</dIV>
<dIV class="collapse" id="pwd1">
<div class="input-group">
<span class="input-group-addon"><small class="glyphicon glyphicon-lock"></small></span>
<input type="password" class="form-control" id="password" name="password" placeholder="password" data-toggle="popover" />
<span class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-password"></i>
</span>
<span class="input-group-btn">
<button class="btn btn-default" id="look_pwd1" type="button"><small><small class="glyphicon glyphicon-eye-open"></small></small></button>
</span>
</div>
</dIV>
<dIV class="collapse" id="pwd_comment">
<i class="glyphicon glyphicon-info-sign"></i> <span id="pwd_comment_text"><!--- Содержимое этого блока будет подменяться во время набора и валидации пароля --->Шесть (или более) букв (прописных и строчных) и цифр</span>
</dIV>
<dIV class="collapse" id="pwd2">
<div class="input-group">
<span class="input-group-addon"><small class="glyphicon glyphicon-lock"></small></span>
<input type="password" class="form-control" id="password_repeat" name="password_repeat" placeholder="повторите passport" />
<span class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-password-repeat"></i>
</span>
<span class="input-group-btn">
<button class="btn btn-default" id="look_pwd2" type="button"><small><small class="glyphicon glyphicon-eye-open"></small></small></button>
</span>
</div>
</dIV>
<dIV class="collapse" id="capt">
<div class="input-group" id="captcha"><!---сюда по onclick="$('#captcha').load('/captcha');" подгрузится капча ---></div>
</dIV>
<dIV class="collapse" id="b_vhod">
<div class="input-group"><nobr>
<button type="submit" class="btn btn-primary btn-sm" name="enter" onclick="$('#status').val('enter');">Войти</button>
<small>
<a href="#" data-toggle="collapse" data-target="#b_reg" onclick="in_reg();" rel="nofollow">Регистрация</a>
<a href="#" data-toggle="collapse" data-target="#b_vost" onclick="in_vost();" rel="nofollow">Воcстановить пароль</a>
</small>
</nobr></div>
</dIV>
<dIV class="collapse" id="b_vost">
<div class="input-group"><nobr>
<button type="submit" class="btn btn-primary btn-sm" name="restore" id="restore" onclick="$('#status').val('restore');">Восстановить пароль</button>
<small>
<a href="#" data-toggle="collapse" data-target="#b_reg" onclick="in_reg();" rel="nofollow">Регистрация</a>
<a href="#" data-toggle="collapse" data-target="#b_vhod" onclick="in_ntr();" rel="nofollow">Войти</a>
</small>
</nobr></div>
</dIV>
<dIV class="collapse" id="b_reg">
<div class="input-group"><nobr>
<button type="submit" class="btn btn-default btn-sm" name="registration" onclick="$('#status').val('registration');"><i class="glyphicon glyphicon-ok"></i>&nbsp;Регистрация</button>
<small>
<a href="#" data-toggle="collapse" data-target="#b_vhod" onclick="in_ntr();" rel="nofollow">Войти</a>
<a href="#" data-toggle="collapse" data-target="#b_vost" onclick="in_vost();" rel="nofollow">Воcстановить пароль</a>
</small>
</nobr></div>
</dIV>
</form>
</ul>
{% endif %}

View File

@@ -0,0 +1,86 @@
{% load static %}{% comment %}
ШАБЛОН LOGIN-LOGOUT-AFTER
Все что происходит после того как щелкунули кнопки:
[[login]], [[регистрация]], [[восстановить пароль]]
и перед тем как блок '#login-logout' перегрузится в следующее состояние.
Фоновые картинки брать здесь: http://www.loadinfo.net/
{% endcomment %}
<script type="text/javascript">
function refresh() { $('#login-logout').load('/login-logout' ); }
$(document).ready(function(){
setTimeout("refresh();", {{ DELAY|default:5000 }});
});
</script>
{% if STATUS == "NO_CAPTCHA" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ ВОССТАНОВЛЕНИИ ПАРОЛЯ НЕ ПРОШЕЛ CAPTCHA --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color: red;"></span>&nbsp;Или вы робот, или не ввели captcha</a>
</li>
</ul>
{% elif STATUS == "LOGOUT" %}
<!--- ПОЛЬЗОВАТЕЛЬ РАЗЛОГИРУЕТСЯ --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-refresh" style="color:green;"></span>&nbsp;До свиданья</a>
</li>
</ul>
{% elif STATUS == "GOOD_LOGIN" %}
<!--- ПОЛЬЗОВАТЕЛЬ НЕ УГАДАЛ ЛОГИН --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ok-sign" style="color:green;"></span>&nbsp;Вхожу</a>
</li>
</ul>
{% elif STATUS == "SHORT_PWD" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ РЕГИСТРАЦИИ УКАЗАЛ НЕДОСТАТОЧНО ДЛИННЫЙ ПАРОЛЬ --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Слишком короткий пароль при регистрации</a>
</li>
</ul>
{% elif STATUS == "PWD1_AND_PWD2_DIFFERENT" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ РЕГИСТРАЦИИ НЕ СМОГ ПОВТОРИТЬ ПАРОЛЬ В ПРОВЕРОЧНОМ ПОЛЕ --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Основной и проверочный пароль не совпадают</a>
</li>
</ul>
{% elif STATUS == "DOUBLE_USER" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ РЕГИСТРАЦИИ ВЫБРАЛ УЖЕ СУЩЕСТВУЮЩИЙ USERNAME --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Пользователь с этим именем уже зарегистрирован</a>
</li>
</ul>
{% elif STATUS == "NO_USER4RESTORE" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ ВОССТАНОВЛЕНИИ ПАРОЛЯ УКАЗАЛ НЕСУЩЕСТВУЮЩИЙ USERNAME --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Логин {{ USERNAME|default:"с таким именем" }} не существует</a>
</li>
</ul>
{% elif STATUS == "NO_MULTIPLE_EMAIL" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ ВОССТАНОВЛЕНИИ УКАЗАЛ EMAIL, НА КОТОРЫЙ ЗЕРЕГИСТРИРОАННО НЕСКОЛЬКО USERNAME --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;На email {{ EMAIL|default:"" }} зарегистрировано несколько пользователей.</a>
</li>
</ul>
{% elif STATUS == "NO_EMAIL4RESTORE" %}
<!--- ПОЛЬЗОВАТЕЛЬ ПРИ ВОССТАНОВЛЕНИИ С ТАКИМ EMAIL НЕ ОБНАРУЖЕН --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Пользователся с email {{ EMAIL|default:"" }} не обнаружено.</a>
</li>
</ul>
{% elif STATUS == "RESTORE_MAIL_SENT" %}
<!--- ПОЛЬЗОВАТЕЛЮ ПРИ ВОССТАНОВЛЕНИИ ОТПРАВЛЕН EMAIL С ИНСТРУКЦИЯМИ --->
<ul class="nav navbar-nav navbar-right">
<li class="btn-default">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" onclick="refresh();" style="background-image: url({% static 'img/in_progress_grey1.gif' %});background-repeat: no-repeat;background-size: 100% 100%;"><span class="glyphicon glyphicon-ban-circle" style="color:red;"></span>&nbsp;Письмо для воммтановления пароля отправлено на email {{ EMAIL|default:"" }}.</a>
</li>
</ul>{% endif %}

View File

@@ -0,0 +1,11 @@
{% load static %}
<script type="text/javascript">
if ( document.getElementById("box") == null ) window.location.replace("/"); // проверяем, а не вызвали-ли этот popup отдельно
$(document).ready(function(){
timerId = setTimeout("$('#box').load('/center_popup_main');", 2000 ); // перегрухить центральный блок
$('#login-logout').load('/login-logout' ); // перегрузить блок login-logout
});
</script>
<h1>Пароль изменен</h1>
<ul>
<a type="button" class="btn btn-default" style="margin:1ex" onclick="clearTimeout(timerId);$('#box').load('/center_popup_main');"> Ок <span class="glyphicon glyphicon-remove-sign"></span> </a>

View File

@@ -0,0 +1,12 @@
{% load static %}
<script type="text/javascript">
if ( document.getElementById("box") == null ) window.location.replace("/"); // проверяем, а не вызвали-ли этот popup отдельно
$(document).ready(function(){ timerId = setTimeout("clearTimeout(timerId);$('#box').load('/center_popup_main');", 12000 ); });
</script>
<h1>Спасибо, вы подтвердили регистрацию:</h1>
<ul>
<li>пользователь: <strong>{{ USER }}</strong></li>
<li>e-mail: <strong>{{ EMAIL }}</strong></li>
</ul>
<a type="button" class="btn btn-default" style="margin:1ex" onclick="$('#box').load('/center_popup_main');"> Ок <span class="glyphicon glyphicon-ok-sign"></span> </a>

View File

@@ -0,0 +1,19 @@
{% load static %}<script type="text/javascript">
if ( document.getElementById("box") == null ) window.location.replace("/"); // проверяем, а не вызвали-ли этот popup отдельно
$(document).ready(function(){ timerId = setTimeout("$('#box').load('/center_popup_main');", 12000 ); });
</script>
<h1>Ошибка обработки:</h1>
<ul>
{% if CONFIRM_BAD_USER_ID == "YES" %}<li><strong class="bg-danger">ERR#010:</strong> Неверный userID</li>{% endif %}
{% if NO_USERNAME4CHANGE == "YES" %}<li><strong class="bg-danger">ERR#011:</strong> UserName для этого пользователя <i>({{ USERNAME }})</i> не совпадает с находящимся в базе</li>{% endif %}
{% if NO_EMAIL4CHANGE == "YES" %}<li><strong class="bg-danger">ERR#012:</strong> Email для этого пользователя ({{ EMAIL }}) не совпадает с находящимся в базе</li>{% endif %}
{% if NO_PWDSDIF4CHANGE == "YES" %}<li><strong class="bg-danger">ERR#013:</strong> Поля «password» и «повторите pessword» не совпадают</li>{% endif %}
{% if NO_PWDSHOT4CHANGE == "YES" %}<li><strong class="bg-danger">ERR#014:</strong> Введен «password» короче 5 знаков</li>{% endif %}
{% if CONFIRM_BAD_HASH == "YES" %}<li><strong class="bg-danger">ERR#019:</strong> Ошибка в проверочном хеше</li>{% endif %}
{% if CONFIRM_BAD_BAD == "YES" %}<li><strong class="bg-danger">ERR#_†_:</strong> Что-то пошло не так... и вообще не ясно почему. Как вы этого добились?!</li>{% endif %}
</ul>
<a type="button" class="btn btn-default" style="margin:1ex" onclick="clearTimeout(timerId);$('#box').load('/center_popup_main');"> Ок <span class="glyphicon glyphicon-remove-sign"></span> </a>

View File

@@ -0,0 +1,174 @@
{% load static %}
<script type="text/javascript">
// этот скрипт не имеет смысл делать внешним и выносить в static
// он высывается только когда пользователь восстанавливает пароль, а это случается крайне редко
if ( document.getElementById("box") == null ) window.location.replace("/"); // проверяем, а не вызвали-ли этот popup отдельно
$('#pwd_commentA').collapse('hide');
$(document).ready(function(){
// ПОДСВЕТИТЬ ПОДСКАЗКУ ПРИ НАБОРЕ ПАРОЛЯ
$("#passwordA").focus(function(){ $('#pwd_commentA').collapse('show'); });
$("#passwordA").blur(function(){ $('#pwd_commentA').collapse('hide'); });
// КНОПОЧКИ "ПОДГЛЯДИЕТЬ ПАРОЛЬ" на основном поле
$("#look_pwd1A").mousedown(function() { $("#passwordA").attr('type', 'text'); });
$("#look_pwd1A").mouseup(function() { $("#passwordA").attr('type', 'password'); });
$("#look_pwd1A").mouseout(function() { $("#passwordA").attr('type', 'password'); });
// КНОПОЧКИ "ПОДГЛЯДИЕТЬ ПАРОЛЬ" на поле "ПОВТОРИТЬ PASSWORD"
$("#look_pwd2A").mousedown(function() { $("#passwordA").attr('type', 'text'); $("#password_repeatA").attr('type', 'text'); });
$("#look_pwd2A").mouseup(function() { $("#passwordA").attr('type', 'password'); $("#password_repeatA").attr('type', 'password'); });
$("#look_pwd2A").mouseout(function() { $("#passwordA").attr('type', 'password'); $("#password_repeatA").attr('type', 'password'); });
// ПРОВЕРКА КОНТРОЛЬНОГО ПАРОЛЯ
$("#password_repeatA").keyup(function(){
if( $(this).val() != '')
if( $(this).val() == $('#passwordA').val() ) $("#valid-password-repeatA").css({ "color": "green" });
else $("#valid-password-repeatA").css({ "color": "red" });
else $("#valid-password-repeatA").css({ "color": "white" });
});
// ПРОВЕРКА КАЧЕСТВА ПАРОЛЯ
$("#passwordA").keyup(function(){
var ucase = new RegExp("[A-Z]+");
var lcase = new RegExp("[a-z]+");
var num = new RegExp("[0-9]+");
if( $(this).val() != '')
if( $(this).val().length >= 6 )
if( ucase.test($(this).val()) && lcase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "green" });
$('#pwd_comment_textA').text("Отличный пароль!");
$("#pwd_commentA").css({ "color": "green" });
}
else if ( ucase.test($(this).val()) && lcase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "grey" });
$('#pwd_comment_textA').text("Хороший пароль! Не хватает цифр.");
$("#pwd_commentA").css({ "color": "green" });
}
else if ( lcase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "grey" });
$('#pwd_comment_textA').text("Хороший пароль! Не хватает прописных букв.");
$("#pwd_commentA").css({ "color": "green" });
}
else if ( ucase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "grey" });
$('#pwd_comment_textA').text("Хороший пароль! Не хватает строчных букв.");
$("#pwd_commentA").css({ "color": "green" });
}
else if ( ucase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Слабый пароль! Не хватает цифр и строчных букв.");
$("#pwd_commentA").css({ "color": "orange" });
}
else if ( lcase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Слабый пароль! Не хватает цифр и прописных букв.");
$("#pwd_commentA").css({ "color": "orange" });
}
else if ( num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Слабый пароль! Не хватает букв.");
$("#pwd_commentA").css({ "color": "orange" });
}
else {
$("#valid-passwordA").css({ "color": "grey" });
$('#pwd_comment_textA').text("Странный пароль! Вы точно сможете его замнить?");
$("#pwd_commentA").css({ "color": "grey" });
}
else
if( ucase.test($(this).val()) && lcase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "grey" });
$('#pwd_comment_textA').text("Хороший пароль!.. но короткий.");
$("#pwd_commentA").css({ "color": "grey" });
}
else if ( ucase.test($(this).val()) && lcase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет цифр.");
$("#pwd_commentA").css({ "color": "orange" });
}
else if ( lcase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет прописных букв.");
$("#pwd_commentA").css({ "color": "orange" });
}
else if ( ucase.test($(this).val()) && num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "orange" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет строчных букв.");
$("#pwd_commentA").css({ "color": "orange" });
}
else if ( ucase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "red" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет строчных букв и цифр.");
$("#pwd_commentA").css({ "color": "red" });
}
else if ( lcase.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "red" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет прописных букв и цифр.");
$("#pwd_commentA").css({ "color": "red" });
}
else if ( num.test($(this).val()) ) {
$("#valid-passwordA").css({ "color": "red" });
$('#pwd_comment_textA').text("Короткий пароль!.. и нет букв.");
$("#pwd_commentA").css({ "color": "red" });
}
else {
$("#valid-passwordA").css({ "color": "red" });
$('#pwd_comment_textA').text("Странныйе символы! Переключитесь на латиницу!");
$("#pwd_commentA").css({ "color": "red" });
}
else {
$("#valid-passwordA").css({ "color": "white" });
$('#pwd_comment_textA').text('Шесть (или более) букв (прописных и строчных) и цифр');
$("#pwd_commentA").css({ "color": "grey" });
}
});
// ОБРАБОТЧИК КНОПКИ SUBMIT. ПЕРЕГРУЖАЕТ БЛОК #box
$('#restore-password').submit(
function enter(){
$.ajax({
url: "/change_password", //Адрес подгружаемой страницы
type: "POST", //Тип запроса
dataType: "html", //Тип данных
data: $("#restore-password").serialize(),
success: function(html){ $("#box").html(html)}
});
return false;
}
);
})
</script>
<h1>Смена пароля</h1>
<p>Для пользователя: <strong>{{ USERNAME }}</strong> <small>({{ EMAIL }})</small></p>
<form id="restore-password">
{% csrf_token %}
<input type="hidden" name="username" value="{{ USERNAME }}" />
<input type="hidden" name="user_id" value="{{ USER_ID }}" />
<input type="hidden" name="email" value="{{ EMAIL }}" />
<div class="col-md-5 pwdA">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></div>
<input type="password" class="form-control" id="passwordA" name="passwordA" placeholder="password" data-toggle="popover" />
<div class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-passwordA"></i>
</div>
<span class="input-group-btn">
<button class="btn btn-default" id="look_pwd1A" type="button"><small><small class="glyphicon glyphicon-eye-open"></small></small></button>
</span>
</div>
<dIV class="collapse" id="pwd_commentA" style="padding-top:2px;">
<i class="glyphicon glyphicon-info-sign"></i> <span id="pwd_comment_textA"><!--- Содержимое этого блока будет подменяться во время набора и валидации пароля --->Шесть (или более) букв (прописных и строчных) и цифр</span>
</dIV>
</div>
<dIV class="col-md-5 pwdA">
<div class="input-group">
<div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></div>
<input type="password" class="form-control" id="password_repeatA" name="password_repeatA" placeholder="повторите passport" />
<div class="input-group-addon check-it">
<i class="glyphicon glyphicon-ok-sign" id="valid-password-repeatA"></i>
</div>
<span class="input-group-btn">
<button class="btn btn-default" id="look_pwd2A" type="button"><small><small class="glyphicon glyphicon-eye-open"></small></small></button>
</span>
</div>
</dIV>
<dIV class="col-md-2 pwdA">
<button type="submit" class="btn btn-primary btn" name="change_pwd" onclick="$('#Astatus').val('enter');">Сменить пароль</button>
</dIV>
</form>

View File

@@ -0,0 +1,368 @@
# -*- coding: utf-8 -*-
__author__ = 'Sergei Erjemin'
from django.shortcuts import render, HttpResponseRedirect
from django.http import HttpRequest, HttpResponse
from django.contrib.auth.models import User, Group
from django.contrib import auth
from django.core import mail
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.template.context_processors import csrf
from time import time
import requests
import oknardia.settings
def captcha(request: HttpRequest) -> HttpResponse:
""" GOOGLE CAPTCHA
:param request: входящий http-запрос
:return response: исходящий http-ответ
"""
return render(request, "user_manager/captcha.html")
def menu_login_logout(request: HttpRequest) -> HttpResponse:
""" Подгружаемый блок с меню логин, логаут и тп.
:param request: входящий http-запрос
:return response: исходящий http-ответ
"""
# Этот блок подгружается в верхнее меню на каждой(!) страничке
# Во избежание высоких нагрузок на сервер и для ускорения загрузки страницы
# организована AJAX-подгрузка блока LOGIN-LOGOUT на уровне фронтенд (JS на клиенте).
#
# Кеширование этого блока на стороне браузера (и запросов с ним связанным) позволит снизить нагрузки.
#
# В дальнейшем, в случае высоких нагрузок на сервис, возможна простая деградация
# с помощью отключения этого блока. Также возможен перенос исполнения функционала
# LOGIN-LOGOUT на отдельный сервер.
to_template = {} # словарь, для передачи шаблону
template = "user_manager/login-logout.html" # шаблон для подгрузки GOOGLE CAPTCHA
if request.user.is_authenticated:
to_template.update({'LOGGED_USER': request.user.username})
else:
to_template.update({'LOGGED_USER': ""})
return render(request, template, to_template)
def confirm_email(request: HttpRequest, user_id: str = "1", hash_part_12: str = "") -> HttpResponse:
""" Подтверждение email-адреса пользователя.
:param request: входящий http-запрос
:param user_id: id пользователя
:param hash_part_12: хэш-сумма из 12 символов (кусок соленого хеша от пароля password[33:-1:2])
:return response: исходящий http-ответ
"""
time_start = time()
to_template = {} # словарь, для передачи шаблону
to_template.update({'CONFIRM_OK': "NO"})
template = "index.html" # шаблон, о том, что email не подтвержден
try:
try:
checking_user = User.objects.get(id=int(user_id))
to_template.update({'USER': checking_user.username})
except ObjectDoesNotExist:
to_template.update({'CONFIRM_BAD_USER_ID': "YES"})
return render(request, template, to_template)
if checking_user.password[33:-1:2] == hash_part_12:
# пользователь подтвердил свой e-mail
to_template.update({'EMAIL': checking_user.email})
# проверить, есть ли группа пользователей "checked_user" и если такой группы нет, то создать ее
group_checked, status_created = Group.objects.get_or_create(name="checked_user")
# проверить, есть ли группа пользователей "unchecked_user" и если такой группы нет, то создать ее
group_unchecked, status_created = Group.objects.get_or_create(name="unchecked_user")
# добавить пользователя в группу "checked_user"
group_checked.user_set.add(checking_user)
# удалить пользователя из группы "unchecked_user"
group_unchecked.user_set.remove(checking_user)
# записать данные о пользователе
checking_user.save()
to_template.update({'CONFIRM_OK': "YES"})
else:
to_template.update({'CONFIRM_BAD_HASH': "YES"})
except TypeError:
# вместо user_id пришла стока, которую нельзя преобразовать в int
to_template.update({'CONFIRM_BAD_BAD': "YES"})
to_template.update(csrf(request)) # токен, для метода POST и GET
to_template.update({'CLOCK': float(time()-time_start)})
return render(request, template, to_template)
def restore_password(request: HttpRequest, user_id: str = "1", hash_part_12: str = "") -> HttpResponse:
""" Восстановление пароля пользователя. Пользователь получил email со ссылкой в которой содержится
# UserID и кусок соленого хеш-пароля
:param request: входящий http-запрос
:param user_id: id пользователя
:param hash_part_12: хэш-сумма из 12 символов (кусок соленого хеша от пароля password[33:-1:2])
:return response: исходящий http-ответ
"""
time_start = time()
to_template = {} # словарь, для передачи шаблону
to_template.update({'CONFIRM_OK': "NO"})
template = "index.html" # шаблон, о том, что email не подтвержден
try:
try:
checking_user = User.objects.get(id=int(user_id))
to_template.update({'USER': checking_user.username})
except ObjectDoesNotExist:
to_template.update({'CONFIRM_BAD_USER_ID': "YES"})
return render(request, template, to_template)
if checking_user.password[33:-1:2] == hash_part_12:
# пользователь -- наш человек -- пришел с правильным ID и Хешем.
to_template.update({'CONFIRM_OK': "CHANGE_PWD"})
to_template.update({'EMAIL': checking_user.email})
to_template.update({'USERNAME': checking_user.username})
to_template.update({'USER_ID': user_id})
else:
to_template.update({'CONFIRM_BAD_HASH': "YES"})
except TypeError:
# вместо user_id пришла стока, которую нельзя преобразовать в int
to_template.update({'CONFIRM_BAD_BAD': "YES"})
to_template.update(csrf(request)) # токен, для метода POST и GET
to_template.update({'CLOCK': float(time()-time_start)})
return render(request, template, to_template)
def change_password(request: HttpRequest) -> HttpResponse:
""" Обработчик формы изменения пароля.
Получает через POST: email, username, user_id, passwordA и password_repeatA
ВАЖНО: passwordA и password_repeatA должны совпадать, и это надо проверять (проверяем тут)
:param request: входящий http-запрос
:return response: исходящий http-ответ
"""
time_start = time()
if request.method != 'POST':
return HttpResponseRedirect("/")
try:
to_template = {} # словарь, для передачи шаблону
to_template.update({'CONFIRM_OK': "NO"})
template = "user_manager/popup_confirm_email_or_restore_password_bad.html" # шаблон, о том, что всякие ошибки
try:
user = User.objects.get(id=int(request.POST['user_id']))
# print(f"user.id={user.id} \t user.email={user.email}")
if user.email != request.POST['email']:
to_template.update({"NO_EMAIL4CHANGE": "YES"})
to_template.update({"EMAIL": request.POST['email']})
return render(request, template, to_template)
if user.username != request.POST['username']:
to_template.update({"NO_USERNAME4CHANGE": "YES"})
to_template.update({"USERNAME": request.POST['username']})
response = render(request, template, to_template)
return response
if request.POST['passwordA'] != request.POST['password_repeatA']:
to_template.update({"NO_PWDSDIF4CHANGE": "YES"})
response = render(request, template, to_template)
return response
if len(request.POST['passwordA']) < 5:
to_template.update({"NO_PWDSHOT4CHANGE": "YES"})
response = render(request, template, to_template)
return response
to_template.update({'CONFIRM_OK': "CHANGE_PASSWORD"})
template = "user_manager/popup_change_password_ok.html" # шаблон, о том, что пароль поменялся
user.set_password(request.POST['passwordA']) # поменять пароль
user.save() # записать новый пароль в базу
# ТЕХНИЧЕСКИЙ ДОЛГ: почему-то не происходит логирование после изменения пароля.
auth.authenticate(username=request.POST['username'],
password=request.POST['password']) # залогировать пользователя
to_template.update({'CLOCK': float(time()-time_start)})
return render(request, template, to_template)
except ObjectDoesNotExist:
to_template.update({"CONFIRM_BAD_USER_ID": "YES"})
return render(request, template, to_template)
except TypeError:
return HttpResponseRedirect("/")
def form_user_menu_processing(request: HttpRequest) -> HttpResponse:
""" Обработчик всех вариантов формы в меню пользователя: login, logout, регистрация и восстановление пароля.
:param request: входящий http-запрос
:return response: исходящий http-ответ
"""
if request.method != 'POST':
return HttpResponseRedirect("/")
if 'status' not in request.POST:
return HttpResponseRedirect("/")
if request.POST['status'] == "":
return HttpResponseRedirect("/")
to_template = {} # словарь, для передачи шаблону
template = "user_manager/login-logout_after.html" # шаблон для подгрузки GOOGLE CAPTCHA
# БЛОК -- LOGOUT
if request.POST['status'] == "logout":
to_template.update({'STATUS ': "LOGOUT"})
to_template.update({"DELAY": "500"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
auth.logout(request) # <------------------ разлогирование
# Переход на главную страницу делать нельзя. Не выкидывать же на главную?? ...return HttpResponseRedirect("/")
return render(request, template, to_template)
# БЛОК -- LOGIN
elif request.POST['status'] == "enter":
user = auth.authenticate(username=request.POST['username'],
password=request.POST['password'])
if user is not None and user.is_active:
# пользователь прошел проверку и залогировался
to_template.update({'STATUS ': "GOOD_LOGIN"})
to_template.update({"DELAY": "500"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
auth.login(request, user) # <------------------ логирование
else:
# пользователь не прошел проверку (не логирован)
to_template.update({'STATUS ': "BAD_LOGIN"})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)
# БЛОК -- РЕГИСТРАЦИЯ НОВОГО ПОЛЬЗОВАТЕЛЯ
elif request.POST['status'] == "registration":
if len(request.POST['password']) < 4:
# очень короткий пароль
to_template.update({'STATUS ': "SHORT_PWD"})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
elif request.POST['password'] != request.POST['password_repeat']:
# поля "пароль" и "повторите пароль" не совпадают
to_template.update({'STATUS ': "PWD1_AND_PWD2_DIFFERENT"})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
elif len(User.objects.filter(username=request.POST['username'])) > 0:
# пользователь с таким именем уже существует
to_template.update({'STATUS ': "DOUBLE_USER"})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
elif request.POST['email'] != "":
# не пустой email и можно регистрировать
# создать пользователя
user = User.objects.create_user(username=request.POST['username'],
email=request.POST['email'],
password=request.POST['password'],
first_name="")
user.is_staff = False
user.is_superuser = False
user.last_name = f"User{User.objects.count()+1:04d}"
# проверить, есть ли группа пользователей "unchecked_user" и если такой группы нет, то создать ее
group, status_created = Group.objects.get_or_create(name="unchecked_user")
# добавляем пользователя в группу "unchecked_user"
user.groups.add(group.id)
# записываем в базу
user.save()
# читаем из базы, чтобы получить хеш пароля и прочее
user = User.objects.get(id=user.id)
# отправить письмо пользователю, для проверки email
message = f"Уважаемый получатель,\n\n" \
f"Вы (или кто-то вместо вас) зарегистрировал ваш e-mail ({user.email}) и login " \
f"({user.username}) в агрегаторе коммерческих предложений пластиковых окон 'ОКНАРДИЯ'.\n\n" \
f"Если это были не вы или регистрация произошла случайно -- не реагируйте на это письмо.\n\n" \
f"Иначе подтвердите свой login и email, перейдя по ссылке:\n" \
f"https://oknardia.ru/USER_{user.id:05d}/CONFIRM:{user.password[33:-1:2]}\nЭто позволит вам " \
f"в любое время и без усилий посмотреть самые актуальные предложения по вашим\nзапросам, " \
f"восстановить пароль в случае утери, и получать все остальные блага зарегистрированного \n" \
f"пользователя.\n\nВы не будете получать никаких рассылок и не заказанных отправлений, кроме " \
f"тех, что указаны в вашем\nпрофиле: https://oknardia.ru/USER_{user.id:05d}\nТам же вы " \
f"сможете, при необходимости, сменить свой email, пароль, имя пользователя и, если захотите,\n" \
f"навсегда удалить свои данные из базы агрегатора 'ОКНАРДИЯ'.\n\n\n" \
f"---------------------------------------\nС уважением,\nАдминистрация агрегатора " \
f"коммерческих\nпредложений пластиковых окон 'ОКНАРДИЯ'\nhttps://oknardia.ru"
try:
# вручную открываем коннект для работы с почтовым сервером
connection = mail.get_connection()
connection.open()
# Собираем вручную почтовое сообщение
email = mail.EmailMessage('ОКНАРДИЯ: подтверждение регистрации', # sub
message, # тело сообщения
'info@oknardia.ru', # from
[user.email], # to
connection=connection) # почтовое соединение
email.send() # отправили почту
connection.close() # закрыли почтовый коннект
except Exception as e:
# Что-то пошло не так и почта не отправилась. Надо подумать что в этим делать
print("ОШИБКА ОТПРАВКИ ПОЧТЫ: ", e)
# Логируемся пользователем
user = auth.authenticate(username=request.POST['username'], password=request.POST['password'])
auth.login(request, user)
return render(request, template, to_template)
# БЛОК -- ВОССТАНОВЛЕНИЕ ПАРОЛЯ
if request.POST['status'] == "restore":
to_template.update({'STATUS ': "RESTORE"})
# вот так узнают IP-клиента -----------------------------+
ip = request.META.get('HTTP_X_FORWARDED_FOR') # |
if ip: # |
ip = ip.split(',')[0] # |
else: # |
ip = request.META.get('REMOTE_ADDR') # <------------+
# а вот так узнаем пройдена каптча или нет
verify_recaptha = requests.get("https://www.google.com/recaptcha/api/siteverify",
params={'secret': oknardia.settings.CAPTCHA_PRIVATE_KEY,
'response': request.POST['g-recaptcha-response'],
'remoteip': ip},
verify=True)
if not verify_recaptha.json().get("success", False):
# Каптча не пройдена. Идите на хуй!
to_template.update({"STATUS": "NO_CAPTCHA"})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)
username_restore_by = "",
id_restore_by = 0
email_restore_by = ""
part_hash_restore_by = ""
if request.POST['username'] != "":
try:
user = User.objects.get(username=request.POST['username'])
id_restore_by = user.id
username_restore_by = user.username
email_restore_by = user.email
part_hash_restore_by = user.password[33:-1:2]
except ObjectDoesNotExist:
# такого пользователя нет, идите на хуй
to_template.update({"STATUS": "NO_USER4RESTORE"})
to_template.update({"USERNAME": request.POST['username']})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)
if request.POST['email'] != "":
try:
user = User.objects.get(email=request.POST['email'])
id_restore_by = user.id
username_restore_by = user.username
email_restore_by = user.email
part_hash_restore_by = user.password[33:-1:2]
except MultipleObjectsReturned:
# Пользователей с такими email много... идите на хуй
to_template.update({"STATUS": "NO_MULTIPLE_EMAIL"})
to_template.update({"EMAIL": request.POST['email']})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)
except ObjectDoesNotExist:
# Пользователей с такими email нет... идите на хуй
to_template.update({"STATUS": "NO_EMAIL4RESTORE"})
to_template.update({"EMAIL": request.POST['email']})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)
# отправить письмо пользователю, для смены пароля email
message = f"Восстановление пароля на 'ОКНАРДИЯ'\n\nВы запросили сброс пароля для аккаунта " \
f"\'{username_restore_by}\' с адресом \'{email_restore_by}\'.Пожалуйста, подтвердите сброс и " \
f"восстановление пароля, перейдя\nпо ссылке ниже.\n\n" \
f"https://oknardia.ru/USER_{id_restore_by:05d}/RESTORE:{part_hash_restore_by}\n\nЕсли вы получили " \
f"письмо по ошибке, просто проигнорируйте его.\n\n\n---------------------------------------\n" \
f"С уважением,\nАдминистрация агрегатора коммерческих\nпредложений пластиковых окон 'ОКНАРДИЯ'\n" \
f"https://oknardia.ru"
try:
# вручную открываем коннект для работы с почтовым сервером
connection = mail.get_connection()
connection.open()
# Собираем вручную почтовое сообщение
email = mail.EmailMessage('ОКНАРДИЯ: восстановление пароля', # subj
message, # тело сообщения
'info@oknardia.ru', # from
[email_restore_by], # to
connection=connection) # почтовое соединение
email.send() # отправили почту
connection.close() # закрыли почтовый коннект
except Exception as e:
# Что-то пошло не так и почта не отправилась. Надо подумать, что с этим делать
print("ОШИБКА ОТПРАВКИ ПОЧТЫ: ", e)
to_template.update({"STATUS'": "RESTORE_MAIL_SENT"})
to_template.update({"EMAIL": email_restore_by})
to_template.update({"DELAY": "5000"}) # ЗАДЕРЖКА ОБНОВЛЕНИИЯ СТАТУСА
return render(request, template, to_template)