Browse Source

Improved layout & style, fixed hamburger menu in login page.

pull/193/head
Pēteris Caune 6 years ago
parent
commit
a58ce791c0
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
8 changed files with 204 additions and 102 deletions
  1. +3
    -1
      CHANGELOG.md
  2. +32
    -2
      hc/accounts/forms.py
  3. +3
    -4
      hc/accounts/tests/test_login.py
  4. +18
    -24
      hc/accounts/views.py
  5. +1
    -1
      hc/urls.py
  6. +74
    -0
      static/css/login.css
  7. +68
    -62
      templates/accounts/login.html
  8. +5
    -8
      templates/base.html

+ 3
- 1
CHANGELOG.md View File

@ -6,9 +6,11 @@ All notable changes to this project will be documented in this file.
### Improvements ### Improvements
- Content updates in the "Welcome" page. - Content updates in the "Welcome" page.
- Added "Docs > Third-Party Resources" page. - Added "Docs > Third-Party Resources" page.
- Improved layout and styling in "Login" page.
### Bug Fixes ### Bug Fixes
- Timezones were missing in the "Change Schedule" dialog, fixed
- Timezones were missing in the "Change Schedule" dialog, fixed.
- Fix hamburger menu button in "Login" page.
## 1.1.0 - 2018-08-20 ## 1.1.0 - 2018-08-20


+ 32
- 2
hc/accounts/forms.py View File

@ -1,5 +1,7 @@
from datetime import timedelta as td from datetime import timedelta as td
from django import forms from django import forms
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -10,9 +12,37 @@ class LowercaseEmailField(forms.EmailField):
return value.lower() return value.lower()
class EmailForm(forms.Form):
email = LowercaseEmailField()
def clean_email(self):
v = self.cleaned_data["email"]
# If registration is not open then validate if an user
# account with this address exists-
if not settings.REGISTRATION_OPEN:
if not User.objects.filter(email=v).exists():
raise forms.ValidationError("Incorrect email address.")
return v
class EmailPasswordForm(forms.Form): class EmailPasswordForm(forms.Form):
identity = LowercaseEmailField()
password = forms.CharField(required=False)
email = LowercaseEmailField()
password = forms.CharField()
def clean(self):
username = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if username and password:
self.user = authenticate(username=username, password=password)
if self.user is None:
raise forms.ValidationError("Incorrect email or password")
if not self.user.is_active:
raise forms.ValidationError("Account is inactive")
return self.cleaned_data
class ReportSettingsForm(forms.Form): class ReportSettingsForm(forms.Form):


+ 3
- 4
hc/accounts/tests/test_login.py View File

