Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions optimap/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"sesame.backends.ModelBackend",
]

AUTH_USER_MODEL = "publications.CustomUser"

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
Expand Down
4 changes: 0 additions & 4 deletions publications/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
from django_q.models import Schedule
from datetime import datetime, timedelta


# Unregister the default User admin
admin.site.unregister(User)

@admin.action(description="Mark selected publications as published")
def make_public(modeladmin, request, queryset):
queryset.update(status="p")
Expand Down
34 changes: 30 additions & 4 deletions publications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from django.contrib.postgres.fields import ArrayField
from django_currentuser.db.models import CurrentUserField
from django.utils.timezone import now
from django.contrib.auth import get_user_model

User = get_user_model()
from django.contrib.auth.models import AbstractUser, Group, Permission
import uuid
from django.utils.timezone import now
import logging
logger = logging.getLogger(__name__)

STATUS_CHOICES = (
("d", "Draft"),
Expand All @@ -14,6 +16,27 @@
("h", "Harvested"),
)

class CustomUser(AbstractUser):
deleted = models.BooleanField(default=False)
deleted_at = models.DateTimeField(null=True, blank=True)
groups = models.ManyToManyField(Group, related_name="publications_users", blank=True)
user_permissions = models.ManyToManyField(Permission, related_name="publications_users_permissions", blank=True)

def soft_delete(self):
"""Marks the user as deleted instead of removing from the database."""
self.deleted = True
self.deleted_at = now()
self.save()
logger.info(f"User {self.username} (ID: {self.id}) was soft deleted at {self.deleted_at}")


def restore(self):
"""Restores a previously deleted user."""
self.deleted = False
self.deleted_at = None
self.save()
logger.info(f"User {self.username} (ID: {self.id}) was restored.")

class Publication(models.Model):
# required fields
title = models.TextField()
Expand Down Expand Up @@ -88,6 +111,9 @@ class Meta:
ordering = ['user_name']
verbose_name = "subscription"

from django.contrib.auth import get_user_model
User = get_user_model()

class EmailLog(models.Model):
TRIGGER_CHOICES = [
("admin", "Admin Panel"),
Expand Down Expand Up @@ -126,7 +152,7 @@ def log_email(cls, recipient, subject, content, sent_by=None, trigger_source="ma
from import_export import fields, resources
from import_export.widgets import ForeignKeyWidget
from django.conf import settings
from django.contrib.auth.models import User

class PublicationResource(resources.ModelResource):
#created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='username')
#updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='username')
Expand Down
14 changes: 13 additions & 1 deletion publications/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from rest_framework_gis import serializers
from .models import Publication
from django.contrib.auth import get_user_model
User = get_user_model()

from publications.models import Publication,Subscription

Expand All @@ -23,4 +25,14 @@ class Meta:
fields = ("search_term","timeperiod_startdate","timeperiod_enddate","user_name")
geo_field = "search_area"
auto_bbox = True


class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "username", "email"]

def to_representation(self, instance):
"""Ensure deleted users are excluded from serialization."""
if instance.deleted:
return None
return super().to_representation(instance)
1 change: 1 addition & 0 deletions publications/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
User = get_user_model()
from django.conf import settings
from django.db.models.signals import post_save
User = get_user_model()
Expand Down
104 changes: 54 additions & 50 deletions publications/templates/base.html
Original file line number Diff line number Diff line change
@@ -1,53 +1,57 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>{% block title %}{% endblock %}OPTIMAP</title>
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}">

<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}"/>
<link rel="stylesheet" href="{% static 'fontawesome/css/fontawesome.min.css' %}"/>
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.min.css' %}"/>

<script src="{% static 'js/jquery-3.4.1.slim.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>

{% block head %}{% endblock %}

<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}"/>
</head>

<body>

<!--Navbar-->
<nav class="navbar navbar-expand-sm navbar-dark nav-fill w-100">
<div class="container-fluid">
<div class="nav navbar-nav">
<a class="navbar-brand" href="/">
<img src="{% static 'optimap_logo_w.svg' %}" width="100" alt="OPTIMAP logo">
</a>

<span class="text-white tagline">Discover Research. Optimized. On a map.</span>
</div>

{% block navbar %}{% endblock %}
</div>
</nav>

<main class="container-fluid">
{% block alert %}{% endblock %}

{% block content %}{% endblock %}
</main>

{% include 'footer.html' %}

{% block scripts %}{% endblock %}

</body>

</html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>{% block title %}{% endblock %}OPTIMAP</title>
<link rel="icon" type="image/png" href="{% static 'favicon.png' %}" />

<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}" />
<link
rel="stylesheet"
href="{% static 'fontawesome/css/fontawesome.min.css' %}"
/>
<link
rel="stylesheet"
href="{% static 'fontawesome/css/solid.min.css' %}"
/>

<script src="{% static 'js/jquery-3.4.1.slim.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>

{% block head %}{% endblock %}

<link rel="stylesheet" type="text/css" href="{% static 'css/main.css' %}" />
</head>

