mirror of
https://github.com/haiwen/seahub.git
synced 2025-07-15 07:52:14 +00:00
[share link] email address verification: redesigned the 'verification form' UI & the 2 emails (#6399)
This commit is contained in:
parent
c64bb90669
commit
cd5a9b824c
@ -2065,3 +2065,11 @@ a.sf-popover-item {
|
||||
background: #6e7687;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
#email-audit-form .email-input {
|
||||
width: 172px;
|
||||
}
|
||||
|
||||
#email-audit-form .get-code-btn {
|
||||
width: 102px;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
||||
from seahub.avatar.templatetags.avatar_tags import api_avatar_url
|
||||
from seahub.utils import get_user_repos
|
||||
from seahub.utils.mail import send_html_email_with_dj_template
|
||||
from django.utils.translation import gettext as _
|
||||
from seahub.settings import SECRET_KEY
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -308,7 +309,7 @@ def get_search_repos(username, org_id):
|
||||
return repos
|
||||
|
||||
def send_share_link_emails(emails, fs, shared_from):
|
||||
subject = "Share links"
|
||||
subject = _("A share link for you")
|
||||
for email in emails:
|
||||
c = {'url': "%s?email=%s" % (fs.get_full_url(), email), 'shared_from': shared_from}
|
||||
send_success = send_html_email_with_dj_template(
|
||||
|
@ -12,6 +12,7 @@ from seahub.share.models import FileShare, UploadLinkShare
|
||||
from seahub.share.utils import SCOPE_SPECIFIC_EMAILS, SCOPE_ALL_USERS, SCOPE_SPECIFIC_USERS
|
||||
from seahub.utils import render_error
|
||||
from seahub.utils import normalize_cache_key, is_pro_version, redirect_to_login
|
||||
from seahub.utils.auth import get_login_bg_image_path
|
||||
from seahub.constants import REPO_SHARE_LINK_COUNT_LIMIT
|
||||
|
||||
|
||||
@ -23,9 +24,14 @@ def _share_link_auth_email_entry(request, fileshare, func, *args, **kwargs):
|
||||
if request.session.get(session_key) is not None:
|
||||
return func(request, fileshare, *args, **kwargs)
|
||||
|
||||
login_bg_image_path = get_login_bg_image_path()
|
||||
if request.method == 'GET':
|
||||
email = request.GET.get('email', '')
|
||||
return render(request, 'share/share_link_email_audit.html', {'email': email, 'token': fileshare.token})
|
||||
return render(request, 'share/share_link_email_audit.html', {
|
||||
'email': email,
|
||||
'token': fileshare.token,
|
||||
'login_bg_image_path': login_bg_image_path,
|
||||
})
|
||||
|
||||
elif request.method == 'POST':
|
||||
code_post = request.POST.get('code', '')
|
||||
@ -40,7 +46,8 @@ def _share_link_auth_email_entry(request, fileshare, func, *args, **kwargs):
|
||||
return func(request, fileshare, *args, **kwargs)
|
||||
else:
|
||||
return render(request, 'share/share_link_email_audit.html', {
|
||||
'err_msg': 'Invalid token, please try again.',
|
||||
'login_bg_image_path': login_bg_image_path,
|
||||
'err_msg': _('Invalid verification code, please try again.'),
|
||||
'email': email_post,
|
||||
'code': code,
|
||||
'token': fileshare.token,
|
||||
|
@ -2,15 +2,16 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block email_con %}
|
||||
|
||||
{% autoescape off %}
|
||||
|
||||
<p style="color:#121214;font-size:14px;">{% trans "Hi," %}</p>
|
||||
|
||||
<p style="font-size:14px;color:#434144;">
|
||||
{% blocktrans %}Your code is {{code}}, this code will be valid for one hour.{% endblocktrans%}
|
||||
{% blocktrans %}The verification code is {{code}}, and it will be valid in one hour.{% endblocktrans%}
|
||||
</p>
|
||||
|
||||
{% endautoescape %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block thanks %}
|
||||
{% endblock %}
|
||||
|
@ -1,80 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block main_panel %}
|
||||
<div class="new-narrow-panel" role="main">
|
||||
<h2 class="hd">{% trans "Email Verification" %}</h2>
|
||||
<form action="" method="post" id="link-audit-form" class="con">{% csrf_token %}
|
||||
<p class="tip">{% trans "Please provide your email address to continue." %}</p>
|
||||
<label for="email">{% trans "Email" %}</label>
|
||||
<input id="email" type="text" class="input email-input" name="email" value="{{email}}" />
|
||||
<button type="button" id="get-code" class="get-code-btn">{% trans "Get code" %}</button><br />
|
||||
|
||||
<label for="code">{% trans "Verification code" %}</label><br />
|
||||
<input id="code" type="text" class="input" name="code" value="{{code}}" />
|
||||
|
||||
{% if err_msg %}
|
||||
<p class="error">{{ err_msg }}</p>
|
||||
{% else %}
|
||||
<p class="error hide"></p>
|
||||
{% endif %}
|
||||
|
||||
<input type="submit" value="{% trans "Submit" %}" />
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$('#get-code').on('click', function() {
|
||||
var email = $('input[name="email"]').val().trim();
|
||||
if (!email) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var $this = $(this);
|
||||
var originalText = $this.text(); // Remember the original text content
|
||||
var seconds = 60;
|
||||
|
||||
$this.prop('disabled', true).addClass('btn-disabled');
|
||||
$this.text(originalText + '(' + seconds + 's)');
|
||||
// do a set interval, using an interval of 1000 milliseconds
|
||||
// and clear it after the number of seconds counts down to 0
|
||||
var interval = setInterval(function() {
|
||||
// decrement the seconds and update the text
|
||||
seconds = seconds - 1;
|
||||
$this.text(originalText + ' (' + seconds + 's)');
|
||||
if (seconds === 0) { // once seconds is 0...
|
||||
$this.prop('disabled', false).removeClass('btn-disabled')
|
||||
.text(originalText); // reset to original text
|
||||
clearInterval(interval); // clear interval
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
$.ajax({
|
||||
url: "{% url "ajax_get_link_audit_code" %}",
|
||||
type: 'POST',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: {
|
||||
token: "{{token}}",
|
||||
email: email
|
||||
},
|
||||
success: function() {
|
||||
feedback("{% trans "A verification code has been sent to the email." %}", 'success');
|
||||
},
|
||||
error: function(xhr) {
|
||||
var error_msg = prepareAjaxErrorMsg(xhr);
|
||||
$('.error', $this.closest('form')).html(error_msg).removeClass('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#link-audit-form').on('submit', function() {
|
||||
var email = $('[name="email"]').val().trim();
|
||||
var code = $('[name="code"]').val().trim();
|
||||
if (!email || !code) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -2,18 +2,21 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block email_con %}
|
||||
|
||||
{% autoescape off %}
|
||||
|
||||
<p style="color:#121214;font-size:14px;">{% trans "Hi," %}</p>
|
||||
|
||||
<p style="font-size:14px;color:#434144;">
|
||||
{% blocktrans %}
|
||||
{{ shared_from }} has shared a library with you.
|
||||
Please click <a href={{ url }}>here</a> to verify your email.
|
||||
{{ shared_from }} shared a file link with you.
|
||||
{% endblocktrans%}
|
||||
</p>
|
||||
<p style="font-size:14px;color:#434144;">
|
||||
{% blocktrans %}
|
||||
You can click <a href={{ url }}>here</a> to verify your email address and visit it.
|
||||
{% endblocktrans%}
|
||||
</p>
|
||||
|
||||
{% endautoescape %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block thanks %}
|
||||
{% endblock %}
|
||||
|
@ -1,16 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block sub_title %}{% trans "Email address verification" %} - {% endblock %}
|
||||
{% block header_css_class %}hide{% endblock %}
|
||||
{% block extra_base_style %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/seafile-ui.css" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_style %}
|
||||
<style type="text/css">
|
||||
html, body, #wrapper { height:100%; }
|
||||
#wrapper {
|
||||
background: url('{{ MEDIA_URL }}{{login_bg_image_path}}') center top no-repeat scroll;
|
||||
background-size: cover;
|
||||
padding-top:1px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block main_panel %}
|
||||
<div class="new-narrow-panel" role="main">
|
||||
<h2 class="hd">{% trans "Email Verification" %}</h2>
|
||||
<form action="" method="post" id="link-audit-form" class="con">{% csrf_token %}
|
||||
<p class="tip">{% trans "Please provide your email address to continue." %}</p>
|
||||
<label for="email">{% trans "Email" %}</label>
|
||||
<input id="email" type="text" class="input email-input" name="email" value="{{email}}" />
|
||||
<button type="button" id="get-code" class="get-code-btn">{% trans "Get code" %}</button><br />
|
||||
<label for="code">{% trans "Verification code" %}</label><br />
|
||||
<input id="code" type="text" class="input" name="code" value="{{code}}" />
|
||||
<div class="login-panel-outer-container vh">
|
||||
<div class="login-panel" id="log-in-panel">
|
||||
<h1 class="login-panel-hd">{% trans "Email address verification" %}</h1>
|
||||
<p class="text-center">{% trans "Please provide your email address to continue." %}</p>
|
||||
<form action="" method="post" id="email-audit-form" class="con">{% csrf_token %}
|
||||
<div class="d-flex mt-2 mb-3">
|
||||
<input type="text" class="input email-input mr-2" name="email" value="{{email}}" placeholder="{% trans "Enter your email address" %}" />
|
||||
<button type="button" id="get-code" class="btn btn-primary btn-sm text-truncate get-code-btn flex-shrink-0" title="{% trans "Get code" %}">{% trans "Get code" %}</button>
|
||||
</div>
|
||||
<input id="code" type="text" class="input d-block" name="code" placeholder="{% trans "Paste the verification code here" %}" />
|
||||
|
||||
{% if err_msg %}
|
||||
<p class="error">{{ err_msg }}</p>
|
||||
@ -18,12 +36,23 @@
|
||||
<p class="error hide"></p>
|
||||
{% endif %}
|
||||
|
||||
<input type="submit" value="{% trans "Submit" %}" />
|
||||
<button type="submit" class="submit btn btn-primary btn-block h-auto">{% trans "Submit" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_script %}
|
||||
<script type="text/javascript">
|
||||
$('.login-panel-outer-container').prepend($($('#logo').html()).attr({'height': 40}).addClass('login-panel-logo'));
|
||||
var $el = $('.login-panel-outer-container');
|
||||
var elHeight = $el.outerHeight();
|
||||
var wdHeight = $(window).height();
|
||||
if (wdHeight > elHeight) {
|
||||
$el.css({'margin-top': (wdHeight - elHeight)/2});
|
||||
}
|
||||
$el.removeClass('vh');
|
||||
|
||||
$('#get-code').on('click', function() {
|
||||
var email = $('input[name="email"]').val().trim();
|
||||
if (!email) {
|
||||
@ -31,10 +60,11 @@ $('#get-code').on('click', function() {
|
||||
}
|
||||
|
||||
var $this = $(this);
|
||||
var originalText = $this.text(); // Remember the original text content
|
||||
//var originalText = $this.text(); // Remember the original text content
|
||||
var originalText = "{% trans "Resend" %}";
|
||||
var seconds = 60;
|
||||
|
||||
$this.prop('disabled', true).addClass('btn-disabled');
|
||||
$this.prop('disabled', true);
|
||||
$this.text(originalText + '(' + seconds + 's)');
|
||||
// do a set interval, using an interval of 1000 milliseconds
|
||||
// and clear it after the number of seconds counts down to 0
|
||||
@ -43,7 +73,7 @@ $('#get-code').on('click', function() {
|
||||
seconds = seconds - 1;
|
||||
$this.text(originalText + '(' + seconds + 's)');
|
||||
if (seconds === 0) { // once seconds is 0...
|
||||
$this.prop('disabled', false).removeClass('btn-disabled')
|
||||
$this.prop('disabled', false)
|
||||
.text(originalText); // reset to original text
|
||||
clearInterval(interval); // clear interval
|
||||
}
|
||||
@ -59,19 +89,19 @@ $('#get-code').on('click', function() {
|
||||
email: email
|
||||
},
|
||||
success: function() {
|
||||
feedback("{% trans "A verification code has been sent to the email." %}", 'success');
|
||||
feedback("{% trans "A verification code has been sent to the email address." %}", 'success');
|
||||
},
|
||||
error: function(xhr) {
|
||||
var error_msg = prepareAjaxErrorMsg(xhr);
|
||||
$('.error', $this.closest('form')).html(error_msg).removeClass('hide');
|
||||
$this.prop('disabled', false).removeClass('btn-disabled')
|
||||
$this.prop('disabled', false)
|
||||
.text(originalText); // reset to original text
|
||||
clearInterval(interval);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#link-audit-form').on('submit', function() {
|
||||
$('#email-audit-form').on('submit', function() {
|
||||
var email = $('[name="email"]').val().trim();
|
||||
var code = $('[name="code"]').val().trim();
|
||||
if (!email || !code) {
|
||||
|
@ -7,6 +7,5 @@ urlpatterns = [
|
||||
path('link/save/', save_shared_link, name='save_shared_link'),
|
||||
path('link/export-excel/', export_shared_link, name='export_shared_link'),
|
||||
path('ajax/private-share-dir/', ajax_private_share_dir, name='ajax_private_share_dir'),
|
||||
path('ajax/get-link-audit-code/', ajax_get_link_audit_code, name='ajax_get_link_audit_code'),
|
||||
path('ajax/get-link-email-audit-code/', ajax_get_link_email_audit_code, name='ajax_get_link_email_audit_code'),
|
||||
]
|
||||
|
@ -344,56 +344,6 @@ def ajax_private_share_dir(request):
|
||||
data = json.dumps({"error": _("Please check the email(s) you entered")})
|
||||
return HttpResponse(data, status=400, content_type=content_type)
|
||||
|
||||
|
||||
def ajax_get_link_audit_code(request):
|
||||
"""
|
||||
Generate a token, and record that token with email in cache, expires in
|
||||
one hour, send token to that email address.
|
||||
|
||||
User provide token and email at share link page, if the token and email
|
||||
are valid, record that email in session.
|
||||
"""
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
|
||||
token = request.POST.get('token')
|
||||
email = request.POST.get('email')
|
||||
if not is_valid_email(email):
|
||||
return HttpResponse(json.dumps({
|
||||
'error': _('Email address is not valid')
|
||||
}), status=400, content_type=content_type)
|
||||
|
||||
dfs = FileShare.objects.get_valid_file_link_by_token(token)
|
||||
ufs = UploadLinkShare.objects.get_valid_upload_link_by_token(token)
|
||||
|
||||
fs = dfs if dfs else ufs
|
||||
if fs is None:
|
||||
return HttpResponse(json.dumps({
|
||||
'error': _('Share link is not found')
|
||||
}), status=400, content_type=content_type)
|
||||
|
||||
cache_key = normalize_cache_key(email, 'share_link_audit_')
|
||||
code = gen_token(max_length=6)
|
||||
cache.set(cache_key, code, SHARE_LINK_AUDIT_CODE_TIMEOUT)
|
||||
|
||||
# send code to user via email
|
||||
subject = _("Verification code for visiting share links")
|
||||
c = {'code': code}
|
||||
|
||||
send_success = send_html_email_with_dj_template(email,
|
||||
subject=subject,
|
||||
dj_template='share/audit_code_email.html',
|
||||
context=c)
|
||||
|
||||
if not send_success:
|
||||
logger.error('Failed to send audit code via email to %s')
|
||||
return HttpResponse(json.dumps({
|
||||
"error": _("Failed to send a verification code, please try again later.")
|
||||
}), status=500, content_type=content_type)
|
||||
|
||||
return HttpResponse(json.dumps({'success': True}), status=200,
|
||||
content_type=content_type)
|
||||
|
||||
|
||||
def ajax_get_link_email_audit_code(request):
|
||||
content_type = 'application/json; charset=utf-8'
|
||||
|
||||
@ -422,7 +372,7 @@ def ajax_get_link_email_audit_code(request):
|
||||
cache.set(cache_key, code, 60 * 60)
|
||||
|
||||
# send code to user via email
|
||||
subject = _("Verification code for visiting share links")
|
||||
subject = _("The verification code")
|
||||
c = {'code': code}
|
||||
|
||||
send_success = send_html_email_with_dj_template(email,
|
||||
|
@ -5,11 +5,13 @@
|
||||
<div style="padding:30px 55px 40px;min-height:300px;border-top:1px solid #efefef;background:transparent url('{{ url_base }}{{media_url}}img/email_bg.jpg') scroll repeat-x center top;">
|
||||
{% block email_con %}{% endblock %}
|
||||
|
||||
<p style="font-size:14px;color:#434144;margin:30px 0;">
|
||||
{% block thanks %}
|
||||
<p style="font-size:14px;color:#434144;margin-top:30px;">
|
||||
{% trans "Thanks for using our site!" %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
<p style="font-size:14px;color:#434144;">
|
||||
<p style="font-size:14px;color:#434144;margin-top:30px;">
|
||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||
</p>
|
||||
</div>
|
||||
|
@ -2,9 +2,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block sub_title %}{% trans "Log In" %} - {% endblock %}
|
||||
|
||||
{% block header_css_class %}hide{% endblock %}
|
||||
|
||||
{% block extra_base_style %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/seafile-ui.css" />
|
||||
{% endblock %}
|
||||
|
Loading…
Reference in New Issue
Block a user