@ -10,7 +10,7 @@ from django.conf import settings
class LoginTestCase(TestCase): class LoginTestCase(TestCase):
def test_it_sends_link(self): def test_it_sends_link(self):
form = {"identity": "[email protected]"}
form = {"email": "[email protected]"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 302 assert r.status_code == 302
@ -32,10 +32,9 @@ class LoginTestCase(TestCase):
self.client.get("/accounts/login/") self.client.get("/accounts/login/")
assert "bad_link" not in self.client.session assert "bad_link" not in self.client.session
@override_settings(REGISTRATION_OPEN=False) @override_settings(REGISTRATION_OPEN=False)
def test_it_obeys_registration_open(self): def test_it_obeys_registration_open(self):
form = {"identity": "[email protected]"}
form = {"email": "[email protected]"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 200 assert r.status_code == 200
@ -45,7 +44,7 @@ class LoginTestCase(TestCase):
alice = User(username="alice", email="[email protected]") alice = User(username="alice", email="[email protected]")
alice.save() alice.save()
form = {"identity": "[email protected]"}
form = {"email": "[email protected]"}
r = self.client.post("/accounts/login/", form) r = self.client.post("/accounts/login/", form)
assert r.status_code == 302 assert r.status_code == 302


+ 18
- 24
hc/accounts/views.py View File

@ -17,7 +17,7 @@ from django.views.decorators.http import require_POST
from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm, from hc.accounts.forms import (ChangeEmailForm, EmailPasswordForm,
InviteTeamMemberForm, RemoveTeamMemberForm, InviteTeamMemberForm, RemoveTeamMemberForm,
ReportSettingsForm, SetPasswordForm, ReportSettingsForm, SetPasswordForm,
TeamNameForm)
TeamNameForm, EmailForm)
from hc.accounts.models import Profile, Member from hc.accounts.models import Profile, Member
from hc.api.models import Channel, Check from hc.api.models import Channel, Check
from hc.lib.badges import get_badge_url from hc.lib.badges import get_badge_url
@ -57,44 +57,38 @@ def _ensure_own_team(request):
request.profile.save() request.profile.save()
def login(request, show_password=False):
bad_credentials = False
def login(request):
form = EmailPasswordForm()
magic_form = EmailForm()
if request.method == 'POST': if request.method == 'POST':
form = EmailPasswordForm(request.POST)
if form.is_valid():
email = form.cleaned_data["identity"]
password = form.cleaned_data["password"]
if len(password):
user = authenticate(username=email, password=password)
if user is not None and user.is_active:
auth_login(request, user)
return redirect("hc-checks")
bad_credentials = True
show_password = True
else:
if request.POST.get("action") == "login":
form = EmailPasswordForm(request.POST)
if form.is_valid():
auth_login(request, form.user)
return redirect("hc-checks")
else:
magic_form = EmailForm(request.POST)
if magic_form.is_valid():
email = magic_form.cleaned_data["email"]
user = None user = None
try: try:
user = User.objects.get(email=email) user = User.objects.get(email=email)
except User.DoesNotExist: except User.DoesNotExist:
if settings.REGISTRATION_OPEN: if settings.REGISTRATION_OPEN:
user = _make_user(email) user = _make_user(email)
else:
bad_credentials = True
if user: if user:
profile = Profile.objects.for_user(user) profile = Profile.objects.for_user(user)
profile.send_instant_login_link() profile.send_instant_login_link()
return redirect("hc-login-link-sent") return redirect("hc-login-link-sent")
else:
form = EmailPasswordForm()
bad_link = request.session.pop("bad_link", None) bad_link = request.session.pop("bad_link", None)
ctx = { ctx = {
"page": "login",
"form": form, "form": form,
"bad_credentials": bad_credentials,
"bad_link": bad_link,
"show_password": show_password
"magic_form": magic_form,
"bad_link": bad_link
} }
return render(request, "accounts/login.html", ctx) return render(request, "accounts/login.html", ctx)


+ 1
- 1
hc/urls.py View File

@ -4,7 +4,7 @@ from django.urls import include, path
from hc.accounts.views import login as hc_login from hc.accounts.views import login as hc_login
urlpatterns = [ urlpatterns = [
path('admin/login/', hc_login, {"show_password": True}),
path('admin/login/', hc_login),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('accounts/', include('hc.accounts.urls')), path('accounts/', include('hc.accounts.urls')),
path('', include('hc.api.urls')), path('', include('hc.api.urls')),


+ 74
- 0
static/css/login.css View File

@ -0,0 +1,74 @@
.page-login h1 {
text-align: center;
margin: 40px 0;
}
.page-login .alert {
margin-bottom: 40px;
}
.page-login form p {
margin-bottom: 20px;
text-align: center;
}
.page-login form input {
margin-bottom: 20px;
}
@media (min-width: 768px) {
#magic-link-form {
margin-right: 50px;
}
#login-form {
margin-left: 50px;
}
}
#link-instruction {
color: #999;
font-style: italic;
padding: 12px 16px;
}
#login-sep {
background:#ddd;
position: absolute;
top: 10%;
right: -1px;
height: 80%;
width: 1px;
}
#login-sep div {
position: absolute;
top: 40%;
width: 40px;
left: -20px;
text-align: center;
background: #fff;
font-style: italic;
color: #666;
font-size: 12px;
padding: 8px 0;
}
#xs-login-sep {
text-align: center;
margin: 40px;
font-style: italic;
color: #666;
font-size: 12px;
height: 1px;
background: #ddd;
}
#xs-login-sep div {
position: relative;
margin: 0 auto;
width: 30px;
top: -9px;
background: #fff;
}

+ 68
- 62
templates/accounts/login.html View File