<body>
<!--Navbar-->
<nav class="navbar navbar-expand-sm navbar-dark nav-fill w-100">
<div class="container-fluid">
<div class="nav navbar-nav">
<a class="navbar-brand" href="/">
<img
src="{% static 'optimap_logo_w.svg' %}"
width="100"
alt="OPTIMAP logo"
/>
</a>

<span class="text-white tagline"
>Discover Research. Optimized. On a map.</span
>
</div>

{% block navbar %}{% endblock %}
</div>
</nav>

<main class="container-fluid">
{% block alert %}{% endblock %} {% block content %}{% endblock %}
</main>

{% include 'footer.html' %} {% block scripts %}{% endblock %}
</body>
</html>
58 changes: 37 additions & 21 deletions publications/templates/menu_snippet.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
<li class="nav-item dropdown">
<a class="nav-link dropdown" href="#" id="navbarDarkDropdown1" role="button" data-toggle="dropdown"
aria-expanded="false">
<i class="fa-solid fa-circle-user fa-3x"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDarkDropdown1">
<span class="dropdown-item-text">New around here? Please login to create a new account.</span>
<div class="dropdown-divider"></div>
<li class="px-3 py-2">
<form class="form" method="POST" action="{% url 'optimap:loginres' %}">
{% csrf_token %}
<div class="form-group">
<input id="email" placeholder="Email" class="form-control form-control-sm" type="email" required=""
name="email">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Login</button>
</div>
</form>
</li>
</ul>
<a
class="nav-link dropdown-toggle"
href="#"
id="navbarDarkDropdown1"
role="button"
data-toggle="dropdown"
aria-expanded="false"
>
<i class="fa-solid fa-circle-user fa-3x"></i>
</a>
<ul
class="dropdown-menu dropdown-menu-right"
aria-labelledby="navbarDarkDropdown1"
>
<span class="dropdown-item-text"
>New around here? Please login to create a new account.</span
>
<div class="dropdown-divider"></div>
<li class="px-3 py-2">
<form class="form" method="POST" action="{% url 'optimap:loginres' %}">
{% csrf_token %}
<div class="form-group">
<input
id="email"
placeholder="Email"
class="form-control form-control-sm"
type="email"
required=""
name="email"
/>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Login</button>
</div>
</form>
</li>
</ul>
</li>

59 changes: 56 additions & 3 deletions publications/templates/user_settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ <h2 class="mb-0">
data-parent="#user_settings"
>
<div class="card-body">
<p>Deleting account is permenant. It cannot be reversed.</p>
<p class="text-wrap text-break text-center mb-3">
Deleting account is permanent. It cannot be reversed.
</p>

<button
type="button"
Expand Down Expand Up @@ -191,7 +193,10 @@ <h5 class="modal-title">Delete Account</h5>
<p>Do you really want to delete this account ?</p>
</div>
<div class="modal-footer">
<form action="{% url 'optimap:delete' %}" method="post">
<form
action="{% url 'optimap:request_delete' %}"
method="post"
>
{% csrf_token %}
<button
type="submit"
Expand All @@ -214,14 +219,62 @@ <h5 class="modal-title">Delete Account</h5>
</div>
</div>
</div>
<!-- Final Confirmation Modal (Only Shows After Clicking Email Link) -->
<div id="finalDeleteModal" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Final Confirmation</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="container">
<p class="text-wrap text-center mb-3">
Confirming this will permenantly delete your account.
</p>
</div>
<div class="modal-footer">
<form
method="POST"
action="{% url 'optimap:finalize_delete' %}"
>
{% csrf_token %}
<button type="submit" class="btn btn-danger">
Permanently Delete Account
</button>
</form>
<button
type="button"
class="btn btn-secondary"
data-dismiss="modal"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% if delete_token %}
<script>
document.addEventListener("DOMContentLoaded", function () {
let modalShown = sessionStorage.getItem("modal_shown");
if (!modalShown) {
$("#finalDeleteModal").modal("show");
sessionStorage.setItem("modal_shown", "true");
}
});
</script>
{% endif %}
<script>
document.addEventListener("DOMContentLoaded", function () {
let toggle = document.getElementById("notify_new_manuscripts");

if ("{{ profile.notify_new_manuscripts }}" === "True") {
toggle.checked = true;
} else {
Expand Down
4 changes: 3 additions & 1 deletion publications/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
path("usersettings/", views.user_settings, name="usersettings"),
path("subscriptions/", views.user_subscriptions, name="subscriptions"),
path("addsubscriptions/", views.add_subscriptions, name="addsubscriptions"),
path("delete/", views.delete_account, name="delete"),
path("request-delete/", views.request_delete, name="request_delete"),
path("confirm-delete/<str:token>/", views.confirm_account_deletion, name="confirm_delete"),
path("finalize-delete/", views.finalize_account_deletion, name="finalize_delete"),
path("changeuser/", views.change_useremail, name="changeuser"),
]
Loading
Loading