mirror of https://github.com/zulip/zulip.git
Add Find My Team feature.
This commit is contained in:
parent
268770489b
commit
f208813ea3
|
@ -1415,3 +1415,12 @@ label.label-title {
|
|||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#find_my_team .form-control {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#find_my_team .btn {
|
||||
height: 40px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
Hello {{ user_profile.full_name }},
|
||||
|
||||
You can log into Zulip using {{ user_profile.email }} on following organization:
|
||||
|
||||
- {{ user_profile.realm.name }} through {{ user_profile.realm.uri }}
|
||||
|
||||
Cheers,
|
||||
The Zulip Team
|
|
@ -0,0 +1,9 @@
|
|||
<p>Hello {{ user_profile.full_name }},<p>
|
||||
|
||||
<p>You can log into Zulip using {{ user_profile.email }} on following organization:</p>
|
||||
|
||||
<p><a href="{{ user_profile.realm.uri }}">{{ user_profile.realm.name }}</a></p>
|
||||
|
||||
<p>Cheers,
|
||||
<br>
|
||||
The Zulip Team</p>
|
|
@ -0,0 +1,48 @@
|
|||
{% extends "zerver/portico.html" %}
|
||||
|
||||
{% block portico_content %}
|
||||
<div class="app find-team-page">
|
||||
<div class="app-main find-team-page-container">
|
||||
<p class="lead">
|
||||
<h3 class="find-team-page-header">{{ _("Find your team") }}…</h3>
|
||||
</p>
|
||||
{% if emails %}
|
||||
<div id="results">
|
||||
<p>
|
||||
We have checked the following email address(es). Any email address
|
||||
not associated with Zulip was not sent the sign-in information.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
{% for email in emails %}
|
||||
<li>{{ email }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="find-team-form">
|
||||
<p>
|
||||
We will send you an email with the sign-in information against
|
||||
any email address associated with Zulip.
|
||||
</p>
|
||||
<form class="form-inline" id="find_my_team" name="email_form"
|
||||
action="{{ current_url() }}" method="post">
|
||||
{{ csrf_input }}
|
||||
<input type="text" class="form-control required"
|
||||
placeholder="{{ _("Enter email addresses") }}"
|
||||
id="emails" name="emails"/>
|
||||
<button type="submit" class="btn btn-primary">{{ _('Find team') }}</button>
|
||||
<div><i>{{ form.emails.help_text }}</i></div>
|
||||
</form>
|
||||
<div id="errors"></div>
|
||||
{% if form.emails.errors %}
|
||||
{% for error in form.emails.errors %}
|
||||
<div class="alert alert-error">{{ error }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="footer-padder"></div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -84,6 +84,11 @@
|
|||
<li><span class="little-bullet">·</span></li>
|
||||
<li><a href="{{ server_uri }}/create_realm">{{ _('Create new organization') }}</a></li>
|
||||
{% endif %}
|
||||
{% if find_team_link_disabled %}
|
||||
{% else %}
|
||||
<li><span class="little-bullet">·</span></li>
|
||||
<li><a href="{{ server_uri }}/find-my-team">{{ _('Find my team') }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,6 +60,7 @@ def add_settings(request):
|
|||
'github_auth_enabled': github_auth_enabled(realm),
|
||||
'development_environment': settings.DEVELOPMENT,
|
||||
'support_email': settings.ZULIP_ADMINISTRATOR,
|
||||
'find_team_link_disabled': settings.FIND_TEAM_LINK_DISABLED,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.contrib.auth.forms import SetPasswordForm, AuthenticationForm, \
|
|||
PasswordResetForm
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.validators import validate_email
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils.translation import ugettext as _
|
||||
from jinja2 import Markup as mark_safe
|
||||
|
@ -201,3 +202,31 @@ Please contact %s to reactivate this group.""" % (
|
|||
(user_profile.email, get_subdomain(self.request)))
|
||||
raise ValidationError(mark_safe(WRONG_SUBDOMAIN_ERROR))
|
||||
return email
|
||||
|
||||
class MultiEmailField(forms.Field):
|
||||
def to_python(self, emails):
|
||||
# type: (Text) -> List[Text]
|
||||
"""Normalize data to a list of strings."""
|
||||
if not emails:
|
||||
return []
|
||||
|
||||
return [email.strip() for email in emails.split(',')]
|
||||
|
||||
def validate(self, emails):
|
||||
# type: (List[Text]) -> None
|
||||
"""Check if value consists only of valid emails."""
|
||||
super(MultiEmailField, self).validate(emails)
|
||||
for email in emails:
|
||||
validate_email(email)
|
||||
|
||||
class FindMyTeamForm(forms.Form):
|
||||
emails = MultiEmailField(
|
||||
help_text="Add up to 10 comma-separated email addresses.")
|
||||
|
||||
def clean_emails(self):
|
||||
# type: () -> List[Text]
|
||||
emails = self.cleaned_data['emails']
|
||||
if len(emails) > 10:
|
||||
raise forms.ValidationError("Please enter at most 10 emails.")
|
||||
|
||||
return emails
|
||||
|
|
|
@ -154,6 +154,7 @@ class TemplateTestCase(ZulipTestCase):
|
|||
full_name=get_form_value('John Doe'),
|
||||
terms=get_form_value(True),
|
||||
email=get_form_value(email),
|
||||
emails=get_form_value(email),
|
||||
),
|
||||
current_url=lambda: 'www.zulip.com',
|
||||
hubot_lozenges_dict={},
|
||||
|
|
|
@ -2304,3 +2304,47 @@ class TestLoginPage(ZulipTestCase):
|
|||
# type: () -> None
|
||||
result = self.client_get("/login/?subdomain=1")
|
||||
self.assertIn(WRONG_SUBDOMAIN_ERROR, result.content.decode('utf8'))
|
||||
|
||||
class FindMyTeamTestCase(ZulipTestCase):
|
||||
def test_template(self):
|
||||
# type: () -> None
|
||||
result = self.client_get('/find-my-team/')
|
||||
self.assertIn("Find your team", result.content.decode('utf8'))
|
||||
|
||||
def test_result(self):
|
||||
# type: () -> None
|
||||
url = '/find-my-team/?emails=iago@zulip.com,cordelia@zulip.com'
|
||||
result = self.client_get(url)
|
||||
content = result.content.decode('utf8')
|
||||
self.assertIn("We have checked the following email address(es)", content)
|
||||
self.assertIn("iago@zulip.com", content)
|
||||
self.assertIn("cordelia@zulip.com", content)
|
||||
|
||||
def test_find_team_zero_emails(self):
|
||||
# type: () -> None
|
||||
data = {'emails': ''}
|
||||
result = self.client_post('/find-my-team/', data)
|
||||
self.assertIn('This field is required', result.content.decode('utf8'))
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
def test_find_team_one_email(self):
|
||||
# type: () -> None
|
||||
data = {'emails': 'hamlet@zulip.com'}
|
||||
result = self.client_post('/find-my-team/', data)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result.url, '/find-my-team/?emails=hamlet%40zulip.com')
|
||||
|
||||
def test_find_team_multiple_emails(self):
|
||||
# type: () -> None
|
||||
data = {'emails': 'hamlet@zulip.com,iago@zulip.com'}
|
||||
result = self.client_post('/find-my-team/', data)
|
||||
self.assertEqual(result.status_code, 302)
|
||||
expected = '/find-my-team/?emails=hamlet%40zulip.com%2Ciago%40zulip.com'
|
||||
self.assertEqual(result.url, expected)
|
||||
|
||||
def test_find_team_more_than_ten_emails(self):
|
||||
# type: () -> None
|
||||
data = {'emails': ','.join(['hamlet-{}@zulip.com'.format(i) for i in range(11)])}
|
||||
result = self.client_post('/find-my-team/', data)
|
||||
self.assertEqual(result.status_code, 200)
|
||||
self.assertIn("Please enter at most 10", result.content.decode('utf8'))
|
||||
|
|
|
@ -12,12 +12,17 @@ from django.shortcuts import redirect
|
|||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core import signing
|
||||
from typing import Text
|
||||
from django.template import loader
|
||||
from django.core.validators import validate_email
|
||||
from django import forms
|
||||
from django.core.mail import send_mail
|
||||
from six.moves import urllib
|
||||
from typing import Any, Dict, Optional, Tuple, Text
|
||||
|
||||
from confirmation.models import Confirmation
|
||||
from zerver.forms import HomepageForm, OurAuthenticationForm, WRONG_SUBDOMAIN_ERROR
|
||||
from zerver.forms import HomepageForm, OurAuthenticationForm, \
|
||||
WRONG_SUBDOMAIN_ERROR, FindMyTeamForm
|
||||
|
||||
from zerver.lib.request import REQ, has_request_variables, JsonableError
|
||||
from zerver.lib.response import json_success, json_error
|
||||
from zerver.lib.utils import get_subdomain
|
||||
|
@ -26,6 +31,7 @@ from zerver.views import create_preregistration_user, get_realm_from_request, \
|
|||
redirect_and_log_into_subdomain
|
||||
from zproject.backends import password_auth_enabled, dev_auth_enabled, google_auth_enabled
|
||||
from zproject.jinja2 import render_to_response
|
||||
from zerver.lib.notifications import send_future_email
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
|
@ -34,6 +40,61 @@ import logging
|
|||
import requests
|
||||
import time
|
||||
import ujson
|
||||
import datetime
|
||||
from typing import Text
|
||||
|
||||
try:
|
||||
import mailer
|
||||
send_mail = mailer.send_mail
|
||||
except ImportError:
|
||||
# no mailer app present, stick with default
|
||||
pass
|
||||
|
||||
def send_find_my_team_emails(user_profile):
|
||||
# type: (UserProfile) -> None
|
||||
text_template = 'zerver/emails/find_team/find_team_email.txt'
|
||||
html_template = 'zerver/emails/find_team/find_team_email_html.txt'
|
||||
context = {'user_profile': user_profile}
|
||||
text_content = loader.render_to_string(text_template, context)
|
||||
html_content = loader.render_to_string(html_template, context)
|
||||
sender = settings.NOREPLY_EMAIL_ADDRESS
|
||||
recipients = [user_profile.email]
|
||||
subject = "Your Zulip Team"
|
||||
|
||||
send_mail(subject, text_content, sender, recipients, html_message=html_content)
|
||||
|
||||
def find_my_team(request):
|
||||
# type: (HttpRequest) -> HttpResponse
|
||||
url = reverse('find-my-team')
|
||||
|
||||
emails = [] # type: List[Text]
|
||||
if request.method == 'POST':
|
||||
form = FindMyTeamForm(request.POST)
|
||||
if form.is_valid():
|
||||
emails = form.cleaned_data['emails']
|
||||
for user_profile in UserProfile.objects.filter(email__in=emails):
|
||||
send_find_my_team_emails(user_profile)
|
||||
|
||||
# Note: Show all the emails in the result otherwise this
|
||||
# feature can be used to ascertain which email addresses
|
||||
# are associated with Zulip.
|
||||
data = urllib.parse.urlencode({'emails': ','.join(emails)})
|
||||
return redirect(url + "?" + data)
|
||||
else:
|
||||
form = FindMyTeamForm()
|
||||
result = request.GET.get('emails')
|
||||
if result:
|
||||
for email in result.split(','):
|
||||
try:
|
||||
validate_email(email)
|
||||
emails.append(email)
|
||||
except forms.ValidationError:
|
||||
pass
|
||||
|
||||
return render_to_response('zerver/find_my_team.html',
|
||||
{'form': form, 'current_url': lambda: url,
|
||||
'emails': emails},
|
||||
request=request)
|
||||
|
||||
def maybe_send_to_registration(request, email, full_name=''):
|
||||
# type: (HttpRequest, Text, Text) -> HttpResponse
|
||||
|
|
|
@ -157,6 +157,7 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
|
|||
'REGISTER_LINK_DISABLED': False,
|
||||
'LOGIN_LINK_DISABLED': False,
|
||||
'ABOUT_LINK_DISABLED': False,
|
||||
'FIND_TEAM_LINK_DISABLED': True,
|
||||
'CUSTOM_LOGO_URL': None,
|
||||
'VERBOSE_SUPPORT_OFFERS': False,
|
||||
'STATSD_HOST': '',
|
||||
|
|
|
@ -131,6 +131,7 @@ i18n_urls = [
|
|||
name='landing-page'),
|
||||
url(r'^new-user/$', RedirectView.as_view(url='/hello', permanent=True)),
|
||||
url(r'^features/$', TemplateView.as_view(template_name='zerver/features.html')),
|
||||
url(r'^find-my-team/$', zerver.views.auth.find_my_team, name='find-my-team'),
|
||||
]
|
||||
|
||||
# If a Terms of Service is supplied, add that route
|
||||
|
|
Loading…
Reference in New Issue