Пост посвящается всем пережившим вчерашний конец света ;)
Мой проект (я его упоминал уже как-то, если в двух словах - он про спортивные соревнования) развивается медленно (потому что спросом не пользуется) и спросом не пользуется (потому что развивается медленно). Но раз в год он точно нужен и востребован, и в прошлый раз там даже какое-то количество людей зарегистрировалось. А раз приближается время, когда все эти люди потянутся на мой сайт опять, да не просто потянутся, а обнаружат, что не могут вспомнить свой пароль, то и эту проблему как-то придется решать. А как? Средствами апекса - никак (пока никак, но об этом позже).
Я забыл пароль :(
Ну с кем не бывает. Если забыл, надо или вспоминать, или восстанавливать. А как вспоминать, если вводил его всего один раз, и то год назад? Значит, без нас забывчивому пользователю не обойтись! Идем на страницу 101 (стандарная страница логина), добавляем там элемент с типом Display Only и дефолтным значением<u><a href="f?p=&APP_ID.:102:&APP_SESSION.:">Забыли пароль?</a></u>
В разделе Label удаляем текст ярлыка и выбираем Template "No Label".
К чему такой огород? В принципе, ссылку можно создать, просто добавив в Region Source региона вышеприведенный код. Но тогда ссылка будет нарисована просто в углу, а в нашем случае она будет выставлена аккуратно под полем для ввода пароля и выровнена по левому краю:
Страница восстановления пароля
Страницу восстановления я решил сделать с ajax'ом. Что самое странное, совсем без извращений обойти не удалось. Пожелания мои к странице были следующие:- пользователь для идентификации может ввести логин или адрес электронной почты от своей учетной записи
- в случае, когда логин или почта найдены, сбросить пароль и выслать на указанный в учетной записи адрес письмо с временным паролем
- если не найден логин/пароль - сообщить об этом пользователю
- запрос делать с помощью ajax, выводить на страницу сообщение о результате.
Зато есть возможность это обойти: создаем обычный Display Only элемент страницы, очищаем у него Label, в HTML Form Element Attributes вставляем строчку onclick="recover_pwd()" (это будет наша javascript-функция для восстановления пароля), а Defualt Value - вот такое:
<button type="button">Восстановить</button>
И получаем кнопку, которая выглядит как HTML Button и при этом вызывает javascript.
Код
Javascript-вызов подробно описывать не буду - там почти все то же самое, что и здесь. Только в этот раз будем передавать два параметра вместо одного (еще напомню, что ajax в апекс-приложениях с авторизацией и без работает немного по-разному).Теперь код для смены пароля:
create or replace function "RECOVER_PWD"
(p_login VARCHAR2,
p_email VARCHAR2) return varchar2
is
u_id number;
u_mail varchar2(255);
by_login boolean;
by_email boolean;
new_pwd varchar2(255) := '123456'; -- это наш временный пароль
new_pwde varchar2(255);
begin
if p_login is not null then
-- находим ID пользователя и email по указанному логину:
by_login := true;
select id, email
into u_id, u_mail
from users
where login = UPPER(p_login);
-- если у пользователя не указан email - сообщаем об этом
-- (остальные варианты обработаем позже):
if u_mail is null then
return('Вы не указали почтового адреса при регистрации. ' ||
'Для восстановления учетной записи свяжитесь с администрацией.');
end if;
elsif p_email is not null then
-- находим ID пользователя и email по указанному почтовому адресу:
by_email := true;
select id
into u_id
from users
where email = LOWER(p_email);
end if;
-- если досюда добрались, значит, нашли указанного пользователя и его адрес.
-- получаем хэш временного пароля:
new_pwde:=dbms_obfuscation_toolkit.md5(input_string => new_pwd);
-- хэш - в таблицу:
update users set pwd = new_pwde where id = u_id;
-- пароль - пользователю на почту:
send_recovery_info(nvl(p_email, u_mail), new_pwd);
-- и отчитаемся:
return('Временный пароль выслан на почтовый адрес, указанный вами при регистрации.');
exception
-- в случае, если селекты ничего не нашли, определяем с помощью
-- переменных by_login и by_email по какому полю искали:
when no_data_found then
if by_login then
return('Нет ни одной учетной записи с таким именем пользователя');
end if;
if by_email then
return('Нет ни одной учетной записи с таким адресом электроной почты');
end if;
when too_many_rows then
-- А это уже караул, кто ж такой бардак в таблице развел???
-- Сделайте уже что-нибудь!!! Только без меня...
end;
Соответственно, Application Process для этого вызова выглядит так:
begin htp.prn(recover_pwd(:P102_LOGIN, :P102_EMAIL)); end;
Мы передаем в нашу основную функцию логин и адрес со страницы восстановления пароля, а возвращаемый результат функции - текстовое описание того, что именно произошло - отправляем обратно на страницу, чтобы пользователь знал, что его запрос не исчез в пучине интернета, а как-то обработался. В случае, если все прошло успешно - пользователю на найденный адрес отправляется уведомление (функция send_recovery_info с 43-й строки):
create or replace procedure "SEND_RECOVERY_INFO"
(p_email VARCHAR2, p_new_pwd varchar2)
is
l_body varchar2(32767);
res integer;
begin
l_body := 'Ваш временный пароль: ' || p_new_pwd || '. Поменяйте его как можно быстрее.';
res := APEX_MAIL.SEND(p_email, 'ваш@обратный.адрес', l_body, NULL, 'Восстановление пароля');
end;
Здесь мы используем процедуру SEND из пакета APEX_MAIL - отправить письмо из апекса проще простого, как видите. Помните только, что вторым параметром придется поставить валидный обратный email - рассылать спам вам апекс не даст. Хотя, когда я отправил тестовое письмо самому себе, gmail поместил его в спам и дополнительно сообщил, что письмо, возможно, отправлено не с того адреса, который указан в поле "от кого" - так что хоть спам вы и не рассылаете, но ваши адресаты его получают ;)
А тем временем на странице у пользователя...
...долго сказка сказывается, да быстро код выполняется. Чтобы увидеть ответ, надо его куда-то вывести. Тут либо у апекса некоторые проблемы, либо я что-то не так понял :( Стандартное апексовое окошко вверху страницы (например, такое зелененькое вверху, появляется, когда вы редактируете свойства какой-нибудь страницы) не приспособлено для ajax - его надо заполнять значением перед показом страницы. Можно сделать просто вызов функции alert(), но тогда браузер вам покажет модальное окно с сообщением, а модальные окна без особой необходимости - это ЗлоЪ™ в чистом виде, вам это любой специалист по юзабилити подтвердит. Я решил сделать так: создал элемент с названием P102_RESPONSE и разместил его ниже. Тут опять возник сюрприз... Чтобы поменять значение элемента страницы, я раньше использовал функцию html_GetElement('elementName').value, но тут оказалось, что таким способом можно менять только значения полей для ввода, а для Display Only элементов надо пользоваться функцией $s (вот кстати документация по всем функциям, чтобы долго не искать):function recover_pwd() {
/*Ну тут мы посылаем запрос серверу, это все уже умеют делать, получаем ответ... */
var gReturn = get.get();
/* и показываем пользователю: */
$s('P102_RESPONSE', gReturn);
}
Вот так можно сделать восстановление пароля. Вроде ничего существенного не забыл...

Комментариев нет:
Отправить комментарий