@ -3,77 +3,82 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div class="hc-dialog">
{% if bad_link %}
<h1>Incorrect Login Link</h1>
<div class="dialog-body">
<p>The login link you just used is either incorrect or expired.</p>
<p>Please use the form below to request a fresh login link:</p>
</div>
{% else %}
<h1>{% site_name %}</h1>
<div class="dialog-body">
<p>
{% if show_password %}
Please enter your email address and password.
{% else %}
Please enter your email address.
Next, we'll send you an email with log-in instructions!
{% endif %}
</p>
</div>
{% endif %}
<div class="col-xs-10 col-xs-offset-1 col-sm-12 col-sm-offset-0 col-lg-8 col-lg-offset-2">
{% if bad_credentials %}
<p class="alert alert-danger">Incorrect email or password.</p>
{% endif %}
<h1>Sign In to {% site_name %}</h1>
{% if bad_link %}
<div class="alert alert-warning">
<p>The login link you just used is either incorrect or expired.</p>
<p>Please use the form below to request a fresh login link.</p>
</div>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="row">
<div class="col-sm-6">
<form id="magic-link-form" method="post">
{% csrf_token %}
<div class="form-group">
<div class="input-group input-group-lg">
<div class="input-group-addon">
<span class="icon-mail"></span>
</div>
<input
type="text"
class="form-control"
name="identity"
value="{{ form.identity.value|default:"" }}"
placeholder="[email protected]">
</div>
</div>
{% if magic_form.email.errors %}
<p class="text-danger">Incorrect email address.</p>
{% else %}
<p>Enter your <strong>email address</strong>.</p>
{% endif %}
{% if not show_password %}
<div class="checkbox" id="password-toggle">
<label>
<input type="checkbox"> I want to use a password
</label>
</div>
{% endif %}
<input
type="text"
class="form-control input-lg"
name="email"
value="{{ magic_form.email.value|default:"" }}"
placeholder="[email protected]">
<p id="link-instruction">
We will email you a magic sign in link.
</p>
<button type="submit" class="btn btn-lg btn-primary btn-block">
Email Me a Link
</button>
</form>
<div id="login-sep" class="hidden-xs"><div>or</div></div>
</div>
<div id="password-block" class="form-group {% if not show_password %} hide {% endif %}">
<div class="input-group input-group-lg">
<div class="input-group-addon">
<span class="icon-dots"></span>
</div>
<input
type="password"
class="form-control"
name="password"
placeholder="password">
</div>
<div class="col-xs-12 visible-xs-block">
<div id="xs-login-sep">
<div>or</div>
</div> </div>
</div>
<div class="col-sm-6">
<form id="login-form" method="post">
{% csrf_token %}
<input type="hidden" name="action" value="login" />
{% if form.non_field_errors %}
<p class="text-danger">Incorrect email or password.</p>
{% else %}
<p>
Enter your <strong>email address</strong> and <strong>password</strong>.
</p>
{% endif %}
<input
type="text"
class="form-control input-lg"
name="email"
value="{{ form.email.value|default:"" }}"
placeholder="[email protected]">
<div class="clearfix">
<button type="submit" class="btn btn-lg btn-primary pull-right">
Log In
<input
type="password"
class="form-control input-lg"
name="password"
placeholder="your password">
<button type="submit" class="btn btn-lg btn-primary btn-block">
Sign In
</button> </button>
</div>
</form>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -82,6 +87,7 @@
{% block scripts %} {% block scripts %}
{% compress js %} {% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script> <script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/login.js' %}"></script> <script src="{% static 'js/login.js' %}"></script>
{% endcompress %} {% endcompress %}
{% endblock %} {% endblock %}

+ 5
- 8
templates/base.html View File

@ -42,6 +42,7 @@
<link rel="stylesheet" href="{% static 'css/checkbox.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/checkbox.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/radio.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css"> <link rel="stylesheet" href="{% static 'css/billing.css' %}" type="text/css">
<link rel="stylesheet" href="{% static 'css/login.css' %}" type="text/css">
{% endcompress %} {% endcompress %}
</head> </head>
<body class="page-{{ page }}"> <body class="page-{{ page }}">
@ -109,14 +110,6 @@
</ul> </ul>
{% if request.user.is_authenticated %}
{% else %}
<ul class="nav navbar-nav navbar-right">
<li><a href="{% url 'hc-login' %}">Log In</a></li>
</ul>
{% endif %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="dropdown"> <li class="dropdown">
@ -148,6 +141,10 @@
</ul> </ul>
</li> </li>
</ul> </ul>
{% elif page != "login" %}
<ul class="nav navbar-nav navbar-right">
<li><a href="{% url 'hc-login' %}">Log In</a></li>
</ul>
{% endif %} {% endif %}
</div> </div>


Loading…
Cancel
Save