diff --git a/perma_web/perma/email.py b/perma_web/perma/email.py index 2112fc0031..e1feee13a4 100644 --- a/perma_web/perma/email.py +++ b/perma_web/perma/email.py @@ -2,6 +2,7 @@ from django.conf import settings from django.core.mail import EmailMessage +from django.http import HttpRequest from django.template import Context, RequestContext, engines from django.utils import timezone @@ -101,19 +102,35 @@ def send_self_email(title, request, template="email/default.txt", context={}, de return message.send(fail_silently=False) -def send_user_email_copy_admins(title, from_address, to_addresses, request, template="email/default.txt", context={}): - """ - Send a message on behalf of a user to another user, cc'ing - the sender and the Perma admins. - Use reply-to for the user address so we can use email services that require authenticated from addresses. +def send_user_email_copy_admins( + title: str, + from_address: str, + to_addresses: list[str], + request: HttpRequest, + template: str = 'email/default.txt', + context: dict | None = None, +): + """Send a message to a user, CCing the sender and the Perma admins. + + This can be used to send a message from one user to another while + copying the admins, or to send a message from Perma to a user while + CCing a copy to the admins. + + Use reply_to for the user address so we can use email services that + require authenticated from addresses. """ + # Handle cases where we want to email a user and CC ourselves + cc_addresses = [settings.DEFAULT_FROM_EMAIL] + if from_address != settings.DEFAULT_FROM_EMAIL: + cc_addresses.append(from_address) + context = context if context is not None else {} message = EmailMessage( - title, - render_email(template, context, request), - settings.DEFAULT_FROM_EMAIL, - to_addresses, - cc=[settings.DEFAULT_FROM_EMAIL, from_address], - reply_to=[from_address] + subject=title, + body=render_email(template, context, request), + from_email=settings.DEFAULT_FROM_EMAIL, + to=to_addresses, + cc=cc_addresses, + reply_to=[from_address], ) return message.send(fail_silently=False) @@ -172,4 +189,3 @@ def registrar_users_plus_stats(registrars=None, year=None): "most_active_org": registrar.most_active_org_in_time_period(start_time, end_time), "registrar_users": registrar_users }) return users - diff --git a/perma_web/perma/forms.py b/perma_web/perma/forms.py index 2b192c10b3..4dcbff8276 100755 --- a/perma_web/perma/forms.py +++ b/perma_web/perma/forms.py @@ -1,20 +1,22 @@ -from axes.utils import reset as reset_login_attempts -import string +import logging import secrets +import string +from typing import Any, Mapping +from axes.utils import reset as reset_login_attempts from django import forms from django.conf import settings from django.contrib.auth.forms import SetPasswordForm +from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db.models.fields import BLANK_CHOICE_DASH from django.forms import Form, ModelForm from django.http import HttpRequest, HttpResponseRedirect from django.urls import reverse from django.utils.html import mark_safe -from perma.models import Registrar, Organization, LinkUser, Sponsorship, UserOrganizationAffiliation +from perma.models import LinkUser, Organization, Registrar, Sponsorship, UserOrganizationAffiliation from perma.utils import get_client_ip -import logging logger = logging.getLogger(__name__) ### HELPERS ### @@ -96,7 +98,7 @@ def __init__(self, *args, **kwargs): ### FIRM (OTHER ORG) QUOTE FORMS ### -class FirmOrganizationForm(ModelForm): +class FirmRegistrarForm(ModelForm): class Meta: model = Registrar fields = ['name', 'email', 'website'] @@ -107,6 +109,47 @@ class Meta: } +class ApproveRegistrarForm(ModelForm): + registrar_user = forms.EmailField(required=False) + + class Meta: + model = Registrar + fields = ['base_rate', 'status'] + + def __init__(self, data: Mapping[str, Any], registrar: Registrar, *args, **kwargs): + super().__init__(data, *args, **kwargs) + + # Populate base rate default value from model + self.fields['base_rate'].initial = registrar.base_rate + self.fields['base_rate'].widget.attrs.setdefault('value', str(registrar.base_rate)) + + # Require base rate and status only if paid registrar has a registrar user + has_registrar_user = registrar.pending_users.exists() or registrar.users.exists() + is_paid_registrar = registrar.nonpaying is False + if has_registrar_user and is_paid_registrar: + self.fields['base_rate'].required = True + self.fields['status'].required = True + else: + self.fields['base_rate'].required = False + self.fields['status'].required = False + + def clean_registrar_user(self) -> str | None: + """Validate whether a LinkUser matching the supplied email exists.""" + cleaned_value = self.cleaned_data['registrar_user'].lower() + if not cleaned_value: + return None + + try: + LinkUser.objects.get(email=cleaned_value) + except ObjectDoesNotExist as error: + raise ValidationError( + 'Email %(email)s does not match an existing user account', + params={'email': self.cleaned_data['registrar_user']}, + ) from error + else: + return cleaned_value + + class FirmUsageForm(Form): estimated_number_of_accounts = forms.ChoiceField( choices=[(option, option) for option in ['1 - 10', '10 - 50', '50 - 100', '100+']], @@ -310,13 +353,13 @@ class CreateUserFormWithFirm(UserForm): add firm to the create user form """ - would_be_org_admin = forms.ChoiceField( + registrar_user_candidate = forms.ChoiceField( widget=forms.Select, choices=[(True, 'Yes'), (False, 'No')], initial=(False, 'No') ) class Meta: model = LinkUser - fields = ['first_name', 'last_name', 'email', 'would_be_org_admin'] + fields = ['first_name', 'last_name', 'email', 'registrar_user_candidate'] def __init__(self, *args, **kwargs): super(CreateUserFormWithFirm, self).__init__(*args, **kwargs) @@ -324,7 +367,7 @@ def __init__(self, *args, **kwargs): self.fields['first_name'].label = 'Your first name' self.fields['last_name'].label = 'Your last name' self.fields['email'].label = 'Your email' - self.fields['would_be_org_admin'].label = 'Would you be an administrator on this account?' + self.fields['registrar_user_candidate'].label = 'Would you be an administrator on this account?' # Populate and set visibility of fields based on whether user is logged in if hasattr(self, 'request') and self.user_is_logged_in(self.request): diff --git a/perma_web/perma/templates/email/admin/firm_request.txt b/perma_web/perma/templates/email/admin/firm_request.txt index 263408ca0f..2852b0d768 100644 --- a/perma_web/perma/templates/email/admin/firm_request.txt +++ b/perma_web/perma/templates/email/admin/firm_request.txt @@ -1,12 +1,16 @@ -{{ user_form.data.email }}{% if user_form.data.first_name or user_form.data.last_name %} ({{ user_form.data.first_name }} {{ user_form.data.last_name }}){% endif %} has requested more information about creating an account for a law firm or other organization: +A new account request from a law firm or other organization has been submitted, and is now awaiting review. -Organization name: {{ organization_form.cleaned_data.name }} -Organization email: {{ organization_form.cleaned_data.email }} -Organization website: {{ organization_form.cleaned_data.website }} +Registrar name: {{ registrar.name }} +Registrar email: {{ registrar.email }} +Registrar website: {{ registrar.website }} Estimated number of individual accounts: {{ usage_form.cleaned_data.estimated_number_of_accounts }} Estimated number of Perma Links created each month (per user): {{ usage_form.cleaned_data.estimated_perma_links_per_month }} -User would be an administrator on the account: {{ user_form.data.would_be_org_admin | yesno }} +User name: {{ user_name }} +User email: {{ user_email }} +User would be an administrator on the account: {{ user_form.data.registrar_user_candidate | yesno }} -This user currently {% if existing_user %}has{% else %}does not have{% endif %} an account. +This user {% if existing_user %}has{% else %}does not have{% endif %} an account. + +https://{{ host }}{{ confirmation_route }} diff --git a/perma_web/perma/templates/email/admin/registrar_request.txt b/perma_web/perma/templates/email/admin/registrar_request.txt index d7e348b367..e3c426a47b 100644 --- a/perma_web/perma/templates/email/admin/registrar_request.txt +++ b/perma_web/perma/templates/email/admin/registrar_request.txt @@ -1,4 +1,5 @@ -A new library account request from pending registrar, named {{ name }} (at {{ email }}) is awaiting review and approval. -Requesting user's email: {{ requested_by_email }} +A new account request from a pending registrar, named {{ name }} ({{ email }}), is awaiting review and approval. + +Requesting user's email: {{ requested_by_email }} http://{{ host }}{{ confirmation_route}} diff --git a/perma_web/perma/templates/email/library_approved.txt b/perma_web/perma/templates/email/library_approved.txt deleted file mode 100644 index 07ce4d6854..0000000000 --- a/perma_web/perma/templates/email/library_approved.txt +++ /dev/null @@ -1,7 +0,0 @@ -TITLE: Your Perma.cc library account is approved - -Your request for a Perma.cc library account has been approved and your personal account has been linked. - -To start creating organizations and users, please click the link below or copy it to your web browser. - -http://{{ host }}{{ account_route }} diff --git a/perma_web/perma/templates/email/pending_registrar.txt b/perma_web/perma/templates/email/pending_registrar.txt index 74f93d8132..791e987c6b 100644 --- a/perma_web/perma/templates/email/pending_registrar.txt +++ b/perma_web/perma/templates/email/pending_registrar.txt @@ -1,6 +1,6 @@ TITLE: A Perma.cc account has been created for you -We will review your library account request as soon as possible. A personal account has been created for you and will be linked to your library once that account is approved. +We will review your organization account request as soon as possible. A personal account has been created for you and will be linked to your organization once that account is approved. To activate this personal account, please click the link below or copy it to your web browser. You will need to create a password. diff --git a/perma_web/perma/templates/email/registrar_approved.txt b/perma_web/perma/templates/email/registrar_approved.txt new file mode 100644 index 0000000000..5630cfcac1 --- /dev/null +++ b/perma_web/perma/templates/email/registrar_approved.txt @@ -0,0 +1,7 @@ +TITLE: Your Perma.cc registrar account is approved + +Your request for a Perma.cc registrar account has been approved and your personal account has been linked. + +To start creating organizations and users, please click the link below or copy it to your web browser. + +http://{{ host }}{{ account_route }} diff --git a/perma_web/perma/templates/registration/sign-up-firms.html b/perma_web/perma/templates/registration/sign-up-firms.html index 22e6c1b1ad..2af401e56d 100644 --- a/perma_web/perma/templates/registration/sign-up-firms.html +++ b/perma_web/perma/templates/registration/sign-up-firms.html @@ -24,7 +24,7 @@