Finally got all the OTP stuff working

Finalized dbsettings views
Easter egg for missing backend pages
This commit is contained in:
Kumi 2020-04-15 22:19:03 +02:00
parent d74a4c1b8b
commit 7708128255
27 changed files with 610 additions and 87 deletions

View file

@ -9,6 +9,9 @@ class BaseOTPProvider:
def get_logo(self): def get_logo(self):
return "" return ""
def __str__(self):
return self.get_name
@property @property
def is_active(self): def is_active(self):
'''Returns True if the provider is properly configured and ready to use.''' '''Returns True if the provider is properly configured and ready to use.'''

1
core/forms/__init__.py Normal file
View file

@ -0,0 +1 @@
from core.forms.auth import *

16
core/forms/auth.py Normal file
View file

@ -0,0 +1,16 @@
from django.forms import Form, EmailField, CharField, PasswordInput, ChoiceField
from core.helpers.otp import get_otp_choices
class LoginForm(Form):
email = EmailField()
password = CharField(widget=PasswordInput)
class OTPSelectorForm(Form):
def __init__(self, *args, **kwargs):
otp_choices = kwargs.pop('otp_choices', [])
super(OTPSelectorForm, self).__init__(*args, **kwargs)
self.fields['provider'] = ChoiceField(choices=otp_choices)
class OTPVerificationForm(Form):
token = CharField()

25
core/helpers/otp.py Normal file
View file

@ -0,0 +1,25 @@
from core.models import OTPUser
from core.modules.otp import providers
def get_user_otps(user):
try:
all_otps = OTPUser.objects.filter(user=user)
except:
return {}
user_providers = [otp.provider for otp in all_otps]
active_providers = {}
for name, provider in providers.items():
if name in user_providers:
active_providers[name] = provider
return active_providers
def get_otp_by_name(name):
for pname, provider in providers.items():
if pname == name:
return provider
def get_otp_choices(user):
return [(name, provider) for name, provider in get_user_otps(user).items()]

View file

@ -1,2 +1,3 @@
from core.models.files import * from core.models.files import *
from core.models.profiles import * from core.models.profiles import *
from core.models.otp import *

13
core/models/otp.py Normal file
View file

@ -0,0 +1,13 @@
from django.db.models import Model, ForeignKey, CharField, DateTimeField, UUIDField, CASCADE
from django.contrib.auth import get_user_model
from uuid import uuid4
class OTPUser(Model):
user = ForeignKey(get_user_model(), CASCADE)
provider = CharField(max_length=255)
class LoginSession(Model):
uuid = UUIDField(default=uuid4, primary_key=True)
user = ForeignKey(get_user_model(), CASCADE)
creation = DateTimeField(auto_now_add=True)

View file

@ -2,12 +2,12 @@ import importlib
from django.conf import settings from django.conf import settings
providers = [] providers = {}
for module in settings.EXPEPHALON_MODULES: for module in settings.EXPEPHALON_MODULES:
try: try:
moo = importlib.import_module(f"{module}.otp") moo = importlib.import_module(f"{module}.otp")
for provider in moo.OTPPROVIDERS: for name, provider in moo.OTPPROVIDERS.items():
providers.append(provider) providers[name] = provider
except (AttributeError, ModuleNotFoundError): except (AttributeError, ModuleNotFoundError):
continue continue

View file

