add login
This commit is contained in:
parent
b26ed184a1
commit
c013bc8d43
|
@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/5.0/ref/settings/
|
|||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from django.contrib.messages import constants as messages
|
||||
from decouple import config, Csv
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
@ -40,6 +42,7 @@ INSTALLED_APPS = [
|
|||
'django_extensions',
|
||||
'django_bootstrap5',
|
||||
"rest_framework",
|
||||
"login",
|
||||
"user",
|
||||
"device",
|
||||
"snapshot",
|
||||
|
@ -47,7 +50,7 @@ INSTALLED_APPS = [
|
|||
"tag",
|
||||
"lot",
|
||||
"documents",
|
||||
"inventory",
|
||||
"dashboard",
|
||||
]
|
||||
|
||||
|
||||
|
@ -55,6 +58,7 @@ MIDDLEWARE = [
|
|||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
|
@ -128,6 +132,11 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
STATIC_ROOT = config('STATIC_ROOT', default="static")
|
||||
MEDIA_ROOT = config('MEDIA_ROOT', default="upload")
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
@ -135,3 +144,27 @@ STATIC_URL = "static/"
|
|||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
AUTH_USER_MODEL = 'user.User'
|
||||
|
||||
MESSAGE_TAGS = {
|
||||
messages.DEBUG: 'alert-secondary',
|
||||
messages.INFO: 'alert-info',
|
||||
messages.SUCCESS: 'alert-success',
|
||||
messages.WARNING: 'alert-warning',
|
||||
messages.ERROR: 'alert-danger',
|
||||
}
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
LOGIN_REDIRECT_URL = 'dashboard:dashboard'
|
||||
LOGOUT_REDIRECT_URL = '/'
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {"level": "DEBUG", "class": "logging.StreamHandler"},
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["console"],
|
||||
"level": "DEBUG",
|
||||
}
|
||||
}
|
||||
|
|
0
login/__init__.py
Normal file
0
login/__init__.py
Normal file
3
login/admin.py
Normal file
3
login/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
6
login/apps.py
Normal file
6
login/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LoginConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "login"
|
0
login/migrations/__init__.py
Normal file
0
login/migrations/__init__.py
Normal file
3
login/models.py
Normal file
3
login/models.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
19
login/templates/2fadmin.html
Normal file
19
login/templates/2fadmin.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "auth/login_base.html" %}
|
||||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block login_content %}
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Two-factor Authentication' %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<div>
|
||||
<span>{% trans "We have sent you an email with a link that you have to click to log in." %}</span>
|
||||
</div>
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!--/.well-->
|
||||
{% endblock %}
|
26
login/templates/2fadmin_email.html
Normal file
26
login/templates/2fadmin_email.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
<p>
|
||||
{% blocktrans %}You're receiving this email because you tried to access {{ site_name }}.{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Please go to the following page" %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% block reset_link %}
|
||||
<a href="{{ protocol }}://{{ domain }}{% url 'idhub:admin_2fauth' admin2fauth=token %}">
|
||||
{{ protocol }}://{{ domain }}{% url 'idhub:admin_2fauth' admin2fauth=token %}
|
||||
</a>
|
||||
{% endblock %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Thanks for using our site!" %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% endautoescape %}
|
14
login/templates/2fadmin_email.txt
Normal file
14
login/templates/2fadmin_email.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}You're receiving this email because you tried to access {{ site_name }}.{% endblocktrans %}
|
||||
|
||||
{% trans "Please go to the following page" %}
|
||||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url 'idhub:admin_2fauth' admin2fauth=token %}
|
||||
{% endblock %}
|
||||
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
|
||||
|
||||
{% trans "Thanks for using our site!" %}
|
||||
|
||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||
|
||||
{% endautoescape %}
|
3
login/templates/2fadmin_email_subject.txt
Normal file
3
login/templates/2fadmin_email_subject.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}Authentication in {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
45
login/templates/login.html
Normal file
45
login/templates/login.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends "login_base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block login_content %}
|
||||
<form action="{% url 'login:login' %}" method="post" class="row g-3 needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
<div class="col-12">
|
||||
<input type="email" name="username" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="yourEmail" required
|
||||
autofocus placeholder="{{ form.username.label }}"
|
||||
{% if form.username.value %}value="{{ form.username.value }}" {% endif %}>
|
||||
<div class="invalid-feedback">Please enter your email.</div>
|
||||
{% if form.username.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.username.errors|striptags }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<input type="password" name="password" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="id_password"
|
||||
placeholder="{{ form.password.label }}" required>
|
||||
{% if form.password.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.password.errors|striptags }}
|
||||
</p>
|
||||
{% endif %}
|
||||
<i class="input-group-text bi bi-eye" id="togglePassword" style="cursor: pointer">
|
||||
</i>
|
||||
</div>
|
||||
<div class="invalid-feedback">Please enter your password!</div>
|
||||
</div>
|
||||
|
||||
<input name="next" type="hidden" value="{{ success_url }}">
|
||||
|
||||
<div class="col-12">
|
||||
<button class="btn btn-primary w-100" type="submit">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="login-footer" class="mt-3">
|
||||
<a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
|
||||
</div>
|
||||
{% endblock %}
|
52
login/templates/login2.html
Normal file
52
login/templates/login2.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends "login_base.html" %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block login_content %}
|
||||
<form action="{% url 'login:login' %}" role="form" method="post">
|
||||
{% csrf_token %}
|
||||
<div id="div_id_username"
|
||||
class="clearfix control-group {% if form.username.errors %}error{% endif %}">
|
||||
<div class="form-group">
|
||||
<input type="text" name="username" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="id_username" required
|
||||
autofocus placeholder="{{ form.username.label }}"
|
||||
{% if form.username.value %}value="{{ form.username.value }}" {% endif %}>
|
||||
{% if form.username.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.username.errors|striptags }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="div_id_password"
|
||||
class="clearfix control-group {% if form.password.errors %}error{% endif %}">
|
||||
<div class="form-group">
|
||||
<input type="password" name="password" maxlength="100" autocapitalize="off"
|
||||
autocorrect="off" class="form-control textinput textInput" id="id_password"
|
||||
placeholder="{{ form.password.label }}" required>
|
||||
{% if form.password.errors %}
|
||||
<p class="text-error">
|
||||
{{ form.password.errors|striptags }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="well well-small text-error" style="border: none">{{ error }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input name="next" type="hidden" value="{{ success_url }}">
|
||||
|
||||
<div class="form-actions-no-box">
|
||||
<input type="submit" name="submit" value="{% trans 'Log in' %}"
|
||||
class="btn btn-primary form-control" id="submit-id-submit">
|
||||
</div>
|
||||
</form>
|
||||
<div id="login-footer" class="mt-3">
|
||||
<a href="{% url 'login:password_reset' %}" data-toggle="modal" data-target="#forgotPasswordModal">{% trans "Forgot your password? Click here to recover" %}</a>
|
||||
</div>
|
||||
{% endblock %}
|
179
login/templates/login3.html
Normal file
179
login/templates/login3.html
Normal file
|
@ -0,0 +1,179 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
|
||||
<title>Login - Usody</title>
|
||||
<meta content="" name="description">
|
||||
<meta content="" name="keywords">
|
||||
|
||||
<!-- Favicons -->
|
||||
<link href="/static/img/favicon.png" rel="icon">
|
||||
<link href="/static/img/apple-touch-icon.png" rel="apple-touch-icon">
|
||||
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.gstatic.com" rel="preconnect">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
|
||||
|
||||
<!-- JS Files -->
|
||||
<script src="/static/js/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@5.0.3" type="text/javascript"></script>
|
||||
|
||||
|
||||
|
||||
<!-- Vendor CSS Files -->
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/vendor/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@5.0.3/dist/style.css" rel="stylesheet" type="text/css">
|
||||
|
||||
|
||||
<!-- Template Main CSS File -->
|
||||
<link href="/static/css/style.css" rel="stylesheet">
|
||||
<link href="/static/css/devicehub.css" rel="stylesheet">
|
||||
|
||||
<!-- =======================================================
|
||||
* Template Name: NiceAdmin - v2.2.0
|
||||
* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/
|
||||
* Author: BootstrapMade.com
|
||||
* License: https://bootstrapmade.com/license/
|
||||
======================================================== -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<main>
|
||||
<div class="container">
|
||||
|
||||
<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center py-4">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-4 col-md-6 d-flex flex-column align-items-center justify-content-center">
|
||||
|
||||
<div class="d-flex justify-content-center py-4">
|
||||
<a href="/login/" class="d-flex align-items-center w-auto">
|
||||
<img src="/static/img/usody_logo_transparent_noicon-y-purple-120x41.png" alt="">
|
||||
</a>
|
||||
</div><!-- End Logo -->
|
||||
|
||||
<div class="card mb-3">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="pt-2 pb-3">
|
||||
<h5 class="card-title text-center pb-0 fs-4 help">Sign in</h5>
|
||||
|
||||
</div>
|
||||
|
||||
<form method="post" class="row g-3 needs-validation" novalidate>
|
||||
<input id="csrf_token" name="csrf_token" type="hidden" value="ImNlZTM1N2RlMjQ2MDMxYmQ0NDQzMWI0ZDQ0NmZkNjdhZjc0NGU2NjMi.Zn14CA.unYlCb0lVq4wtzKx_42V3vXQ0dg">
|
||||
|
||||
<div class="col-12">
|
||||
<input type="email" placeholder="Email" name="email" class="form-control" id="yourEmail" required value="">
|
||||
<div class="invalid-feedback">Please enter your email.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="input-group">
|
||||
<input type="password" placeholder="Password" name="password" class="form-control" id="id_password" required>
|
||||
<i class="input-group-text bi bi-eye" id="togglePassword" style="cursor: pointer">
|
||||
</i>
|
||||
</div>
|
||||
<div class="invalid-feedback">Please enter your password!</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO(@slamora): hidde until it is implemented
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="remember" id="rememberMe">
|
||||
<label class="form-check-label" for="rememberMe">Remember me</label>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="col-12">
|
||||
<button class="btn btn-primary w-100" type="submit">Next</button>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<p class="small mb-0">Don't have account? <a href="https://www.usody.com/pricing.html">Create an account</a></p>
|
||||
<p class="small mb-0">Forgot password? <a href="/reset_password/">Reset your password</a></p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="credits">
|
||||
<a href="https://help.usody.com/en/getting-started/login-usody/" target="_blank">Help</a> |
|
||||
<a href="https://www.usody.com/legal/privacy-policy" target="_blank">Privacy</a> |
|
||||
<a href="https://www.usody.com/legal/terms" target="_blank">Terms</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</main><!-- End #main -->
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Do you want to try USOdy tools?</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Just write an email to <a href="mali:hello@usody.com">hello@usody.com</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End register modal -->
|
||||
<script>
|
||||
const togglePassword = document.querySelector('#togglePassword');
|
||||
const password = document.querySelector('#id_password');
|
||||
|
||||
togglePassword.addEventListener('click', function (e) {
|
||||
// toggle the type attribute
|
||||
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
// toggle the eye slash icon
|
||||
if(type == "password"){
|
||||
this.classList.remove('bi-eye-slash');
|
||||
this.classList.add('bi-eye');
|
||||
} else if(type == "text"){
|
||||
this.classList.remove('bi-eye');
|
||||
this.classList.add('bi-eye-slash');
|
||||
}
|
||||
password.setAttribute('type', type);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<a href="#" class="back-to-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>
|
||||
|
||||
<!-- Vendor JS Files -->
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Template Main JS File -->
|
||||
<script src="/static/js/main.js"></script>
|
||||
|
||||
<!-- Api backend -->
|
||||
<script>
|
||||
const API_URLS = {
|
||||
Auth_Token: `Basic ${btoa("fa0cd7fb-690d-4ebb-a482-c1d4eae3deb1:")}`, //
|
||||
currentUserID: "56957cdb-db7b-452c-843b-200dc4604b08",
|
||||
lots: "/lots/",
|
||||
lots_detail: "/inventory/lot/ReplaceTEXT/device/",
|
||||
devices: "/devices/",
|
||||
devices_modify: "/lots/UUID/devices",
|
||||
devices_detail: "/inventory/device/ReplaceTEXT/"
|
||||
}
|
||||
</script>
|
||||
<script src="/static/js/api.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
133
login/templates/login_base.html
Normal file
133
login/templates/login_base.html
Normal file
|
@ -0,0 +1,133 @@
|
|||
{% load i18n static %}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% block head %}
|
||||
{% block meta %}
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="robots" content="NONE,NOARCHIVE" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="Pangea">
|
||||
{% endblock %}
|
||||
<title>{% block title %}{% if title %}{{ title }} – {% endif %}DeviceHub{% endblock %}</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link rel="stylesheet" href= "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
|
||||
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
.bd-placeholder-img {
|
||||
font-size: 1.125rem;
|
||||
text-anchor: middle;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.bd-placeholder-img-lg {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="{% static "/css/dashboard.css" %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body id="body-login">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<section class="section register min-vh-100 d-flex flex-column align-items-center justify-content-center py-4">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-4 col-md-6 d-flex flex-column align-items-center justify-content-center">
|
||||
|
||||
<div class="d-flex justify-content-center py-4">
|
||||
<a href="/login/" class="d-flex align-items-center w-auto">
|
||||
<img class="img-fluid" src="{% static '/images/logo-pangea-monocrome-h.png' %}"
|
||||
alt="Pangea.org - Internet etic i solidari" />
|
||||
</a>
|
||||
</div><!-- End Logo -->
|
||||
|
||||
<div class="card mb-3 shadow p-3 mb-5 bg-body rounded">
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="pt-2 pb-3">
|
||||
<h5 class="card-title text-center pb-0 fs-4 help">Sign in</h5>
|
||||
|
||||
</div>
|
||||
|
||||
{% block login_content %}
|
||||
{% endblock login_content %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="credits">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<main class="col-md-12 bt-5">
|
||||
{% block messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock messages %}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer text-center">
|
||||
<div class="container">
|
||||
<span class="text-muted">{{ commit_id }}</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/static/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
|
||||
<script src="/static/js/dashboard.js"></script>
|
||||
<script>
|
||||
const togglePassword = document.querySelector('#togglePassword');
|
||||
const password = document.querySelector('#id_password');
|
||||
|
||||
togglePassword.addEventListener('click', function (e) {
|
||||
// toggle the type attribute
|
||||
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
// toggle the eye slash icon
|
||||
if(type == "password"){
|
||||
this.classList.remove('bi-eye-slash');
|
||||
this.classList.add('bi-eye');
|
||||
} else if(type == "text"){
|
||||
this.classList.remove('bi-eye');
|
||||
this.classList.add('bi-eye-slash');
|
||||
}
|
||||
password.setAttribute('type', type);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
27
login/templates/password_reset.html
Normal file
27
login/templates/password_reset.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "login_base.html" %}
|
||||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block login_content %}
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset' %}</h2>
|
||||
<span>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<div>
|
||||
<form action="{% url 'login:password_reset' %}" role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_form_errors form type='non_fields' %}
|
||||
<div class="form-actions-no-box">
|
||||
<input type="submit" name="submit" value="{% trans 'Reset my password' %}" class="btn btn-primary form-control" id="submit-id-submit">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!--/.well-->
|
||||
{% endblock %}
|
16
login/templates/password_reset_complete.html
Normal file
16
login/templates/password_reset_complete.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% extends "login_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block login_content %}
|
||||
<div class="container-fluid" style="margin-top: 30px">
|
||||
<div class="row-fluid">
|
||||
<div class="well" style="width: 800px; margin: auto auto 50px auto">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset complete' %}</h2>
|
||||
<p>{% trans 'Your password has been set. You may go ahead and log in now.' %}</p>
|
||||
<a href="{% url 'login:login' %}">{% trans 'Login' %}</a>
|
||||
</div>
|
||||
</div><!--/.well-->
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!-- /.container-fluid -->
|
||||
{% endblock %}
|
36
login/templates/password_reset_confirm.html
Normal file
36
login/templates/password_reset_confirm.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends "auth/login_base.html" %}
|
||||
{% load i18n django_bootstrap5 %}
|
||||
|
||||
{% block login_content %}
|
||||
<div class="container-fluid" style="margin-top: 30px">
|
||||
<div class="row-fluid">
|
||||
{% if validlink %}
|
||||
<div class="well" style="width: 600px; margin-left: auto; margin-right: auto">
|
||||
<h2>{% trans 'Enter new password' %}</h2>
|
||||
<p>{% trans 'Please enter your new password twice so we can verify you typed it in correctly.' %}</p>
|
||||
</div><!-- /well -->
|
||||
|
||||
<div class="well" style="width: 320px; margin-left: auto; margin-right: auto">
|
||||
<div class="row-fluid">
|
||||
<div>
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_form_errors form type='non_fields' %}
|
||||
<div class="form-actions-no-box">
|
||||
<input type="submit" name="submit" value="{% trans 'Change my password' %}" class="btn btn-primary form-control" id="submit-id-submit">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!--/.well-->
|
||||
{% else %}
|
||||
<div class="well" style="width: 800px; margin-left: auto; margin-right: auto">
|
||||
<h2>{% trans 'Password reset unsuccessful' %}</h2>
|
||||
<p>{% trans 'The password reset link was invalid, possibly because it has already been used.' %}<br />
|
||||
{% trans 'Please request a new password reset.' %}</p>
|
||||
</div><!-- /well -->
|
||||
{% endif %}
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!-- /.container-fluid -->
|
||||
{% endblock %}
|
15
login/templates/password_reset_done.html
Normal file
15
login/templates/password_reset_done.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% extends "auth/login_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block login_content %}
|
||||
<div class="well">
|
||||
<div class="row-fluid">
|
||||
<h2>{% trans 'Password reset sent' %}</h2>
|
||||
|
||||
<p>{% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}</p>
|
||||
|
||||
<p>{% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}</p>
|
||||
|
||||
</div><!-- /.row-fluid -->
|
||||
</div><!--/.well-->
|
||||
{% endblock %}
|
30
login/templates/password_reset_email.html
Normal file
30
login/templates/password_reset_email.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
<p>
|
||||
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Please go to the following page and choose a new password:" %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% block reset_link %}
|
||||
<a href="{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}">
|
||||
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}
|
||||
</a>
|
||||
{% endblock %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% trans "Thanks for using our site!" %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% endautoescape %}
|
14
login/templates/password_reset_email.txt
Normal file
14
login/templates/password_reset_email.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
|
||||
|
||||
{% trans "Please go to the following page and choose a new password:" %}
|
||||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url 'idhub:password_reset_confirm' uidb64=uid token=token %}
|
||||
{% endblock %}
|
||||
{% trans "Your username, in case you've forgotten:" %} {{ user.username }}
|
||||
|
||||
{% trans "Thanks for using our site!" %}
|
||||
|
||||
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||
|
||||
{% endautoescape %}
|
3
login/templates/password_reset_subject.txt
Normal file
3
login/templates/password_reset_subject.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktrans %}Password reset on {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
3
login/tests.py
Normal file
3
login/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
34
login/urls.py
Normal file
34
login/urls.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
from django.contrib.auth import views as auth_views
|
||||
from django.views.generic import RedirectView
|
||||
from django.urls import path, reverse_lazy
|
||||
from login.views import (
|
||||
LoginView,
|
||||
LogoutView,
|
||||
PasswordResetView,
|
||||
PasswordResetConfirmView,
|
||||
)
|
||||
|
||||
app_name = 'login'
|
||||
|
||||
urlpatterns = [
|
||||
path("", RedirectView.as_view(url=reverse_lazy('login:login'),
|
||||
permanent=False)),
|
||||
path('login/', LoginView.as_view(), name='login'),
|
||||
path('logout/', LogoutView, name='logout'),
|
||||
path('auth/password_reset/', PasswordResetView.as_view(), name='password_reset'),
|
||||
path('auth/password_reset/done/',
|
||||
auth_views.PasswordResetDoneView.as_view(
|
||||
template_name='password_reset_done.html'
|
||||
),
|
||||
name='password_reset_done'
|
||||
),
|
||||
path('auth/reset/<uidb64>/<token>/', PasswordResetConfirmView.as_view(),
|
||||
name='password_reset_confirm'
|
||||
),
|
||||
path('auth/reset/done/',
|
||||
auth_views.PasswordResetCompleteView.as_view(
|
||||
template_name='password_reset_complete.html'
|
||||
),
|
||||
name='password_reset_complete'
|
||||
),
|
||||
]
|
73
login/views.py
Normal file
73
login/views.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
import logging
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth import login as auth_login
|
||||
from django.contrib.auth import logout as auth_logout
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.shortcuts import redirect
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LoginView(auth_views.LoginView):
|
||||
template_name = 'login.html'
|
||||
extra_context = {
|
||||
'title': _('Login'),
|
||||
'success_url': reverse_lazy('dashboard:dashboard'),
|
||||
# 'commit_id': settings.COMMIT,
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.extra_context['success_url'] = request.GET.get(
|
||||
'next',
|
||||
reverse_lazy('dashboard:dashboard')
|
||||
)
|
||||
if not self.request.user.is_anonymous:
|
||||
return redirect(reverse_lazy('dashboard:dashboard'))
|
||||
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
user = form.get_user()
|
||||
auth_login(self.request, user)
|
||||
|
||||
if user.is_anonymous:
|
||||
return redirect(reverse_lazy("login:login"))
|
||||
|
||||
return redirect(self.extra_context['success_url'])
|
||||
|
||||
|
||||
def LogoutView(request):
|
||||
auth_logout(request)
|
||||
return redirect(reverse_lazy("login:login"))
|
||||
|
||||
|
||||
class PasswordResetConfirmView(auth_views.PasswordResetConfirmView):
|
||||
template_name = 'password_reset_confirm.html'
|
||||
success_url = reverse_lazy('login:password_reset_complete')
|
||||
|
||||
def form_valid(self, form):
|
||||
password = form.cleaned_data.get("new_password1")
|
||||
user = form.user
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
|
||||
|
||||
class PasswordResetView(auth_views.PasswordResetView):
|
||||
template_name = 'password_reset.html'
|
||||
email_template_name = 'password_reset_email.txt'
|
||||
html_email_template_name = 'password_reset_email.html'
|
||||
subject_template_name = 'password_reset_subject.txt'
|
||||
success_url = reverse_lazy('login:password_reset_done')
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
return super().form_valid(form)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
|
Loading…
Reference in a new issue