From e9dff746b94264b9f0b447756004294a789b88c6 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 17 Dec 2024 15:36:00 +0000
Subject: [PATCH 01/22] adapting admin_email/admin.py

---
 django/admin_email/admin.py | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index aa647e3e..b5fc57cd 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -2,6 +2,7 @@ from django.contrib import admin
 from django_mail_admin.models import OutgoingEmail
 from django_mail_admin.admin import OutgoingEmailAdmin, LogInline
 from django.core.mail import EmailMessage
+from django_nano_account.models import User
 from .forms import CustomOutgoingEmailAdminForm
 import logging
 
@@ -24,15 +25,18 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
         """
         # if email has not already been sent, attempt to send it
         if obj.status != 0:
-
-            email = EmailMessage(
-                subject=obj.subject,
-                body=obj.message,
-                from_email=obj.from_email,
-                to=obj.to,
-                cc=obj.cc,
-                bcc=obj.bcc
-            )
+            # Filter users who have opted in
+            recipients = User.objects.filter(email__in=obj.to, email_opt_in=True).values_list('email', flat=True)
+            
+            if recipients:
+                email = EmailMessage(
+                    subject=obj.subject,
+                    body=obj.message,
+                    from_email=obj.from_email,
+                    to=recipients,
+                    cc=obj.cc,
+                    bcc=obj.bcc
+                )
 
             try:
                 email.send(fail_silently=False)
-- 
GitLab


From cea620b367574b94b2c0baa134cd3734f915f32b Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Mon, 13 Jan 2025 15:59:42 +0000
Subject: [PATCH 02/22] add button to send to all mail subscribers

---
 django/admin_email/admin.py | 32 ++++++++++++++++++++------------
 django/admin_email/forms.py |  5 ++++-
 2 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index b5fc57cd..c911ff4c 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -21,13 +21,20 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
 
     def save_model(self, request, obj, form, change):
         """
-        Override method to send email (instead of queuing) on saving
+        Override method to send email (instead of queuing) on saving.
         """
-        # if email has not already been sent, attempt to send it
+        # If email has not already been sent, attempt to send it
         if obj.status != 0:
-            # Filter users who have opted in
-            recipients = User.objects.filter(email__in=obj.to, email_opt_in=True).values_list('email', flat=True)
-            
+            send_to_opt_in_users = form.cleaned_data.get('send_to_opt_in_users', False)
+
+            # Determine recipients based on the checkbox
+            if send_to_opt_in_users:
+                # Send to all users who opted in
+                recipients = User.objects.filter(email_opt_in=True).values_list('email', flat=True)
+            else:
+                # Send to the specific recipients in `obj.to`
+                recipients = User.objects.filter(email__in=obj.to, email_opt_in=True).values_list('email', flat=True)
+
             if recipients:
                 email = EmailMessage(
                     subject=obj.subject,
@@ -38,14 +45,15 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                     bcc=obj.bcc
                 )
 
-            try:
-                email.send(fail_silently=False)
-                obj.status = 0  # sent
-            except Exception as e:
-                obj.status = 1  # failed
-                super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
-                logger.exception(e)
+                try:
+                    # Attempt to send the email
+                    email.send(fail_silently=False)
+                    obj.status = 0  # Mark as sent
+                except Exception as e:
+                    obj.status = 1  # Mark as failed
+                    logger.exception(f"Failed to send email: {e}")
 
+        # Save the updated status and ensure default behavior is invoked
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
 
diff --git a/django/admin_email/forms.py b/django/admin_email/forms.py
index 1de752b0..c7dbf097 100644
--- a/django/admin_email/forms.py
+++ b/django/admin_email/forms.py
@@ -1,4 +1,5 @@
 from django_mail_admin.forms import OutgoingEmailAdminForm
+from django import forms
 from django.conf import settings
 
 
@@ -6,8 +7,10 @@ class CustomOutgoingEmailAdminForm(OutgoingEmailAdminForm):
     """
     Customised form inherited from `django_mail_admin.forms.OutgoingEmailAdminForm`
     """
+    send_to_opt_in_users = forms.BooleanField(required=False, label="Send to All Mailing List Subscribers")
+
     class Meta(OutgoingEmailAdminForm.Meta):
-        fields = ['from_email', 'to', 'cc', 'bcc', 'subject', 'message']
+        fields = ['from_email', 'to', 'cc', 'bcc', 'subject', 'message', 'send_to_opt_in_users']
 
     def __init__(self, *args, **kwargs):
         """
-- 
GitLab


From 0c3573f58ca25ef828a743db6edb7bff1c6f1736 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 14 Jan 2025 09:36:12 +0000
Subject: [PATCH 03/22] Added unsubscribe and email template

---
 django/admin_email/admin.py               | 28 ++++++++++-------------
 django/admin_email/forms.py               |  2 +-
 django/admin_email/urls.py                |  6 +++++
 django/admin_email/views.py               | 10 ++++++++
 django/core/templates/email_template.html |  5 ++++
 django/core/urls.py                       |  1 +
 6 files changed, 35 insertions(+), 17 deletions(-)
 create mode 100644 django/admin_email/urls.py
 create mode 100644 django/admin_email/views.py
 create mode 100644 django/core/templates/email_template.html

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index c911ff4c..ffb48d2e 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -4,6 +4,8 @@ from django_mail_admin.admin import OutgoingEmailAdmin, LogInline
 from django.core.mail import EmailMessage
 from django_nano_account.models import User
 from .forms import CustomOutgoingEmailAdminForm
+from django.template.loader import render_to_string
+
 import logging
 
 logger = logging.getLogger(__name__)
@@ -20,40 +22,34 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
     inlines = (LogInline,)
 
     def save_model(self, request, obj, form, change):
-        """
-        Override method to send email (instead of queuing) on saving.
-        """
-        # If email has not already been sent, attempt to send it
         if obj.status != 0:
             send_to_opt_in_users = form.cleaned_data.get('send_to_opt_in_users', False)
-
-            # Determine recipients based on the checkbox
             if send_to_opt_in_users:
-                # Send to all users who opted in
                 recipients = User.objects.filter(email_opt_in=True).values_list('email', flat=True)
             else:
-                # Send to the specific recipients in `obj.to`
-                recipients = User.objects.filter(email__in=obj.to, email_opt_in=True).values_list('email', flat=True)
+                recipients = obj.to if obj.to else []
+
+            for recipient in recipients:
+                email_body_with_unsubscribe = render_to_string('email_template.html', {
+                    'email_body': obj.message,
+                    'unsubscribe_url': request.build_absolute_uri(f'/unsubscribe/{recipient}')
+                })
 
-            if recipients:
                 email = EmailMessage(
                     subject=obj.subject,
-                    body=obj.message,
+                    body=email_body_with_unsubscribe,
                     from_email=obj.from_email,
-                    to=recipients,
+                    to=[recipient],  # Wrap recipient in a list
                     cc=obj.cc,
                     bcc=obj.bcc
                 )
 
                 try:
-                    # Attempt to send the email
                     email.send(fail_silently=False)
                     obj.status = 0  # Mark as sent
                 except Exception as e:
                     obj.status = 1  # Mark as failed
-                    logger.exception(f"Failed to send email: {e}")
-
-        # Save the updated status and ensure default behavior is invoked
+                    logger.exception(f"Failed to send email to {recipient}: {e}")
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
 
diff --git a/django/admin_email/forms.py b/django/admin_email/forms.py
index c7dbf097..5242769d 100644
--- a/django/admin_email/forms.py
+++ b/django/admin_email/forms.py
@@ -10,7 +10,7 @@ class CustomOutgoingEmailAdminForm(OutgoingEmailAdminForm):
     send_to_opt_in_users = forms.BooleanField(required=False, label="Send to All Mailing List Subscribers")
 
     class Meta(OutgoingEmailAdminForm.Meta):