@ -3,7 +3,16 @@ import importlib
from django.conf import settings from django.conf import settings
from django.urls import path from django.urls import path
URLPATTERNS = [] from core.views import DashboardView, LoginView, OTPSelectorView, LogoutView, OTPValidatorView, BackendNotImplementedView
URLPATTERNS = [
path('login/', LoginView.as_view(), name="login"),
path('login/otp/select/', OTPSelectorView.as_view(), name="otpselector"),
path('login/otp/validate/', OTPValidatorView.as_view(), name="otpvalidator"),
path('logout/', LogoutView.as_view(), name="logout"),
path('admin/', DashboardView.as_view(), name="dashboard"),
path('admin/oops/', BackendNotImplementedView.as_view(), name="backendni")
]
for module in settings.EXPEPHALON_MODULES: for module in settings.EXPEPHALON_MODULES:
try: try:
@ -13,6 +22,11 @@ for module in settings.EXPEPHALON_MODULES:
except (AttributeError, ModuleNotFoundError): except (AttributeError, ModuleNotFoundError):
pass pass
if "dbsettings" in settings.INSTALLED_APPS: try:
from core.views import DBSettingsListView from core.views import DBSettingsListView, DBSettingsEditView, DBSettingsDeleteView, DBSettingsCreateView
URLPATTERNS.append(path("admin/dbsettings/", DBSettingsListView.as_view(), name="dbsettings")) URLPATTERNS.append(path("admin/dbsettings/", DBSettingsListView.as_view(), name="dbsettings"))
URLPATTERNS.append(path("admin/dbsettings/<pk>/delete/", DBSettingsDeleteView.as_view(), name="dbsettings_delete"))
URLPATTERNS.append(path("admin/dbsettings/<pk>/edit/", DBSettingsEditView.as_view(), name="dbsettings_edit"))
URLPATTERNS.append(path("admin/dbsettings/create/", DBSettingsCreateView.as_view(), name="dbsettings_create"))
except:
pass

View file

