add login view and logic to display it

This commit is contained in:
Laura Klünder 2017-12-07 13:12:56 +01:00
parent cfd59cfec3
commit eb75c9b475
6 changed files with 163 additions and 13 deletions

View file

@ -25,6 +25,9 @@ header #user small {
margin-top: -3px;
color: #606c76;
}
header #user small:empty {
display:none;
}
input {
font-family: inherit;
@ -45,12 +48,26 @@ input {
main {
flex-grow: 1;
border: 0 solid #CCCCCC;
border-top-width: 1px;
padding: 10px;
padding-top: 20px;
}
main.map {
/*noinspection CssUnknownTarget*/
background: url('../../img/loader.gif') no-repeat center;
position: relative;
padding: 0;
border-top-width: 0;
}
main.account form {
max-width: 400px;
}
#modal-content :last-child {
margin: 0;
}
#map {
position: absolute;
z-index: 1;
@ -165,6 +182,9 @@ main:not([data-view=route-result]) #route-result-buttons {
h2 {
font-size: 2.5rem;
margin: 0 0 1rem;
}
.details-head h2 {
margin: 0;
}
@ -453,7 +473,7 @@ main:not([data-view=route-result]) #route-dots {
.buttons > *:hover, .buttons > *:active {
background-color: #eeeeee;
}
button, .button {
main.map button, main.map .button {
font-size: 1.3rem;
line-height: 1.3;
height: 3.3rem;
@ -588,6 +608,7 @@ main:not([data-view=route-result]) #route-summary {
top: 8px;
right: 8px;
font-size: 30px;
padding: 0;
color: #b2b2b2;
}
#close-modal:hover {
@ -611,3 +632,41 @@ main:not([data-view=route-result]) #route-summary {
main > .share-ui {
display: none;
}
ul.errorlist {
margin-bottom:0.5rem;
color:red;
list-style-type:none;
}
ul.messages {
list-style-type:none;
}
ul.messages li {
padding:1.0rem;
border-radius: .4rem;
border: 0.1rem solid #666666;
background-color:#EEEEEE;
}
ul.messages li.info {
color: #006688;
border-color: #006688;
background-color:#DDE4FF;
}
ul.messages li.success {
color: #338800;
border-color: #339900;
background-color:#E4FFDD;
}
ul.messages li.warning {
color: #CC6600;
border-color: #FF9900;
background-color:#FFFFDD;
}
ul.messages li.error {
color: #CC0000;
border-color: #CC0000;
background-color:#FFEEEE;
}

View file

@ -76,7 +76,10 @@ c3nav = {
$('#route-search-buttons').find('.close').on('click', c3nav._route_buttons_close_click);
$('#map').on('click', '.location-popup .button-clear', c3nav._popup_button_click);
$('#modal').on('click', c3nav._modal_click);
$('#modal').on('click', c3nav._modal_click)
.on('click', 'a', c3nav._modal_link_click)
.on('submit', 'form', c3nav._modal_submit);
$('header #user').on('click', c3nav._modal_link_click);
window.onpopstate = c3nav._onpopstate;
},
@ -713,20 +716,44 @@ c3nav = {
open_modal: function (content) {
var $modal = $('#modal');
$modal.toggleClass('loading', !content)
.find('#modal-content')
.html('<button class="button-clear material-icons" id="close-modal">clear</button>')
.append(content || '');
c3nav._set_modal_content(content);
if (!$modal.is('.show')) {
c3nav._push_state({modal: true, sidebar: true});
$modal.addClass('show');
}
},
_set_modal_content: function(content) {
$('#modal').toggleClass('loading', !content)
.find('#modal-content')
.html('<button class="button-clear material-icons" id="close-modal">clear</button>')
.append(content || '');
},
_modal_click: function(e) {
if (e.target.id === 'modal' || e.target.id === 'close-modal') {
history.back();
}
},
_modal_link_click: function(e) {
e.preventDefault();
e.stopPropagation();
c3nav.open_modal();
$.get($(this).attr('href'), c3nav._modal_loaded).fail(c3nav._modal_error);
},
_modal_submit: function(e) {
e.preventDefault();
$.post($(this).attr('action'), $(this).serialize(), c3nav._modal_loaded).fail(c3nav._modal_error);
},
_modal_loaded: function(data) {
if (data.startsWith('{')) {
c3nav._set_user_data(JSON.parse(data));
history.back();
return;
}
c3nav._set_modal_content($('<div>'+data+'</div>').find('main').html());
},
_modal_error: function(data) {
$('#modal').removeClass('loading').find('#modal-content').html('<h3>Error '+data.status+'</h3>');
},
// map
init_map: function () {
@ -929,8 +956,13 @@ c3nav = {
window.setTimeout(c3nav.refresh_tile_access, 16000);
},
refresh_tile_access: function () {
$.ajax('/api/users/current/');
$.get('/api/users/current/', c3nav._set_user_data);
c3nav.schedule_refresh_tile_access();
},
_set_user_data: function (data) {
var $user = $('header #user');
$user.find('span').text(data.title);
$user.find('small').text(data.subtitle || '');
}
};
$(document).ready(c3nav.init);

View file

@ -20,9 +20,9 @@
{% if not embed %}
<header>
<h1>c3nav</h1>
<a href="#" id="user">
<a href="/account/" id="user">
<span>{{ user_data.title }}</span>
{% if user_data.subtitle %}<small>{{ user_data.subtitle }}</small>{% endif %}
<small>{% if user_data.subtitle %}{{ user_data.subtitle }}{% endif %}</small>
</a>
</header>
{% endif %}

View file

@ -0,0 +1,14 @@
{% extends 'site/base.html' %}
{% load i18n %}
{% block content %}
<main class="account">
<h2>{% trans 'Log in' %}</h2>
<form method="post" action="{{ request.path_info }}?{{ request.GET.urlencode }}">
{% csrf_token %}
{{ form }}
<button type="submit">{% trans 'Log in' %}</button>
</form>
</main>
{% endblock %}

View file

@ -1,6 +1,6 @@
from django.conf.urls import url
from c3nav.site.views import map_index, qr_code
from c3nav.site.views import account_view, login_view, logout_view, map_index, qr_code
slug = r'(?P<slug>[a-z0-9-_.:]+)'
slug2 = r'(?P<slug2>[a-z0-9-_.:]+)'
@ -13,6 +13,9 @@ urlpatterns = [
url(r'^%s(?P<mode>[od])/%s/%s$' % (embed, slug, pos), map_index, name='site.index'),
url(r'^%sr/%s/%s/%s%s$' % (embed, slug, slug2, details, pos), map_index, name='site.index'),
url(r'^%s(?P<mode>r)/%s$' % (embed, pos), map_index, name='site.index'),
url(r'^%s$' % pos, map_index, name='site.index'),
url(r'^qr/(?P<path>.*)$', qr_code, name='site.qr'),
url(r'^%s$' % pos, map_index, name='site.index')
url(r'^login$', login_view, name='site.login'),
url(r'^logout$', logout_view, name='site.logout'),
url(r'^account/$', account_view, name='site.account'),
]

View file

@ -3,10 +3,14 @@ from typing import Optional
import qrcode
from django.conf import settings
from django.contrib.auth import login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import render
from django.views.decorators.cache import cache_control
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.decorators.cache import cache_control, never_cache
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.http import etag
@ -111,3 +115,41 @@ def qr_code(request, path):
response = HttpResponse(content_type='image/png')
qr.make_image().save(response, 'PNG')
return response
def close_response(request):
ajax = request.is_ajax() or 'ajax' in request.GET
if ajax:
return HttpResponse(json.dumps(get_user_data(request), cls=DjangoJSONEncoder).encode(),
content_type='text/plain')
redirect_path = request.GET['next'] if request.GET.get('next', '').startswith('/') else reverse('site.index')
return redirect(redirect_path)
@never_cache
def login_view(request):
if request.user.is_authenticated:
return close_response(request)
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
login(request, form.user_cache)
return close_response(request)
else:
form = AuthenticationForm(request)
return render(request, 'site/login.html', {'form': form})
@never_cache
def logout_view(request):
logout(request)
return close_response(request)
@never_cache
@login_required(login_url='site.login')
def account_view(request):
# todo: show account info here
pass