-        fields = ['from_email', 'to', 'cc', 'bcc', 'subject', 'message', 'send_to_opt_in_users']
+        fields = ['from_email', 'to', 'send_to_opt_in_users', 'cc', 'bcc', 'subject', 'message']
 
     def __init__(self, *args, **kwargs):
         """
diff --git a/django/admin_email/urls.py b/django/admin_email/urls.py
new file mode 100644
index 00000000..e02a8e6d
--- /dev/null
+++ b/django/admin_email/urls.py
@@ -0,0 +1,6 @@
+from django.urls import path
+from . import views
+
+urlpatterns = [
+    path('<str:email>/', views.unsubscribe, name='unsubscribe'),
+]
diff --git a/django/admin_email/views.py b/django/admin_email/views.py
new file mode 100644
index 00000000..df5ec8c7
--- /dev/null
+++ b/django/admin_email/views.py
@@ -0,0 +1,10 @@
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
+from django_nano_account.models import User
+
+
+def unsubscribe(request, email):
+    user = get_object_or_404(User, email=email)
+    user.email_opt_in = False
+    user.save()
+    return HttpResponse(f"You have successfully unsubscribed {email}.")
diff --git a/django/core/templates/email_template.html b/django/core/templates/email_template.html
new file mode 100644
index 00000000..8a10b5ea
--- /dev/null
+++ b/django/core/templates/email_template.html
@@ -0,0 +1,5 @@
+<!-- email_template.html -->
+<p>{{ email_body }}</p>
+<p>
+    <a href="{{ unsubscribe_url }}">Click here to unsubscribe</a>
+</p>
diff --git a/django/core/urls.py b/django/core/urls.py
index 0e790f78..bb1644d4 100644
--- a/django/core/urls.py
+++ b/django/core/urls.py
@@ -11,6 +11,7 @@ urlpatterns = [
     path('upload/', include('microscopy_data.urls')),
     path('analysis/', include('hpcsubmit.urls')),
     path("contact/", include("django_contact_form.urls")),
+    path('unsubscribe/', include('admin_email.urls')),
 ] + static(
     settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
 )
-- 
GitLab


From 2589387ee35b7c930d201295ab0fb1b23b0140aa Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 14 Jan 2025 10:04:58 +0000
Subject: [PATCH 04/22] improve email template

---
 django/core/templates/email_template.html | 104 ++++++++++++++++++++--
 1 file changed, 99 insertions(+), 5 deletions(-)

diff --git a/django/core/templates/email_template.html b/django/core/templates/email_template.html
index 8a10b5ea..f256c420 100644
--- a/django/core/templates/email_template.html
+++ b/django/core/templates/email_template.html
@@ -1,5 +1,99 @@
-<!-- email_template.html -->
-<p>{{ email_body }}</p>
-<p>
-    <a href="{{ unsubscribe_url }}">Click here to unsubscribe</a>
-</p>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>email_template</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <style>
+        body {
+            min-height: 100vh;
+            background: white;
+            font-family: 'Didact Gothic', sans-serif;
+            color: black;
+            display: grid;
+            grid-template-rows: auto 1fr auto;
+        }
+        .container {
+            width: 100%;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+            background-color: #f9f9f9;
+            border: 1px solid #ddd;
+            border-radius: 5px;
+        }
+        .header, .footer {
+            background-color: #007BFF;
+            color: #fff;
+            text-align: center;
+            padding: 10px 0;
+        }
+        .content {
+            margin-bottom: 20px;
+        }
+        .unsubscribe {
+            text-align: center;
+            margin-top: 20px;
+        }
+        .unsubscribe a {
+            color: #007BFF;
+            text-decoration: none;
+        }
+        .unsubscribe a:hover {
+            text-decoration: underline;
+        }
+        a {
+            color: #714316;
+            text-decoration: underline;
+        }
+        a:hover {
+            color: #7d2604;
+        }
+        h1, h2 {
+            color: #995c38;
+        }
+        footer {
+            width: 100%;
+            height: auto;
+            line-height: 20px;
+            padding-top: 20px;
+            text-align: center;
+            color: #482b0e;
+            background-color: #fdb44b;
+            z-index: 1000;
+        }
+        footer a {
+            color: #4a3212;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="content">
+            {{ email_body }}
+        </div>
+        <div class="unsubscribe">
+            <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: #fff; background-color: #007BFF; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
+        </div>
+        <br>
+        <footer id="footer" role="contentinfo">
+            <p>
+                Developed by the
+                <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
+                at the
+                <a href="https://www.bham.ac.uk">University of Birmingham</a>
+            </p>
+            <p>
+                <a href="{% url 'general:user_agreement' %}">User Agreement</a> |
+                <a href="{% url 'django_contact_form' %}">Contact us</a> |
+                <a href="https://www.birmingham.ac.uk/privacy/index.aspx" target="_blank">Privacy</a> |
+                <a href="https://www.birmingham.ac.uk/legal/index.aspx" target="_blank">Legal</a> |
+                <a href="https://www.birmingham.ac.uk/university/governance/policies-regs/information/index.aspx" target="_blank">Freedom of Information</a> |
+                <a href="{% url 'general:cookies' %}">Cookies</a> |
+                <a href="https://accessibility.bear.bham.ac.uk/statements-nano-org.html" target="_blank">Accessibility</a>
+            </p>
+        </footer>
+    </div>
+</body>
+</html>
\ No newline at end of file
-- 
GitLab


From 43586e5d5a8fbadf1ca992284f67912dc9081973 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 14 Jan 2025 10:12:22 +0000
Subject: [PATCH 05/22] tried to add Unsubscribe Confirmation page

---
 django/admin_email/views.py                   |  4 ++--
 .../templates/unsubscribe_confirmation.html   | 21 +++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)
 create mode 100644 django/core/templates/unsubscribe_confirmation.html

diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index df5ec8c7..0c4fc8ee 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,5 +1,5 @@
 from django.http import HttpResponse
-from django.shortcuts import get_object_or_404
+from django.shortcuts import render, get_object_or_404
 from django_nano_account.models import User
 
 
@@ -7,4 +7,4 @@ def unsubscribe(request, email):
     user = get_object_or_404(User, email=email)
     user.email_opt_in = False
     user.save()
-    return HttpResponse(f"You have successfully unsubscribed {email}.")
+    return render(request, 'unsubscribe_confirmation.html', {'email': email})
diff --git a/django/core/templates/unsubscribe_confirmation.html b/django/core/templates/unsubscribe_confirmation.html
new file mode 100644
index 00000000..a4203b04
--- /dev/null
+++ b/django/core/templates/unsubscribe_confirmation.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Unsubscribe Confirmation</title>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
+    <link rel="stylesheet" href="{% static 'css/style.css' %}">
+</head>
+<body>
+    <div class="container mt-5">
+        <div class="card">
+            <div class="card-body text-center">
+                <h1 class="card-title">Unsubscribe Successful</h1>
+                <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
+                
+            </div>
+        </div>
+    </div>
+</body>
+</html>
\ No newline at end of file
-- 
GitLab


From 20667857a320c516b377fde937aa03b1af6f0d1d Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 14 Jan 2025 14:55:53 +0000
Subject: [PATCH 06/22] update django-n-account dir

---
 django-nano-account | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/django-nano-account b/django-nano-account
index 9596f67f..722fc739 160000
--- a/django-nano-account
+++ b/django-nano-account
@@ -1 +1 @@
-Subproject commit 9596f67f87ba3cc7eaa13b7ba404aa431c349848
+Subproject commit 722fc739630ffa72bcd2b2687f72424263052e8d
-- 
GitLab


From 17eb6e0c32defbbfef0f87bb38ce93b259dafcf5 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Tue, 14 Jan 2025 17:27:35 +0000
Subject: [PATCH 07/22] fixing the html email rendering

---
 django/admin_email/admin.py | 8 +++++---
 django/admin_email/views.py | 1 -
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index ffb48d2e..74d0b0fb 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -1,7 +1,7 @@
 from django.contrib import admin
 from django_mail_admin.models import OutgoingEmail
 from django_mail_admin.admin import OutgoingEmailAdmin, LogInline
-from django.core.mail import EmailMessage
+from django.core.mail import EmailMultiAlternatives
 from django_nano_account.models import User
 from .forms import CustomOutgoingEmailAdminForm
 from django.template.loader import render_to_string
@@ -35,15 +35,17 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                     'unsubscribe_url': request.build_absolute_uri(f'/unsubscribe/{recipient}')
                 })
 
-                email = EmailMessage(
+                email = EmailMultiAlternatives(
                     subject=obj.subject,
                     body=email_body_with_unsubscribe,
                     from_email=obj.from_email,
-                    to=[recipient],  # Wrap recipient in a list
+                    to=[recipient],
                     cc=obj.cc,
                     bcc=obj.bcc
                 )
 
+                email.attach_alternative(email_body_with_unsubscribe, "text/html")
+
                 try:
                     email.send(fail_silently=False)
                     obj.status = 0  # Mark as sent
diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index 0c4fc8ee..1c18391e 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,4 +1,3 @@
-from django.http import HttpResponse
 from django.shortcuts import render, get_object_or_404
 from django_nano_account.models import User
 
-- 
GitLab


From 12df383be563a7005ca3581c59a84fed1b89c25b Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 15:47:16 +0000
Subject: [PATCH 08/22] add template for direct email and try to fix
 unsubscribe page

---
 django/admin_email/admin.py                   | 19 ++--
 django/admin_email/views.py                   | 11 ++-
 django/core/templates/email_template.html     | 14 ---
 .../email_template_for_mail_list.html         | 99 +++++++++++++++++++
 4 files changed, 122 insertions(+), 21 deletions(-)
 create mode 100644 django/core/templates/email_template_for_mail_list.html

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 74d0b0fb..9061f1bc 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -5,6 +5,7 @@ from django.core.mail import EmailMultiAlternatives
 from django_nano_account.models import User
 from .forms import CustomOutgoingEmailAdminForm
 from django.template.loader import render_to_string
+from urllib.parse import quote
 
 import logging
 
@@ -26,25 +27,31 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
             send_to_opt_in_users = form.cleaned_data.get('send_to_opt_in_users', False)
             if send_to_opt_in_users:
                 recipients = User.objects.filter(email_opt_in=True).values_list('email', flat=True)
+                template_name = 'email_template_for_mail_list.html'
             else:
                 recipients = obj.to if obj.to else []
+                template_name = 'email_template.html'
 
             for recipient in recipients:
-                email_body_with_unsubscribe = render_to_string('email_template.html', {
-                    'email_body': obj.message,
-                    'unsubscribe_url': request.build_absolute_uri(f'/unsubscribe/{recipient}')
-                })
+                context = {'email_body': obj.message}
+
+                # Add unsubscribe URL only if send_to_opt_in_users is True
+                if send_to_opt_in_users:
+                    context['unsubscribe_url'] = request.build_absolute_uri(f'/unsubscribe/{quote(recipient)}/')
+
+                # Render the email body using the selected template
+                email_body_final = render_to_string(template_name, context)
 
                 email = EmailMultiAlternatives(
                     subject=obj.subject,
-                    body=email_body_with_unsubscribe,
+                    body=obj.message,
                     from_email=obj.from_email,
                     to=[recipient],
                     cc=obj.cc,
                     bcc=obj.bcc
                 )
 
-                email.attach_alternative(email_body_with_unsubscribe, "text/html")
+                email.attach_alternative(email_body_final, "text/html")
 
                 try:
                     email.send(fail_silently=False)
diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index 1c18391e..39f8cd11 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,9 +1,18 @@
 from django.shortcuts import render, get_object_or_404
 from django_nano_account.models import User
+from django.http import Http404
+from urllib.parse import unquote
 
 
 def unsubscribe(request, email):
-    user = get_object_or_404(User, email=email)
+    email = unquote(email)
+
+    try:
+        user = User.objects.get(email=email)
+    except User.DoesNotExist:
+        raise Http404(f"No user found with email {email}")
+
     user.email_opt_in = False
     user.save()
     return render(request, 'unsubscribe_confirmation.html', {'email': email})
+
diff --git a/django/core/templates/email_template.html b/django/core/templates/email_template.html
index f256c420..cc379dcf 100644
--- a/django/core/templates/email_template.html
+++ b/django/core/templates/email_template.html
@@ -32,17 +32,6 @@
         .content {
             margin-bottom: 20px;
         }
-        .unsubscribe {
-            text-align: center;
-            margin-top: 20px;
-        }
-        .unsubscribe a {
-            color: #007BFF;
-            text-decoration: none;
-        }
-        .unsubscribe a:hover {
-            text-decoration: underline;
-        }
         a {
             color: #714316;
             text-decoration: underline;
@@ -73,9 +62,6 @@
         <div class="content">
             {{ email_body }}
         </div>
-        <div class="unsubscribe">
-            <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: #fff; background-color: #007BFF; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
-        </div>
         <br>
         <footer id="footer" role="contentinfo">
             <p>
diff --git a/django/core/templates/email_template_for_mail_list.html b/django/core/templates/email_template_for_mail_list.html
new file mode 100644
index 00000000..f256c420
--- /dev/null
+++ b/django/core/templates/email_template_for_mail_list.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>email_template</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <style>
+        body {
+            min-height: 100vh;
+            background: white;
+            font-family: 'Didact Gothic', sans-serif;
+            color: black;
+            display: grid;
+            grid-template-rows: auto 1fr auto;
+        }
+        .container {
+            width: 100%;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+            background-color: #f9f9f9;
+            border: 1px solid #ddd;
+            border-radius: 5px;
+        }
+        .header, .footer {
+            background-color: #007BFF;
+            color: #fff;
+            text-align: center;
+            padding: 10px 0;
+        }
+        .content {
+            margin-bottom: 20px;
+        }
+        .unsubscribe {
+            text-align: center;
+            margin-top: 20px;
+        }
+        .unsubscribe a {
+            color: #007BFF;
+            text-decoration: none;
+        }
+        .unsubscribe a:hover {
+            text-decoration: underline;
+        }
+        a {
+            color: #714316;
+            text-decoration: underline;
+        }
+        a:hover {
+            color: #7d2604;
+        }
+        h1, h2 {
+            color: #995c38;
+        }
+        footer {
+            width: 100%;
+            height: auto;
+            line-height: 20px;
+            padding-top: 20px;
+            text-align: center;
+            color: #482b0e;
+            background-color: #fdb44b;
+            z-index: 1000;
+        }
+        footer a {
+            color: #4a3212;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="content">
+            {{ email_body }}
+        </div>
+        <div class="unsubscribe">
+            <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: #fff; background-color: #007BFF; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
+        </div>
+        <br>
+        <footer id="footer" role="contentinfo">
+            <p>
+                Developed by the
+                <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
+                at the
+                <a href="https://www.bham.ac.uk">University of Birmingham</a>
+            </p>
+            <p>
+                <a href="{% url 'general:user_agreement' %}">User Agreement</a> |
+                <a href="{% url 'django_contact_form' %}">Contact us</a> |
+                <a href="https://www.birmingham.ac.uk/privacy/index.aspx" target="_blank">Privacy</a> |
+                <a href="https://www.birmingham.ac.uk/legal/index.aspx" target="_blank">Legal</a> |
+                <a href="https://www.birmingham.ac.uk/university/governance/policies-regs/information/index.aspx" target="_blank">Freedom of Information</a> |
+                <a href="{% url 'general:cookies' %}">Cookies</a> |
+                <a href="https://accessibility.bear.bham.ac.uk/statements-nano-org.html" target="_blank">Accessibility</a>
+            </p>
+        </footer>
+    </div>
+</body>
+</html>
\ No newline at end of file
-- 
GitLab


From e889b7dfdbf25c2937a14510afe80dda2a8d732c Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 16:06:00 +0000
Subject: [PATCH 09/22] update unsub page and delete unused import

---
 django/admin_email/views.py                   |  3 +-
 .../templates/unsubscribe_confirmation.html   | 29 +++++++------------
 2 files changed, 11 insertions(+), 21 deletions(-)

diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index 39f8cd11..7164487d 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,4 +1,4 @@
-from django.shortcuts import render, get_object_or_404
+from django.shortcuts import render
 from django_nano_account.models import User
 from django.http import Http404
 from urllib.parse import unquote
@@ -15,4 +15,3 @@ def unsubscribe(request, email):
     user.email_opt_in = False
     user.save()
     return render(request, 'unsubscribe_confirmation.html', {'email': email})
-
diff --git a/django/core/templates/unsubscribe_confirmation.html b/django/core/templates/unsubscribe_confirmation.html
index a4203b04..a1eef99f 100644
--- a/django/core/templates/unsubscribe_confirmation.html
+++ b/django/core/templates/unsubscribe_confirmation.html
@@ -1,21 +1,12 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Unsubscribe Confirmation</title>
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
-    <link rel="stylesheet" href="{% static 'css/style.css' %}">
-</head>
-<body>
-    <div class="container mt-5">
-        <div class="card">
-            <div class="card-body text-center">
-                <h1 class="card-title">Unsubscribe Successful</h1>
-                <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
-                
-            </div>
+{% extends "base.html" %}
+
+{% block content %}
+<div class="container mt-5">
+    <div class="card">
+        <div class="card-body text-center">
+            <h1 class="card-title">Unsubscribe Successful</h1>
+            <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
         </div>
     </div>
-</body>
-</html>
\ No newline at end of file
+</div>
+{% endblock %}
-- 
GitLab


From fca3e7cc7481ff6d34495819520d90b2f91d560b Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 16:17:06 +0000
Subject: [PATCH 10/22] correcting html indents for unsub

---
 .../core/templates/unsubscribe_confirmation.html | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/django/core/templates/unsubscribe_confirmation.html b/django/core/templates/unsubscribe_confirmation.html
index a1eef99f..aa625669 100644
--- a/django/core/templates/unsubscribe_confirmation.html
+++ b/django/core/templates/unsubscribe_confirmation.html
@@ -1,12 +1,12 @@
 {% extends "base.html" %}
 
-{% block content %}
-<div class="container mt-5">
-    <div class="card">
-        <div class="card-body text-center">
-            <h1 class="card-title">Unsubscribe Successful</h1>
-            <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
+    {% block content %}
+        <div class="container mt-5">
+            <div class="card">
+                <div class="card-body text-center">
+                    <h1 class="card-title">Unsubscribe Successful</h1>
+                    <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
+                </div>
+            </div>
         </div>
-    </div>
-</div>
 {% endblock %}
-- 
GitLab


From bdd967a2978eba61f7aa77a2feea8d65e369555d Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 16:27:31 +0000
Subject: [PATCH 11/22] update django-nano-account

---
 django-nano-account | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/django-nano-account b/django-nano-account
index 722fc739..a42b9816 160000
--- a/django-nano-account
+++ b/django-nano-account
@@ -1 +1 @@
-Subproject commit 722fc739630ffa72bcd2b2687f72424263052e8d
+Subproject commit a42b9816b51d2d85dfe5df44d79671e56c2393d0
-- 
GitLab


From 3e4c2f35df85afa4846cf851b2bc4d068788b152 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 16:29:30 +0000
Subject: [PATCH 12/22] try identation unsub html

---
 .../core/templates/unsubscribe_confirmation.html | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/django/core/templates/unsubscribe_confirmation.html b/django/core/templates/unsubscribe_confirmation.html
index aa625669..a1eef99f 100644
--- a/django/core/templates/unsubscribe_confirmation.html
+++ b/django/core/templates/unsubscribe_confirmation.html
@@ -1,12 +1,12 @@
 {% extends "base.html" %}
 
-    {% block content %}
-        <div class="container mt-5">
-            <div class="card">
-                <div class="card-body text-center">
-                    <h1 class="card-title">Unsubscribe Successful</h1>
-                    <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
-                </div>
-            </div>
+{% block content %}
+<div class="container mt-5">
+    <div class="card">
+        <div class="card-body text-center">
+            <h1 class="card-title">Unsubscribe Successful</h1>
+            <p class="card-text">You have successfully unsubscribed {{ email }}.</p>
         </div>
+    </div>
+</div>
 {% endblock %}
-- 
GitLab


From 88dcb268e86dc8f208396ae056e32eb963cb6a94 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 17:03:02 +0000
Subject: [PATCH 13/22] Update to consider linebreaks in mail

---
 django/admin_email/admin.py                             | 3 ++-
 django/core/templates/email_template.html               | 2 +-
 django/core/templates/email_template_for_mail_list.html | 2 +-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 9061f1bc..870b7825 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -2,6 +2,7 @@ from django.contrib import admin
 from django_mail_admin.models import OutgoingEmail
 from django_mail_admin.admin import OutgoingEmailAdmin, LogInline
 from django.core.mail import EmailMultiAlternatives
+from django.utils.html import linebreaks
 from django_nano_account.models import User
 from .forms import CustomOutgoingEmailAdminForm
 from django.template.loader import render_to_string
@@ -33,7 +34,7 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                 template_name = 'email_template.html'
 
             for recipient in recipients:
-                context = {'email_body': obj.message}
+                context = {'email_body': linebreaks(obj.message)}
 
                 # Add unsubscribe URL only if send_to_opt_in_users is True
                 if send_to_opt_in_users:
diff --git a/django/core/templates/email_template.html b/django/core/templates/email_template.html
index cc379dcf..be169388 100644
--- a/django/core/templates/email_template.html
+++ b/django/core/templates/email_template.html
@@ -60,7 +60,7 @@
 <body>
     <div class="container">
         <div class="content">
-            {{ email_body }}
+            {{ email_body|linebreaks }}
         </div>
         <br>
         <footer id="footer" role="contentinfo">
diff --git a/django/core/templates/email_template_for_mail_list.html b/django/core/templates/email_template_for_mail_list.html
index f256c420..2942ed9b 100644
--- a/django/core/templates/email_template_for_mail_list.html
+++ b/django/core/templates/email_template_for_mail_list.html
@@ -71,7 +71,7 @@
 <body>
     <div class="container">
         <div class="content">
-            {{ email_body }}
+            {{ email_body|linebreaks }}
         </div>
         <div class="unsubscribe">
             <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: #fff; background-color: #007BFF; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
-- 
GitLab


From 930d8ef5d200b0ba102f5a1690c8fc571575e2ec Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 17:27:05 +0000
Subject: [PATCH 14/22] Changing email template colour and writing

---
 django/core/templates/email_template.html               | 6 +++---
 django/core/templates/email_template_for_mail_list.html | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/django/core/templates/email_template.html b/django/core/templates/email_template.html
index be169388..7ce1af78 100644
--- a/django/core/templates/email_template.html
+++ b/django/core/templates/email_template.html
@@ -24,8 +24,8 @@
             border-radius: 5px;
         }
         .header, .footer {
-            background-color: #007BFF;
-            color: #fff;
+            background-color: #fdb44b;
+            color: black;
             text-align: center;
             padding: 10px 0;
         }
@@ -65,7 +65,7 @@
         <br>
         <footer id="footer" role="contentinfo">
             <p>
-                Developed by the
+                nano-org website has been developed by the 
                 <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
                 at the
                 <a href="https://www.bham.ac.uk">University of Birmingham</a>
diff --git a/django/core/templates/email_template_for_mail_list.html b/django/core/templates/email_template_for_mail_list.html
index 2942ed9b..4b8afb93 100644
--- a/django/core/templates/email_template_for_mail_list.html
+++ b/django/core/templates/email_template_for_mail_list.html
@@ -24,8 +24,8 @@
             border-radius: 5px;
         }
         .header, .footer {
-            background-color: #007BFF;
-            color: #fff;
+            background-color: #fdb44b;
+            color: black;
             text-align: center;
             padding: 10px 0;
         }
@@ -37,7 +37,7 @@
             margin-top: 20px;
         }
         .unsubscribe a {
-            color: #007BFF;
+            color: #fdb44b;
             text-decoration: none;
         }
         .unsubscribe a:hover {
@@ -79,7 +79,7 @@
         <br>
         <footer id="footer" role="contentinfo">
             <p>
-                Developed by the
+                nano-org website has been developed by the
                 <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
                 at the
                 <a href="https://www.bham.ac.uk">University of Birmingham</a>
-- 
GitLab


From 81b8c68e3870c8168bf8e59f53f8780d5636a42e Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Wed, 15 Jan 2025 17:33:05 +0000
Subject: [PATCH 15/22] Correcting colour change

---
 django/core/templates/email_template_for_mail_list.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/django/core/templates/email_template_for_mail_list.html b/django/core/templates/email_template_for_mail_list.html
index 4b8afb93..2d88a5d4 100644
--- a/django/core/templates/email_template_for_mail_list.html
+++ b/django/core/templates/email_template_for_mail_list.html
@@ -74,7 +74,7 @@
             {{ email_body|linebreaks }}
         </div>
         <div class="unsubscribe">
-            <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: #fff; background-color: #007BFF; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
+            <a href="{{ unsubscribe_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: black; background-color: #fdb44b; text-decoration: none; border-radius: 5px;">Unsubscribe from Mailing List</a>
         </div>
         <br>
         <footer id="footer" role="contentinfo">
-- 
GitLab


From a5838e4bdbbc0a5ff7d60213d9771803c24aa542 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Thu, 16 Jan 2025 10:15:06 +0000
Subject: [PATCH 16/22] Add updated django nano account

---
 django-nano-account | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/django-nano-account b/django-nano-account
index a42b9816..f021203f 160000
--- a/django-nano-account
+++ b/django-nano-account
@@ -1 +1 @@
-Subproject commit a42b9816b51d2d85dfe5df44d79671e56c2393d0
+Subproject commit f021203f5c814f6ac9d9d722194cef12bb5a9814
-- 
GitLab


From fd2d7bc47d17a4f7a1b744882fb52cf20d48779e Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Thu, 16 Jan 2025 10:30:16 +0000
Subject: [PATCH 17/22] Initial Mailing List Sign Up Email

---
 django/admin_email/admin.py                   | 28 ++++++
 django/admin_email/urls.py                    |  1 +
 django/admin_email/views.py                   | 14 ++-
 .../templates/signup_mailing_list_email.html  | 99 +++++++++++++++++++
 django/core/urls.py                           |  2 +
 5 files changed, 143 insertions(+), 1 deletion(-)
 create mode 100644 django/core/templates/signup_mailing_list_email.html

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 870b7825..3288256d 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -62,5 +62,33 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                     logger.exception(f"Failed to send email to {recipient}: {e}")
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
+    def send_initial_sign_in_email(self, obj, request):
+        recipients = User.objects.all().values_list('email', flat=True)
+        subject = "Stay Updated with Our Latest Analysis Features"
+        template_name = 'signup_mailing_list_email.html'
+
+        for recipient in recipients:
+            context = {
+                'email_body': "Welcome to our platform! We're excited to have you on board. Click the link below to join our mailing list and stay updated with our latest features and research papers.",
+                'join_mailing_list_url': request.build_absolute_uri(f'/join_mailing_list/{quote(recipient)}/')
+            }
+
+            email_body_final = render_to_string(template_name, context)
+
+            email = EmailMultiAlternatives(
+                subject=subject,
+                body=context['email_body'],
+                from_email=obj.from_email,
+                to=[recipient]
+            )
+
+            email.attach_alternative(email_body_final, "text/html")
+
+            try:
+                email.send(fail_silently=False)
+                logger.info(f"Sent initial sign-in email to {recipient}")
+            except Exception as e:
+                logger.exception(f"Failed to send initial sign-in email to {recipient}: {e}")
+
 
 admin.site.register(OutgoingEmail, CustomOutgoingEmailAdmin)
diff --git a/django/admin_email/urls.py b/django/admin_email/urls.py
index e02a8e6d..da601503 100644
--- a/django/admin_email/urls.py
+++ b/django/admin_email/urls.py
@@ -2,5 +2,6 @@ from django.urls import path
 from . import views
 
 urlpatterns = [
+    path('join_mailing_list/<str:email>/', views.join_mailing_list, name='join_mailing_list'),
     path('<str:email>/', views.unsubscribe, name='unsubscribe'),
 ]
diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index 7164487d..533edae4 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,9 +1,21 @@
-from django.shortcuts import render
+from django.shortcuts import render, redirect
 from django_nano_account.models import User
 from django.http import Http404
 from urllib.parse import unquote
 
 
+def join_mailing_list(request, email):
+    email = unquote(email)
+
+    try:
+        user = User.objects.get(email=email)
+    except User.DoesNotExist:
+        raise Http404(f"No user found with email {email}")
+
+    user.email_opt_in = True
+    user.save()
+    return redirect('home')
+
 def unsubscribe(request, email):
     email = unquote(email)
 
diff --git a/django/core/templates/signup_mailing_list_email.html b/django/core/templates/signup_mailing_list_email.html
new file mode 100644
index 00000000..9681ff07
--- /dev/null
+++ b/django/core/templates/signup_mailing_list_email.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>mailing_list_email</title>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <style>
+        body {
+            min-height: 100vh;
+            background: white;
+            font-family: 'Didact Gothic', sans-serif;
+            color: black;
+            display: grid;
+            grid-template-rows: auto 1fr auto;
+        }
+        .container {
+            width: 100%;
+            max-width: 600px;
+            margin: 0 auto;
+            padding: 20px;
+            background-color: #f9f9f9;
+            border: 1px solid #ddd;
+            border-radius: 5px;
+        }
+        .header, .footer {
+            background-color: #fdb44b;
+            color: black;
+            text-align: center;
+            padding: 10px 0;
+        }
+        .content {
+            margin-bottom: 20px;
+        }
+        .unsubscribe {
+            text-align: center;
+            margin-top: 20px;
+        }
+        .unsubscribe a {
+            color: #fdb44b;
+            text-decoration: none;
+        }
+        .unsubscribe a:hover {
+            text-decoration: underline;
+        }
+        a {
+            color: #714316;
+            text-decoration: underline;
+        }
+        a:hover {
+            color: #7d2604;
+        }
+        h1, h2 {
+            color: #995c38;
+        }
+        footer {
+            width: 100%;
+            height: auto;
+            line-height: 20px;
+            padding-top: 20px;
+            text-align: center;
+            color: #482b0e;
+            background-color: #fdb44b;
+            z-index: 1000;
+        }
+        footer a {
+            color: #4a3212;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <div class="content">
+            {{ email_body|linebreaks }}
+        </div>
+        <div class="join_mailing_list">
+            <a href="{{ join_mailing_list_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: black; background-color: #fdb44b; text-decoration: none; border-radius: 5px;">Join our Mailing List</a>
+        </div>
+        <br>
+        <footer id="footer" role="contentinfo">
+            <p>
+                nano-org website has been developed by the
+                <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
+                at the
+                <a href="https://www.bham.ac.uk">University of Birmingham</a>
+            </p>
+            <p>
+                <a href="{% url 'general:user_agreement' %}">User Agreement</a> |
+                <a href="{% url 'django_contact_form' %}">Contact us</a> |
+                <a href="https://www.birmingham.ac.uk/privacy/index.aspx" target="_blank">Privacy</a> |
+                <a href="https://www.birmingham.ac.uk/legal/index.aspx" target="_blank">Legal</a> |
+                <a href="https://www.birmingham.ac.uk/university/governance/policies-regs/information/index.aspx" target="_blank">Freedom of Information</a> |
+                <a href="{% url 'general:cookies' %}">Cookies</a> |
+                <a href="https://accessibility.bear.bham.ac.uk/statements-nano-org.html" target="_blank">Accessibility</a>
+            </p>
+        </footer>
+    </div>
+</body>
+</html>
\ No newline at end of file
diff --git a/django/core/urls.py b/django/core/urls.py
index bb1644d4..71b2c60a 100644
--- a/django/core/urls.py
+++ b/django/core/urls.py
@@ -12,6 +12,8 @@ urlpatterns = [
     path('analysis/', include('hpcsubmit.urls')),
     path("contact/", include("django_contact_form.urls")),
     path('unsubscribe/', include('admin_email.urls')),
+    path('join_mailing_list/', include('admin_email.urls')),
+
 ] + static(
     settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
 )
-- 
GitLab


From de4f71f0b148a8c32519c33674f4c75399276137 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Thu, 16 Jan 2025 14:46:36 +0000
Subject: [PATCH 18/22] added subscription successful page

---
 django/admin_email/admin.py                     | 17 ++++++++++++-----
 .../templates/emails/email_template.html        | 11 +++++++++++
 .../emails/unsubscribe_email_template.txt       |  9 +++++++++
 .../mail_admin/unsubscribe_email_template.txt   |  9 +++++++++
 django/admin_email/urls.py                      |  2 +-
 django/admin_email/views.py                     |  5 +++--
 .../core/templates/subscribed_confirmation.html | 12 ++++++++++++
 django/core/urls.py                             |  4 +---
 8 files changed, 58 insertions(+), 11 deletions(-)
 create mode 100644 django/admin_email/templates/emails/email_template.html
 create mode 100644 django/admin_email/templates/emails/unsubscribe_email_template.txt
 create mode 100644 django/admin_email/templates/mail_admin/unsubscribe_email_template.txt
 create mode 100644 django/core/templates/subscribed_confirmation.html

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 3288256d..86e34b02 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -22,6 +22,7 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
     form = CustomOutgoingEmailAdminForm
     list_display = ['id', 'to_display', 'subject', 'from_email', 'status']
     inlines = (LogInline,)
+    actions = ['send_initial_sign_in_email']
 
     def save_model(self, request, obj, form, change):
         if obj.status != 0:
@@ -38,7 +39,7 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
 
                 # Add unsubscribe URL only if send_to_opt_in_users is True
                 if send_to_opt_in_users:
-                    context['unsubscribe_url'] = request.build_absolute_uri(f'/unsubscribe/{quote(recipient)}/')
+                    context['unsubscribe_url'] = request.build_absolute_uri(f'/email/unsubscribe/{quote(recipient)}/')
 
                 # Render the email body using the selected template
                 email_body_final = render_to_string(template_name, context)
@@ -62,15 +63,17 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                     logger.exception(f"Failed to send email to {recipient}: {e}")
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
-    def send_initial_sign_in_email(self, obj, request):
-        recipients = User.objects.all().values_list('email', flat=True)
+    def send_initial_sign_in_email(self, request, queryset):
+        # recipients are those who have not signedup yet
+        recipients = User.objects.filter(email_opt_in=False).values_list('email', flat=True)
+
         subject = "Stay Updated with Our Latest Analysis Features"
         template_name = 'signup_mailing_list_email.html'
 
         for recipient in recipients:
             context = {
                 'email_body': "Welcome to our platform! We're excited to have you on board. Click the link below to join our mailing list and stay updated with our latest features and research papers.",
-                'join_mailing_list_url': request.build_absolute_uri(f'/join_mailing_list/{quote(recipient)}/')
+                'join_mailing_list_url': request.build_absolute_uri(f'/email/join_mailing_list/{quote(recipient)}/')
             }
 
             email_body_final = render_to_string(template_name, context)
@@ -78,7 +81,7 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
             email = EmailMultiAlternatives(
                 subject=subject,
                 body=context['email_body'],
-                from_email=obj.from_email,
+                from_email='your_email@example.com',
                 to=[recipient]
             )
 
@@ -90,5 +93,9 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
             except Exception as e:
                 logger.exception(f"Failed to send initial sign-in email to {recipient}: {e}")
 
+        self.message_user(request, "Initial sign-in emails have been sent.")
+
+    send_initial_sign_in_email.short_description = "Send mailing list email to users not signed up"
+
 
 admin.site.register(OutgoingEmail, CustomOutgoingEmailAdmin)
diff --git a/django/admin_email/templates/emails/email_template.html b/django/admin_email/templates/emails/email_template.html
new file mode 100644
index 00000000..cd36d67a
--- /dev/null
+++ b/django/admin_email/templates/emails/email_template.html
@@ -0,0 +1,11 @@
+!DOCTYPE html>
+<html>
+<head>
+    <title>Email</title>
+</head>
+<body>
+    <p>Dear {{ user.username }},</p>
+    <p>Here is the message content.</p>
+    <p>Best regards,<br> Helen</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/django/admin_email/templates/emails/unsubscribe_email_template.txt b/django/admin_email/templates/emails/unsubscribe_email_template.txt
new file mode 100644
index 00000000..11033354
--- /dev/null
+++ b/django/admin_email/templates/emails/unsubscribe_email_template.txt
@@ -0,0 +1,9 @@
+Hello {{ user.first_name }},
+
+{{ message }}
+
+If you wish to unsubscribe from future emails, click the link below:
+{{ unsubscribe_url }}
+
+Thank you,
+The Team
diff --git a/django/admin_email/templates/mail_admin/unsubscribe_email_template.txt b/django/admin_email/templates/mail_admin/unsubscribe_email_template.txt
new file mode 100644
index 00000000..11033354
--- /dev/null
+++ b/django/admin_email/templates/mail_admin/unsubscribe_email_template.txt
@@ -0,0 +1,9 @@
+Hello {{ user.first_name }},
+
+{{ message }}
+
+If you wish to unsubscribe from future emails, click the link below:
+{{ unsubscribe_url }}
+
+Thank you,
+The Team
diff --git a/django/admin_email/urls.py b/django/admin_email/urls.py
index da601503..6488b1d0 100644
--- a/django/admin_email/urls.py
+++ b/django/admin_email/urls.py
@@ -3,5 +3,5 @@ from . import views
 
 urlpatterns = [
     path('join_mailing_list/<str:email>/', views.join_mailing_list, name='join_mailing_list'),
-    path('<str:email>/', views.unsubscribe, name='unsubscribe'),
+    path('unsubscribe/<str:email>/', views.unsubscribe, name='unsubscribe'),
 ]
diff --git a/django/admin_email/views.py b/django/admin_email/views.py
index 533edae4..66c3f066 100644
--- a/django/admin_email/views.py
+++ b/django/admin_email/views.py
@@ -1,4 +1,4 @@
-from django.shortcuts import render, redirect
+from django.shortcuts import render
 from django_nano_account.models import User
 from django.http import Http404
 from urllib.parse import unquote
@@ -14,7 +14,8 @@ def join_mailing_list(request, email):
 
     user.email_opt_in = True
     user.save()
-    return redirect('home')
+    return render(request, 'subscribed_confirmation.html', {'email': email})
+
 
 def unsubscribe(request, email):
     email = unquote(email)
diff --git a/django/core/templates/subscribed_confirmation.html b/django/core/templates/subscribed_confirmation.html
new file mode 100644
index 00000000..98540181
--- /dev/null
+++ b/django/core/templates/subscribed_confirmation.html
@@ -0,0 +1,12 @@
+{% extends "base.html" %}
+
+{% block content %}
+<div class="container mt-5">
+    <div class="card">
+        <div class="card-body text-center">
+            <h1 class="card-title">Subscribed to Mailing List!</h1>
+            <p class="card-text">You have successfully subscribed to our mailing list {{ email }}.</p>
+        </div>
+    </div>
+</div>
+{% endblock %}
diff --git a/django/core/urls.py b/django/core/urls.py
index 71b2c60a..610333f2 100644
--- a/django/core/urls.py
+++ b/django/core/urls.py
@@ -11,9 +11,7 @@ urlpatterns = [
     path('upload/', include('microscopy_data.urls')),
     path('analysis/', include('hpcsubmit.urls')),
     path("contact/", include("django_contact_form.urls")),
-    path('unsubscribe/', include('admin_email.urls')),
-    path('join_mailing_list/', include('admin_email.urls')),
-
+    path('email/', include('admin_email.urls')),
 ] + static(
     settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
 )
-- 
GitLab


From e49fc9d3c130a941ed32b56dd761c095584938d8 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Thu, 16 Jan 2025 14:50:38 +0000
Subject: [PATCH 19/22] Change message of sign up email

---
 django/admin_email/admin.py                   |  23 +++-
 .../templates/signup_mailing_list_email.html  | 117 ++++--------------
 2 files changed, 44 insertions(+), 96 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 86e34b02..8e95da62 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -41,7 +41,6 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                 if send_to_opt_in_users:
                     context['unsubscribe_url'] = request.build_absolute_uri(f'/email/unsubscribe/{quote(recipient)}/')
 
-                # Render the email body using the selected template
                 email_body_final = render_to_string(template_name, context)
 
                 email = EmailMultiAlternatives(
@@ -57,14 +56,14 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
 
                 try:
                     email.send(fail_silently=False)
-                    obj.status = 0  # Mark as sent
+                    obj.status = 0  
                 except Exception as e:
-                    obj.status = 1  # Mark as failed
+                    obj.status = 1  
                     logger.exception(f"Failed to send email to {recipient}: {e}")
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
     def send_initial_sign_in_email(self, request, queryset):
-        # recipients are those who have not signedup yet
+        # recipients are those who have not signed up yet
         recipients = User.objects.filter(email_opt_in=False).values_list('email', flat=True)
 
         subject = "Stay Updated with Our Latest Analysis Features"
@@ -72,7 +71,21 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
 
         for recipient in recipients:
             context = {
-                'email_body': "Welcome to our platform! We're excited to have you on board. Click the link below to join our mailing list and stay updated with our latest features and research papers.",
+                'email_body': (
+                    "Hi,\n\n"
+                    "We hope you're finding our nano-org website valuable for your research and data analysis needs. "
+                    "We're excited to offer you the opportunity to join our mailing list, where you'll receive updates on "
+                    "new analysis features, research papers, and other important developments.\n\n"
+                    "By joining our mailing list, you'll be the first to know about:\n"
+                    "• New analysis tools and features\n"
+                    "• Latest research papers and publications\n"
+                    "• Important announcements and updates\n\n"
+                    "If you'd like to stay informed, simply click the link below to join our mailing list:\n\n"
+                    "[Join Mailing List]\n\n"
+                    "If you prefer not to receive these updates, no action is needed. You can also opt-out at any time by clicking the unsubscribe link in any of our emails.\n\n"
+                    "Thank you for being a valued member of our community!\n\n"
+                    "Best regards,\nHelen"
+                ),
                 'join_mailing_list_url': request.build_absolute_uri(f'/email/join_mailing_list/{quote(recipient)}/')
             }
 
diff --git a/django/core/templates/signup_mailing_list_email.html b/django/core/templates/signup_mailing_list_email.html
index 9681ff07..c2691411 100644
--- a/django/core/templates/signup_mailing_list_email.html
+++ b/django/core/templates/signup_mailing_list_email.html
@@ -1,99 +1,34 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-    <title>mailing_list_email</title>
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title>Join Our Mailing List</title>
     <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <style>
-        body {
-            min-height: 100vh;
-            background: white;
-            font-family: 'Didact Gothic', sans-serif;
-            color: black;
-            display: grid;
-            grid-template-rows: auto 1fr auto;
-        }
-        .container {
-            width: 100%;
-            max-width: 600px;
-            margin: 0 auto;
-            padding: 20px;
-            background-color: #f9f9f9;
-            border: 1px solid #ddd;
-            border-radius: 5px;
-        }
-        .header, .footer {
-            background-color: #fdb44b;
-            color: black;
-            text-align: center;
-            padding: 10px 0;
-        }
-        .content {
-            margin-bottom: 20px;
-        }
-        .unsubscribe {
-            text-align: center;
-            margin-top: 20px;
-        }
-        .unsubscribe a {
-            color: #fdb44b;
-            text-decoration: none;
-        }
-        .unsubscribe a:hover {
-            text-decoration: underline;
-        }
-        a {
-            color: #714316;
-            text-decoration: underline;
-        }
-        a:hover {
-            color: #7d2604;
-        }
-        h1, h2 {
-            color: #995c38;
-        }
-        footer {
-            width: 100%;
-            height: auto;
-            line-height: 20px;
-            padding-top: 20px;
-            text-align: center;
-            color: #482b0e;
-            background-color: #fdb44b;
-            z-index: 1000;
-        }
-        footer a {
-            color: #4a3212;
-        }
-    </style>
 </head>
 <body>
-    <div class="container">
-        <div class="content">
-            {{ email_body|linebreaks }}
-        </div>
-        <div class="join_mailing_list">
-            <a href="{{ join_mailing_list_url }}" style="display: inline-block; padding: 10px 20px; font-size: 16px; color: black; background-color: #fdb44b; text-decoration: none; border-radius: 5px;">Join our Mailing List</a>
-        </div>
-        <br>
-        <footer id="footer" role="contentinfo">
-            <p>
-                nano-org website has been developed by the
-                <a href="https://www.birmingham.ac.uk/bear-software">Research Software Group</a>
-                at the
-                <a href="https://www.bham.ac.uk">University of Birmingham</a>
-            </p>
-            <p>
-                <a href="{% url 'general:user_agreement' %}">User Agreement</a> |
-                <a href="{% url 'django_contact_form' %}">Contact us</a> |
-                <a href="https://www.birmingham.ac.uk/privacy/index.aspx" target="_blank">Privacy</a> |
-                <a href="https://www.birmingham.ac.uk/legal/index.aspx" target="_blank">Legal</a> |
-                <a href="https://www.birmingham.ac.uk/university/governance/policies-regs/information/index.aspx" target="_blank">Freedom of Information</a> |
-                <a href="{% url 'general:cookies' %}">Cookies</a> |
-                <a href="https://accessibility.bear.bham.ac.uk/statements-nano-org.html" target="_blank">Accessibility</a>
-            </p>
-        </footer>
+    <div style="font-family: Arial, sans-serif; line-height: 1.5; max-width: 600px; margin: 0 auto; background: #f9f9f9; padding: 20px; border: 1px solid #ddd;">
+        <p>Hi,</p>
+        <p>
+            We hope you're finding our nano-org website valuable for your research and data analysis needs. We're excited to offer you the opportunity to join our mailing list, where you'll receive updates on new analysis features, research papers, and other important developments.
+        </p>
+        <p>
+            By joining our mailing list, you'll be the first to know about:
+        </p>
+        <ul>
+            <li>New analysis tools and features</li>
+            <li>Latest research papers and publications</li>
+            <li>Important announcements and updates</li>
+        </ul>
+        <p>
+            If you'd like to stay informed, simply click the link below to join our mailing list:
+        </p>
+        <p style="text-align: center;">
+            <a href="{{ join_mailing_list_url }}" style="padding: 10px 20px; background: #fdb44b; color: white; text-decoration: none; border-radius: 5px;">Join Mailing List</a>
+        </p>
+        <p>
+            If you prefer not to receive these updates, no action is needed. You can also opt-out at any time by clicking the unsubscribe link in any of our emails.
+        </p>
+        <p>Thank you for being a valued member of our community!</p>
+        <p>Best regards,<br>Helen</p>
     </div>
 </body>
-</html>
\ No newline at end of file
+</html>
-- 
GitLab


From 3644ccc6a4d84c3c62d3144e1c9da09c1674183b Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Thu, 16 Jan 2025 15:49:13 +0000
Subject: [PATCH 20/22] fixing emails from option and link in plain text

---
 django/admin_email/admin.py | 43 ++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 8e95da62..0f71f1cf 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -70,8 +70,9 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
         template_name = 'signup_mailing_list_email.html'
 
         for recipient in recipients:
-            context = {
-                'email_body': (
+            join_link = request.build_absolute_uri(f'/email/join_mailing_list/{quote(recipient)}/')
+
+            email_body = (
                     "Hi,\n\n"
                     "We hope you're finding our nano-org website valuable for your research and data analysis needs. "
                     "We're excited to offer you the opportunity to join our mailing list, where you'll receive updates on "
@@ -81,30 +82,32 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                     "• Latest research papers and publications\n"
                     "• Important announcements and updates\n\n"
                     "If you'd like to stay informed, simply click the link below to join our mailing list:\n\n"
-                    "[Join Mailing List]\n\n"
+                    f"{join_link}\n\n"
                     "If you prefer not to receive these updates, no action is needed. You can also opt-out at any time by clicking the unsubscribe link in any of our emails.\n\n"
                     "Thank you for being a valued member of our community!\n\n"
                     "Best regards,\nHelen"
-                ),
-                'join_mailing_list_url': request.build_absolute_uri(f'/email/join_mailing_list/{quote(recipient)}/')
-            }
+                )
 
+            context = {
+                'email_body': email_body,
+                'join_mailing_list_url': join_link,
+            }
+            
             email_body_final = render_to_string(template_name, context)
+            for obj in queryset:
+                email = EmailMultiAlternatives(
+                    subject=subject,
+                    body=email_body,
+                    from_email=obj.from_email,
+                    to=[recipient]
+                )
+                email.attach_alternative(email_body_final, "text/html")  # HTML version
 
-            email = EmailMultiAlternatives(
-                subject=subject,
-                body=context['email_body'],
-                from_email='your_email@example.com',
-                to=[recipient]
-            )
-
-            email.attach_alternative(email_body_final, "text/html")
-
-            try:
-                email.send(fail_silently=False)
-                logger.info(f"Sent initial sign-in email to {recipient}")
-            except Exception as e:
-                logger.exception(f"Failed to send initial sign-in email to {recipient}: {e}")
+                try:
+                    email.send(fail_silently=False)
+                    logger.info(f"Sent initial sign-in email to {recipient}")
+                except Exception as e:
+                    logger.exception(f"Failed to send initial sign-in email to {recipient}: {e}")
 
         self.message_user(request, "Initial sign-in emails have been sent.")
 
-- 
GitLab


From c23ee79c95aec61d88dfaf9221dc364bbbdad4f1 Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Fri, 17 Jan 2025 09:51:28 +0000
Subject: [PATCH 21/22] update nano account

---
 django-nano-account | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/django-nano-account b/django-nano-account
index f021203f..12546a6b 160000
--- a/django-nano-account
+++ b/django-nano-account
@@ -1 +1 @@
-Subproject commit f021203f5c814f6ac9d9d722194cef12bb5a9814
+Subproject commit 12546a6b749ac4d23e6732bcde4171cd498eb285
-- 
GitLab


From e20b5080384c1ea01ab0c3570db4c9ff7afbfbae Mon Sep 17 00:00:00 2001
From: HelenAbbott <h.e.abbott.1@bham.ac.uk>
Date: Fri, 17 Jan 2025 10:56:39 +0000
Subject: [PATCH 22/22] improve unsub button

---
 django/admin_email/admin.py                          | 6 +++---
 django/core/templates/signup_mailing_list_email.html | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/django/admin_email/admin.py b/django/admin_email/admin.py
index 0f71f1cf..9015de3d 100644
--- a/django/admin_email/admin.py
+++ b/django/admin_email/admin.py
@@ -56,9 +56,9 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
 
                 try:
                     email.send(fail_silently=False)
-                    obj.status = 0  
+                    obj.status = 0
                 except Exception as e:
-                    obj.status = 1  
+                    obj.status = 1
                     logger.exception(f"Failed to send email to {recipient}: {e}")
         super(OutgoingEmailAdmin, self).save_model(request, obj, form, change)
 
@@ -92,7 +92,7 @@ class CustomOutgoingEmailAdmin(OutgoingEmailAdmin):
                 'email_body': email_body,
                 'join_mailing_list_url': join_link,
             }
-            
+
             email_body_final = render_to_string(template_name, context)
             for obj in queryset:
                 email = EmailMultiAlternatives(
diff --git a/django/core/templates/signup_mailing_list_email.html b/django/core/templates/signup_mailing_list_email.html
index c2691411..c910661b 100644
--- a/django/core/templates/signup_mailing_list_email.html
+++ b/django/core/templates/signup_mailing_list_email.html
@@ -22,7 +22,7 @@
             If you'd like to stay informed, simply click the link below to join our mailing list:
         </p>
         <p style="text-align: center;">
-            <a href="{{ join_mailing_list_url }}" style="padding: 10px 20px; background: #fdb44b; color: white; text-decoration: none; border-radius: 5px;">Join Mailing List</a>
+            <a href="{{ join_mailing_list_url }}" style="padding: 10px 20px; background: #fdb44b; color: rgb(5, 5, 5); text-decoration: none; border-radius: 5px;">Join Mailing List</a>
         </p>
         <p>
             If you prefer not to receive these updates, no action is needed. You can also opt-out at any time by clicking the unsubscribe link in any of our emails.
-- 
GitLab