@ -6,7 +6,7 @@ from django.conf import settings
dashboard_section = NavSection("Dashboard", "") dashboard_section = NavSection("Dashboard", "")
dashboard_item = NavItem("Dashboard", "fa-rocket", "backend") dashboard_item = NavItem("Dashboard", "fa-rocket", "dashboard")
dashboard_section.add_item(dashboard_item) dashboard_section.add_item(dashboard_item)
@ -16,10 +16,10 @@ navigations["backend_main"].add_section(dashboard_section)
clients_section = NavSection("Clients", "") clients_section = NavSection("Clients", "")
client_list_item = NavItem("List Clients", "fa-user-tag", "backend") client_list_item = NavItem("List Clients", "fa-user-tag", "backendni")
client_add_item = NavItem("Add Client", "fa-user-edit", "backend") client_add_item = NavItem("Add Client", "fa-user-edit", "backendni")
client_groups_item = NavItem("Client Groups", "fa-users", "backend") client_groups_item = NavItem("Client Groups", "fa-users", "backendni")
client_leads_item = NavItem("Leads", "fa-blender-phone", "backend") client_leads_item = NavItem("Leads", "fa-blender-phone", "backendni")
clients_section.add_item(client_list_item) clients_section.add_item(client_list_item)
clients_section.add_item(client_add_item) clients_section.add_item(client_add_item)
@ -32,8 +32,8 @@ navigations["backend_main"].add_section(clients_section)
quotes_section = NavSection("Quotes", "") quotes_section = NavSection("Quotes", "")
quote_list_item = NavItem("List Quotes", "fa-file-invoice-dollar", "backend") quote_list_item = NavItem("List Quotes", "fa-file-invoice-dollar", "backendni")
quote_create_item = NavItem("Create Quote", "fa-plus-square", "backend") quote_create_item = NavItem("Create Quote", "fa-plus-square", "backendni")
quotes_section.add_item(quote_list_item) quotes_section.add_item(quote_list_item)
quotes_section.add_item(quote_create_item) quotes_section.add_item(quote_create_item)
@ -44,11 +44,11 @@ navigations["backend_main"].add_section(quotes_section)
billing_section = NavSection("Billing", "") billing_section = NavSection("Billing", "")
invoice_list_item = NavItem("List Invoices", "fa-file-invoice-dollar", "backend") invoice_list_item = NavItem("List Invoices", "fa-file-invoice-dollar", "backendni")
invoice_create_item = NavItem("Create Invoice", "fa-plus-square", "backend") invoice_create_item = NavItem("Create Invoice", "fa-plus-square", "backendni")
billable_list_item = NavItem("List Billable Items", "fa-hand-holding-usd", "backend") billable_list_item = NavItem("List Billable Items", "fa-hand-holding-usd", "backendni")
billable_create_item = NavItem("Create Billable Item", "fa-plus-square", "backend") billable_create_item = NavItem("Create Billable Item", "fa-plus-square", "backendni")
list_transaction_item = NavItem("Transaction List", "fa-funnel-dollar", "backend") list_transaction_item = NavItem("Transaction List", "fa-funnel-dollar", "backendni")
billing_section.add_item(invoice_list_item) billing_section.add_item(invoice_list_item)
billing_section.add_item(invoice_create_item) billing_section.add_item(invoice_create_item)
@ -62,9 +62,9 @@ navigations["backend_main"].add_section(billing_section)
support_section = NavSection("Support", "") support_section = NavSection("Support", "")
ticket_view_item = NavItem("View Tickets", "fa-life-ring", "backend") ticket_view_item = NavItem("View Tickets", "fa-life-ring", "backendni")
ticket_add_item = NavItem("Add Ticket", "fa-plus-square", "backend") ticket_add_item = NavItem("Add Ticket", "fa-plus-square", "backendni")
conversation_add_item = NavItem("Add Conversation", "fa-comments", "backend") conversation_add_item = NavItem("Add Conversation", "fa-comments", "backendni")
support_section.add_item(ticket_view_item) support_section.add_item(ticket_view_item)
support_section.add_item(ticket_add_item) support_section.add_item(ticket_add_item)
@ -76,8 +76,8 @@ navigations["backend_main"].add_section(support_section)
reports_section = NavSection("Reports", "") reports_section = NavSection("Reports", "")
report_period_item = NavItem("Income by period", "fa-chart-bar", "backend") report_period_item = NavItem("Income by period", "fa-chart-bar", "backendni")
report_forecast_item = NavItem("Income Forecast", "fa-chart-area", "backend") report_forecast_item = NavItem("Income Forecast", "fa-chart-area", "backendni")
reports_section.add_item(report_period_item) reports_section.add_item(report_period_item)
reports_section.add_item(report_forecast_item) reports_section.add_item(report_forecast_item)
@ -88,14 +88,14 @@ navigations["backend_main"].add_section(reports_section)
administration_section = NavSection("Administration", "") administration_section = NavSection("Administration", "")
user_administration_item = NavItem("Administrator Users", "fa-users-cog", "backend") user_administration_item = NavItem("Administrator Users", "fa-users-cog", "backendni")
brand_administration_item = NavItem("Brands", "fa-code-branch", "backend") brand_administration_item = NavItem("Brands", "fa-code-branch", "backendni")
sms_administration_item = NavItem("SMS Gateway", "fa-sms", "backend") sms_administration_item = NavItem("SMS Gateway", "fa-sms", "backendni")
otp_administration_item = NavItem("Two-Factor Authentication", "fa-id-badge", "backend") otp_administration_item = NavItem("Two-Factor Authentication", "fa-id-badge", "backendni")
backup_administration_item = NavItem("Backups", "fa-shield-alt", "backend") backup_administration_item = NavItem("Backups", "fa-shield-alt", "backendni")
product_administration_item = NavItem("Products", "fa-cube", "backend") product_administration_item = NavItem("Products", "fa-cube", "backendni")
pgroup_administration_item = NavItem("Product Groups", "fa-cubes", "backend") pgroup_administration_item = NavItem("Product Groups", "fa-cubes", "backendni")
payment_administration_item = NavItem("Payment Gateways", "fa-credit-card", "backend") payment_administration_item = NavItem("Payment Gateways", "fa-credit-card", "backendni")
dbsettings_item = NavItem("Database Settings", "fa-database", "dbsettings") dbsettings_item = NavItem("Database Settings", "fa-database", "dbsettings")
administration_section.add_item(user_administration_item) administration_section.add_item(user_administration_item)

View file

@ -1,10 +1 @@
from django.urls import path, include from core.modules.urls import URLPATTERNS as urlpatterns
from core.views import DashboardView
from core.modules.urls import URLPATTERNS
import importlib
urlpatterns = [
path('admin/', DashboardView.as_view(), name="backend"),
] + URLPATTERNS

View file

@ -1,7 +1,10 @@
from django.shortcuts import render from django.shortcuts import render
from django.views.generic import TemplateView, ListView from django.views.generic import TemplateView
from django.conf import settings from django.conf import settings
from core.views.dbsettings import *
from core.views.auth import *
# Create your views here. # Create your views here.
class IndexView(TemplateView): class IndexView(TemplateView):
@ -10,12 +13,5 @@ class IndexView(TemplateView):
class DashboardView(TemplateView): class DashboardView(TemplateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/index.html" template_name = f"{settings.EXPEPHALON_BACKEND}/index.html"
try: class BackendNotImplementedView(TemplateView):
from dbsettings.models import Setting template_name = f"{settings.EXPEPHALON_BACKEND}/notimplemented.html"
class DBSettingsListView(ListView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings.html"
model = Setting
except ModuleNotFoundError:
pass

113
core/views/auth.py Normal file
View file

@ -0,0 +1,113 @@
from django.conf import settings
from django.views.generic import FormView, View
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import redirect
from django.core.exceptions import PermissionDenied
from django.contrib import messages
from core.forms import LoginForm, OTPSelectorForm, OTPVerificationForm
from core.models.otp import LoginSession
from core.helpers.otp import get_user_otps, get_otp_choices, get_otp_by_name
class LoginView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/login.html"
form_class = LoginForm
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect(request.GET.get("next", "dashboard"))
return super().get(request, *args, **kwargs)
def form_valid(self, form):
user = authenticate(username=form.cleaned_data['email'],password=form.cleaned_data['password'])
if user:
if not get_user_otps(user):
login(self.request, user)
return redirect("dashboard")
session = LoginSession.objects.create(user=user)
self.request.session["otpsession"] = str(session.uuid)
self.request.session["next"] = self.request.GET.get("next", "dashboard")
return redirect("otpselector")
return super().form_invalid(form)
class OTPSelectorView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/otp_selector.html"
form_class = OTPSelectorForm
def clean_session(self):
for key in ("otpsession", "otpprovider", "next"):
try:
del self.request.session[key]
except:
pass
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
try:
assert self.request.session["otpsession"]
except:
raise PermissionDenied()
user = LoginSession.objects.get(uuid=self.request.session["otpsession"]).user
kwargs["otp_choices"] = get_otp_choices(user)
return kwargs
def form_valid(self, form):
self.request.session["otpprovider"] = form.cleaned_data["provider"]
return redirect("otpvalidator")
def form_invalid(self, form):
self.clean_session()
return redirect("login")
class OTPValidatorView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/otp_verifier.html"
form_class = OTPVerificationForm
def clean_session(self):
for key in ("otpsession", "otpprovider", "next"):
try:
del self.request.session[key]
except:
pass
def validate_session(self, request):
try:
assert request.session["otpsession"]
assert request.session["otpprovider"]
user = LoginSession.objects.get(uuid=request.session["otpsession"]).user
assert request.session["otpprovider"] in get_user_otps(user).keys()
provider = get_otp_by_name(request.session["otpprovider"])()
return user, provider
except:
self.clean_session()
raise PermissionDenied()
def get(self, request, *args, **kwargs):
user, provider = self.validate_session(request)
response = provider.start_authentication(user)
messages.info(request, response)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.validate_session(request)
return super().post(request, *args, **kwargs)
def form_invalid(self, form):
self.clean_session()
return redirect("login")
def form_valid(self, form):
user, provider = self.validate_session(self.request)
if provider.validate_token(user, form.cleaned_data["token"]):
login(self.request, user)
ret = redirect(self.request.session.get("next", "dashboard"))
self.clean_session()
return ret
self.clean_session()
messages.error(self.request, "Incorrect token entered. Please try again. If the issue persists, contact support to regain access to your account.")
return redirect("login")
class LogoutView(View):
def get(self, request, *args, **kwargs):
logout(request)
return redirect("login")

30
core/views/dbsettings.py Normal file
View file

@ -0,0 +1,30 @@
from django.conf import settings
from django.views.generic import ListView, UpdateView, DeleteView, CreateView
from django.urls import reverse_lazy
try:
from dbsettings.models import Setting
class DBSettingsListView(ListView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings.html"
model = Setting
class DBSettingsEditView(UpdateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_update.html"
model = Setting
success_url = reverse_lazy("dbsettings")
fields = ["key", "value"]
class DBSettingsDeleteView(DeleteView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_delete.html"
model = Setting
success_url = reverse_lazy("dbsettings")
class DBSettingsCreateView(CreateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_create.html"
model = Setting
success_url = reverse_lazy("dbsettings")
fields = ["key", "value"]
except ModuleNotFoundError:
pass

View file

@ -16,6 +16,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'polymorphic', 'polymorphic',
'phonenumber_field', 'phonenumber_field',
'bootstrap4',
'core', 'core',
'dbsettings', 'dbsettings',
] + EXPEPHALON_MODULES ] + EXPEPHALON_MODULES

View file

@ -5,3 +5,4 @@ boto3
Pillow Pillow
django-polymorphic django-polymorphic
django-phonenumber-field[phonenumbers] django-phonenumber-field[phonenumbers]
django-bootstrap4

View file

@ -37,4 +37,4 @@ class SMSOTP(BaseOTPProvider):
except OTPToken.DoesNotExist: except OTPToken.DoesNotExist:
return False return False
OTPPROVIDERS = [SMSOTP] OTPPROVIDERS = {"smsotp": SMSOTP}

@ -1 +0,0 @@
Subproject commit 4e6402443679e0a9d12c7401ac8783ef4646657f

View file

@ -0,0 +1,46 @@
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Language" content="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Login - ArchitectUI HTML Bootstrap 4 Dashboard Template</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no"
/>
<meta name="description" content="ArchitectUI HTML Bootstrap 4 Dashboard Template">
<!-- Disable tap highlight on IE -->
<meta name="msapplication-tap-highlight" content="no">
<link href="{% static "backend/css/main.css" %}" rel="stylesheet"></head>
<body>
<div class="app-container app-theme-white body-tabs-shadow">
<div class="app-container">
<div class="h-100">
<div class="h-100 no-gutters row">
<div class="d-none d-lg-block col-lg-4">
<div class="slider-light">
<div class="slick-slider">
<div>
<div class="position-relative h-100 d-flex justify-content-center align-items-center bg-plum-plate" tabindex="-1">
<div class="slide-img-bg" style="background-image: url('assets/images/originals/city.jpg');"></div>
<div class="slider-content"><h3>Perfect Balance</h3>
<p>ArchitectUI is like a dream. Some think it's too good to be true! Extensive collection of unified React Boostrap Components and Elements.</p></div>
</div>
</div>
</div>
</div>
</div>
<div class="h-100 d-flex bg-white justify-content-center align-items-center col-md-12 col-lg-8">
{% block content %}{% endblock %}
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="{% static "backend/scripts/main.js" %}"></script></body>
</html>

View file

@ -102,16 +102,16 @@
<h6 tabindex="-1" class="dropdown-header">Header</h6> <h6 tabindex="-1" class="dropdown-header">Header</h6>
<button type="button" tabindex="0" class="dropdown-item">Actions</button> <button type="button" tabindex="0" class="dropdown-item">Actions</button>
<div tabindex="-1" class="dropdown-divider"></div> <div tabindex="-1" class="dropdown-divider"></div>
<button type="button" tabindex="0" class="dropdown-item">Dividers</button> <a href="{% url "logout" %}" type="button" tabindex="0" class="dropdown-item">Logout</a>
</div> </div>
</div> </div>
</div> </div>
<div class="widget-content-left ml-3 header-user-info"> <div class="widget-content-left ml-3 header-user-info">
<div class="widget-heading"> <div class="widget-heading">
Klaus-Uwe Mitterer {{ request.user.get_full_name }}
</div> </div>
<div class="widget-subheading"> <div class="widget-subheading">
Chief Expephalon Officer {{ request.user.profile.role }}
</div> </div>
</div> </div>
<div class="widget-content-right header-user-info ml-3"> <div class="widget-content-right header-user-info ml-3">

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Create Setting
<div class="page-title-subheading">Create a new key-value setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Create Setting
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Edit Setting
<div class="page-title-subheading">Edit key and value of a setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Deleting {{ form.key.value }}
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
Are you sure you wish to delete {{ form.key.value }}?
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -13,9 +13,9 @@
</div> </div>
</div> </div>
<div class="page-title-actions"> <div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success"> <a href="{% url "dbsettings_create" %}" type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting <i class="fa fa-plus"></i> New Setting
</button> </a>
</div> </div>
</div> </div>
@ -45,8 +45,8 @@
{% for setting in object_list %} {% for setting in object_list %}
<tr> <tr>
<th scope="row">{{ setting.key }}</th> <th scope="row">{{ setting.key }}</th>
<td><a href="javascript:void(0);">Click to display...</a></td> <td><a href="javascript:alert('{{ setting.value }}');">Click to display...</a></td>
<td><a href="/devices/20/edit"><i class="fas fa-edit" title="Edit Setting"></i></a> <a href="#"><i style="color: darkred;" onclick="askdelete(20);" class="fas fa-trash-alt" title="Delete Setting"></i></a></td> <td><a href="{% url "dbsettings_edit" setting.key %}"><i class="fas fa-edit" title="Edit Setting"></i></a> <a href="#"><i style="color: darkred;" onclick="askdelete(20);" class="fas fa-trash-alt" title="Delete Setting"></i></a></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Edit Setting
<div class="page-title-subheading">Edit key and value of a setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Editing {{ form.key.value }}
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,26 +1,34 @@
<html> {% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
<head> {% block content %}
<link rel="stylesheet" href="{% static "backend/css/login.css"> <div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<link href="https://fontproxy.kumi.systems/css?family=Ubuntu" rel="stylesheet"> <div class="app-logo"></div>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <h4 class="mb-0">
<link rel="stylesheet" href="https://fa.kumi.systems/css/all.css"> <span class="d-block">Welcome back,</span>
<title>Sign in</title> <span>Please sign in to your account.</span></h4>
</head> <h6 class="mt-3">No account? <a href="javascript:void(0);" class="text-primary">Sign up now</a></h6>
{% bootstrap_messages %}
<body> <div class="divider row"></div>
<div class="main"> <div>
<p class="sign" align="center">Sign in</p> <form method="POST" class="">
{% csrf_token %} {% csrf_token %}
<form class="form1"> <div class="form-row">
<input class="un " type="text" align="center" name="username" placeholder="Username"> <div class="col-md-6">
<input class="pass" type="password" align="center" name="password" placeholder="Password"> <div class="position-relative form-group"><label for="exampleEmail" class="">Email</label><input name="email" id="exampleEmail" placeholder="Email here..." type="email" class="form-control"></div>
<a class="submit" align="center">Sign in</a> </div>
<p class="forgot" align="center"><a href="#">Forgot Password?</p> <div class="col-md-6">
<div class="position-relative form-group"><label for="examplePassword" class="">Password</label><input name="password" id="examplePassword" placeholder="Password here..." type="password"
class="form-control"></div>
</div>
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div> </div>
</div>
</body> {% endblock %}
</html>

View file

@ -0,0 +1,40 @@
{% extends "backend/base.html" %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-times">
</i>
</div>
<div>Oops!
<div class="page-title-subheading">This is not implemented yet...
</div>
</div>
</div>
<div class="page-title-actions">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
But here's something for you:
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<div class="card mb-3 widget-chart widget-chart2 text-left w-100">
<iframe width="1020" height="630" src="https://www.youtube-nocookie.com/embed/VFZNvj-HfBU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<div class="app-logo"></div>
<h4 class="mb-0">
<span class="d-block">Welcome back,</span>
<span>Please select your Two-Factor Authentication provider</span></h4>
{% bootstrap_messages %}
<div class="divider row"></div>
<div>
<form method="POST" class="">
{% csrf_token %}
<div class="form-row">
{% bootstrap_form form %}
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<div class="app-logo"></div>
<h4 class="mb-0">
<span class="d-block">Welcome back,</span>
<span>Please enter your OTP Token</span></h4>
{% bootstrap_messages %}
<div class="divider row"></div>
<div>
<form method="POST" class="">
{% csrf_token %}
<div class="form-row">
{% bootstrap_form form %}
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}