diff --git a/esp/esp/dbmail/models.py b/esp/esp/dbmail/models.py
index acc49a17cf..449eda3b7a 100644
--- a/esp/esp/dbmail/models.py
+++ b/esp/esp/dbmail/models.py
@@ -74,7 +74,7 @@ def send_mail(subject, message, from_email, recipient_list, fail_silently=False,
from_email = from_email.strip()
# the from_email must match one of our DMARC domains/subdomains
# or the email may be rejected by email clients
- if not re.match(r"(^.+@%s>?$)|(^.+@(\w+\.)?learningu\.org>?$)" % settings.SITE_INFO[1].replace(".", "\."), from_email):
+ if not re.match(r'(^.+@{0}$)|(^.+<.+@{0}>$)|(^.+@(\w+\.)?learningu\.org$)|(^.+<.+@(\w+\.)?learningu\.org>$)'.format(settings.SITE_INFO[1].replace('.', '\.')), from_email):
raise ESPError("Invalid 'From' email address (" + from_email + "). The 'From' email address must " +
"end in @" + settings.SITE_INFO[1] + " (your website), " +
"@learningu.org, or a valid subdomain of learningu.org " +
diff --git a/esp/esp/program/controllers/confirmation.py b/esp/esp/program/controllers/confirmation.py
index aa15edae30..064a9e80c8 100644
--- a/esp/esp/program/controllers/confirmation.py
+++ b/esp/esp/program/controllers/confirmation.py
@@ -1,5 +1,4 @@
-from __future__ import absolute_import
__author__ = "Individual contributors (see AUTHORS file)"
__date__ = "$DATE$"
__rev__ = "$REV$"
@@ -37,13 +36,14 @@
from esp.program.models import Program, ClassSection, ClassSubject
from esp.users.models import ESPUser, Record, RecordType
from esp.program.modules.module_ext import DBReceipt
+from esp.program.modules.forms.admincore import get_template_source
from django.template import Template, Context
from django.template.loader import select_template
from esp.dbmail.models import send_mail
class ConfirmationEmailController(object):
- def send_confirmation_email(self, user, program, repeat=False, override=False):
+ def send_confirmation_email(self, user, program, repeat=False, override=False, context = {}):
options = program.studentclassregmoduleinfo
## Get or create a userbit indicating whether or not email's been sent.
try:
@@ -52,12 +52,16 @@ def send_confirmation_email(self, user, program, repeat=False, override=False):
except Exception:
created = False
if (created or repeat) and (options.send_confirmation or override):
+ context['user'] = user
+ context['program'] = program
+ receipt = select_template(['program/confemails/%s_custom_receipt.html' %(program.id), 'program/confemails/default.html'])
+ # render the custom pretext first
try:
- receipt_template = Template(DBReceipt.objects.get(program=program, action='confirmemail').receipt)
- receipt_text = receipt_template.render(Context({'user': user, 'program': program}))
- except:
- receipt_template = select_template(['program/confemails/%s_confemail.txt' %(program.id), 'program/confemails/default.txt'])
- receipt_text = receipt_template.render({'user': user, 'program': program})
+ pretext = DBReceipt.objects.get(program=program, action='confirmemail').receipt
+ except DBReceipt.DoesNotExist:
+ pretext = get_template_source(['program/confemails/%s_custom_pretext.html' %(program.id), 'program/confemails/default_pretext.html'])
+ context['pretext'] = Template(pretext).render( Context(context, autoescape=False) )
+ receipt_text = receipt.render( context )
send_mail("Thank you for registering for %s!" %(program.niceName()), \
receipt_text, \
(ESPUser.email_sendto_address(program.director_email, program.niceName() + " Directors")), \
diff --git a/esp/esp/program/migrations/0027_auto_20240220_2124.py b/esp/esp/program/migrations/0027_auto_20240220_2124.py
index 82c854de1e..863f19d37f 100644
--- a/esp/esp/program/migrations/0027_auto_20240220_2124.py
+++ b/esp/esp/program/migrations/0027_auto_20240220_2124.py
@@ -11,7 +11,7 @@
def replace_director_emails(apps, schema_editor):
Program = apps.get_model('program', 'Program')
for prog in Program.objects.all():
- if not re.match(r"(^.+@%s$)|(^.+@(\w+\.)*learningu\.org$)" % settings.SITE_INFO[1].replace(".", "\."), prog.director_email):
+ if not re.match(r'(^.+@{0}$)|(^.+@(\w+\.)?learningu\.org$)'.format(settings.SITE_INFO[1].replace('.', '\.')), prog.director_email):
prog.director_email = 'info@' + settings.SITE_INFO[1]
prog.save()
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
help_text='The director email address must end in @' + settings.SITE_INFO[1] +
' (your website), @learningu.org, or a valid subdomain of learningu.org (i.e., @subdomain.learningu.org). The default is info@' + settings.SITE_INFO[1] +
', which redirects to the "default" email address from your site\'s settings by default. You can create and manage your email redirects here.',
- validators=[validators.RegexValidator(r'(^.+@%s$)|(^.+@(\w+\.)?learningu\.org$)' % settings.SITE_INFO[1].replace('.', '\.'))]),
+ validators=[validators.RegexValidator(r'(^.+@{0}$)|(^.+<.+@{0}>$)|(^.+@(\w+\.)?learningu\.org$)|(^.+<.+@(\w+\.)?learningu\.org>$)'.format(settings.SITE_INFO[1].replace('.', '\.')))]),
),
# This will run backwards, but won't do anything
migrations.RunPython(replace_director_emails, lambda a, s: None),
diff --git a/esp/esp/program/models/__init__.py b/esp/esp/program/models/__init__.py
index 58bb8d436c..44edb6fa79 100644
--- a/esp/esp/program/models/__init__.py
+++ b/esp/esp/program/models/__init__.py
@@ -271,7 +271,7 @@ class Program(models.Model, CustomFormsLinkModel):
grade_max = models.IntegerField()
# director contact email address used for from field and display
director_email = models.EmailField(default='info@' + settings.SITE_INFO[1], max_length=75,
- validators=[validators.RegexValidator(r'(^.+@%s$)|(^.+@(\w+\.)?learningu\.org$)' % settings.SITE_INFO[1].replace('.', '\.'))],
+ validators=[validators.RegexValidator(r'(^.+@{0}$)|(^.+@(\w+\.)?learningu\.org$)'.format(settings.SITE_INFO[1].replace('.', '\.')))],
help_text=mark_safe('The director email address must end in @' + settings.SITE_INFO[1] + ' (your website), ' +
'@learningu.org, or a valid subdomain of learningu.org (i.e., @subdomain.learningu.org). ' +
'The default is info@' + settings.SITE_INFO[1] + ', which redirects to the "default" ' +
diff --git a/esp/esp/program/modules/forms/admincore.py b/esp/esp/program/modules/forms/admincore.py
index 7a03627d94..ed1c41f9c9 100644
--- a/esp/esp/program/modules/forms/admincore.py
+++ b/esp/esp/program/modules/forms/admincore.py
@@ -1,4 +1,3 @@
-from __future__ import absolute_import
from decimal import Decimal
from django import forms
from django.conf import settings
@@ -19,7 +18,7 @@
from esp.utils.models import TemplateOverride
def get_rt_choices():
- choices = [("All", "All")]
+ choices = [("All","All")]
for rt in RegistrationType.objects.all().order_by('name'):
if rt.displayName:
choices.append((rt.name, '%s (displayed as "%s")' % (rt.name, rt.displayName)))
@@ -91,16 +90,16 @@ def save(self):
class Meta:
fieldsets = [
('Program Title', {'fields': ['term', 'term_friendly'] }),
- ('Program Constraints', {'fields':['grade_min', 'grade_max', 'program_size_max', 'program_allow_waitlist']}),
- ('About Program Creator', {'fields':['director_email', 'director_cc_email', 'director_confidential_email']}),
- ('Financial Details', {'fields':['base_cost', 'sibling_discount']}),
- ('Program Internal Details', {'fields':['program_type', 'program_modules', 'program_module_questions', 'class_categories', 'flag_types']}),
+ ('Program Constraints', {'fields':['grade_min','grade_max','program_size_max','program_allow_waitlist']}),
+ ('About Program Creator',{'fields':['director_email', 'director_cc_email', 'director_confidential_email']}),
+ ('Financial Details' ,{'fields':['base_cost','sibling_discount']}),
+ ('Program Internal Details' ,{'fields':['program_type','program_modules','program_module_questions','class_categories','flag_types']}),
]# Here you can also add description for each fieldset.
widgets = {
'program_modules': forms.SelectMultiple(attrs={'class': 'hidden-field'}),
}
model = Program
-ProgramSettingsForm.base_fields['director_email'].widget = forms.EmailInput(attrs={'pattern': r'(^.+@%s$)|(^.+@(\w+\.)?learningu\.org$)' % settings.SITE_INFO[1].replace('.', '\.')})
+ProgramSettingsForm.base_fields['director_email'].widget = forms.EmailInput(attrs={'pattern': r'(^.+@{0}$)|(^.+@(\w+\.)?learningu\.org$)'.format(settings.SITE_INFO[1].replace('.', '\.'))})
class TeacherRegSettingsForm(BetterModelForm):
""" Form for changing teacher class registration settings. """
@@ -128,7 +127,7 @@ class Meta:
('Priority Registration Settings', {'fields': ['priority_limit']}), # use_priority is not included here to prevent confusion; to my knowledge, only HSSP uses this setting - WG
('Enrollment Settings', {'fields': ['register_from_catalog', 'visible_enrollments', 'visible_meeting_times', 'show_emailcodes']}), # use_grade_range_exceptions is excluded until there is an interface for it - WG 5/25/23
('Button Settings', {'fields': ['confirm_button_text', 'view_button_text', 'cancel_button_text', 'temporarily_full_text', 'cancel_button_dereg', 'send_confirmation']}),
- ('Visual Options', {'fields': ['progress_mode', 'force_show_required_modules']}),
+ ('Visual Options', {'fields': ['progress_mode','force_show_required_modules']}),
]# Here you can also add description for each fieldset.
model = StudentClassRegModuleInfo
@@ -141,14 +140,16 @@ def get_template_source(template_list):
class ReceiptsForm(BetterForm):
confirm = forms.CharField(widget=forms.Textarea(attrs={'class': 'fullwidth'}),
- help_text = "This text is shown on the website when a student clicks the 'confirm registration' button (HTML is supported).\
- If no text is supplied, the default text will be used. The text is then followed by the student's information,\
- the program information, the student's purchased items, and the student's schedule.",
+ help_text = mark_safe("This text is shown on the website when a student clicks the 'confirm registration' button (HTML is supported).\
+ If no text is supplied, the default text will be used. The text is then followed by the student's information,\
+ the program information, the student's purchased items, and the student's schedule."),
required = False)
confirmemail = forms.CharField(widget=forms.Textarea(attrs={'class': 'fullwidth'}),
- help_text = "This receipt is sent via email when a student clicks the 'confirm registration' button.\
- If no text is supplied, the default text will be used.",
- required = False)
+ help_text = mark_safe("This text is sent via email when a student clicks the 'confirm registration' button.\
+ If no text is supplied, the default text will be used. The text is then followed by the student's information,\
+ the program information, the student's purchased items, and the student's schedule. This email can be disabled\
+ by deactivating the 'Send confirmation' option in the 'Student Registration Settings' above."),
+ required = False)
cancel = forms.CharField(widget=forms.Textarea(attrs={'class': 'fullwidth'}),
help_text = "This receipt is shown on the website when a student clicks the 'cancel registration' button.\
If no text is supplied, the student will be redirected to the main student registration page instead.",
@@ -164,7 +165,7 @@ def __init__(self, *args, **kwargs):
elif action == "confirm":
receipt_text = get_template_source(['program/receipts/%s_custom_pretext.html' %(self.program.id), 'program/receipts/default_pretext.html'])
elif action == "confirmemail":
- receipt_text = get_template_source(['program/confemails/%s_confemail.txt' %(self.program.id), 'program/confemails/default.txt'])
+ receipt_text = get_template_source(['program/confemails/%s_confemail_pretext.html' %(self.program.id),'program/confemails/default_pretext.html'])
else:
receipt_text = ""
self.fields[action].initial = receipt_text.encode('UTF-8')
@@ -179,7 +180,7 @@ def save(self):
if action == "confirm":
default_text = get_template_source(['program/receipts/%s_custom_pretext.html' %(self.program.id), 'program/receipts/default_pretext.html'])
elif action == "confirmemail":
- default_text = get_template_source(['program/confemails/%s_confemail.txt' %(self.program.id), 'program/confemails/default.txt'])
+ default_text = get_template_source(['program/confemails/%s_confemail_pretext.html' %(self.program.id),'program/confemails/default_pretext.html'])
elif action == "cancel":
default_text = ""
if cleaned_text == default_text:
@@ -226,7 +227,7 @@ def __init__(self, *args, **kwargs):
self.fields[key].initial = self.fields[key].default = tag_info.get('default')
self.fields[key].required = False
set_val = Tag.getBooleanTag(key, program = self.program) if tag_info.get('is_boolean', False) else Tag.getProgramTag(key, program = self.program)
- if set_val is not None and set_val != self.fields[key].initial:
+ if set_val != None and set_val != self.fields[key].initial:
if isinstance(self.fields[key], forms.MultipleChoiceField):
set_val = set_val.split(",")
self.fields[key].initial = set_val
diff --git a/esp/esp/program/modules/handlers/commmodule.py b/esp/esp/program/modules/handlers/commmodule.py
index 06fa7797a2..a6ee70876e 100644
--- a/esp/esp/program/modules/handlers/commmodule.py
+++ b/esp/esp/program/modules/handlers/commmodule.py
@@ -41,6 +41,7 @@
from esp.users.controllers.usersearch import UserSearchController
from esp.users.views.usersearch import get_user_checklist
from esp.dbmail.models import ActionHandler
+from esp.tagdict.models import Tag
from django.template import Template
from django.template import Context as DjangoContext
from esp.middleware import ESPError
@@ -81,7 +82,7 @@ def commprev(self, request, tl, one, two, module, extra, prog):
# Set From address
if request.POST.get('from', '').strip():
fromemail = request.POST['from']
- if not re.match(r"(^.+@%s$)|(^.+@(\w+\.)?learningu\.org$)" % settings.SITE_INFO[1].replace(".", "\."), fromemail):
+ if not re.match(r'(^.+@{0}$)|(^.+<.+@{0}>$)|(^.+@(\w+\.)?learningu\.org$)|(^.+<.+@(\w+\.)?learningu\.org>$)'.format(settings.SITE_INFO[1].replace('.', '\.')), fromemail):
raise ESPError("Invalid 'From' email address. The 'From' email address must " +
"end in @" + settings.SITE_INFO[1] + " (your website), " +
"@learningu.org, or a valid subdomain of learningu.org " +
@@ -91,7 +92,8 @@ def commprev(self, request, tl, one, two, module, extra, prog):
prs = PlainRedirect.objects.filter(original = "info")
if not prs.exists():
redirect = PlainRedirect.objects.create(original = "info", destination = settings.DEFAULT_EMAIL_ADDRESSES['default'])
- fromemail = '%s@%s' % ("info", settings.SITE_INFO[1])
+ fromemail = '%s <%s@%s>' % (Tag.getTag('full_group_name') or '%s %s' % (settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME),
+ "info", settings.SITE_INFO[1])
# Set Reply-To address
if request.POST.get('replyto', '').strip():
@@ -254,7 +256,8 @@ def commpanel(self, request, tl, one, two, module, extra, prog):
if request.method == 'POST':
# Turn multi-valued QueryDict into standard dictionary
data = ListGenModule.processPost(request)
-
+ context['default_from'] = '%s <%s@%s>' % (Tag.getTag('full_group_name') or '%s %s' % (settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME),
+ "info", settings.SITE_INFO[1])
## Handle normal list selecting submissions
if ('base_list' in data and 'recipient_type' in data) or ('combo_base_list' in data):
@@ -275,7 +278,7 @@ def commpanel(self, request, tl, one, two, module, extra, prog):
prs = PlainRedirect.objects.filter(original = "info")
if not prs.exists():
redirect = PlainRedirect.objects.create(original = "info", destination = settings.DEFAULT_EMAIL_ADDRESSES['default'])
- context['from'] = '%s@%s' % ("info", settings.SITE_INFO[1])
+ context['from'] = context['default_from']
return render_to_response(self.baseDir()+'step2.html', request, context)
## Prepare a message starting from an earlier request
diff --git a/esp/esp/program/modules/handlers/studentregcore.py b/esp/esp/program/modules/handlers/studentregcore.py
index 625f35757e..9ff555a42a 100644
--- a/esp/esp/program/modules/handlers/studentregcore.py
+++ b/esp/esp/program/modules/handlers/studentregcore.py
@@ -206,12 +206,13 @@ def confirmreg_forreal(self, request, tl, one, two, module, extra, prog, new_reg
else:
raise ESPError("You must finish all the necessary steps first, then click on the Save button to finish registration.", log=False)
- cfe = ConfirmationEmailController()
- cfe.send_confirmation_email(user, self.program)
-
# when does class registration close for this user?
context['deadline'] = Permission.user_deadline_when(user, "Student/Classes", prog)
+ cfe = ConfirmationEmailController()
+ # this email includes the student's schedule (by default), so send a new email each time they confirm their reg
+ cfe.send_confirmation_email(user, self.program, context = context, repeat = True)
+
context["request"] = request
context["program"] = prog
context.update(esp_context_stuff())
diff --git a/esp/esp/program/modules/migrations/0044_auto_20240507_2041.py b/esp/esp/program/modules/migrations/0044_auto_20240507_2041.py
new file mode 100644
index 0000000000..4527630eab
--- /dev/null
+++ b/esp/esp/program/modules/migrations/0044_auto_20240507_2041.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2024-05-07 20:41
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('modules', '0043_auto_20240321_2023'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='studentclassregmoduleinfo',
+ name='send_confirmation',
+ field=models.BooleanField(default=True, help_text=b'Check this box to send each student an email each time they confirm their registration. You can customize the text of the email using the "Confirmemail" registration receipt below.'),
+ ),
+ ]
diff --git a/esp/esp/program/modules/migrations/0045_merge_20240515_2025.py b/esp/esp/program/modules/migrations/0045_merge_20240515_2025.py
new file mode 100644
index 0000000000..a8cc3cd789
--- /dev/null
+++ b/esp/esp/program/modules/migrations/0045_merge_20240515_2025.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.29 on 2024-05-15 20:25
+from __future__ import unicode_literals
+
+from __future__ import absolute_import
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('modules', '0044_auto_20240507_2041'),
+ ('modules', '0044_auto_20240509_2341'),
+ ]
+
+ operations = [
+ ]
diff --git a/esp/esp/program/modules/module_ext.py b/esp/esp/program/modules/module_ext.py
index 89b55742d9..6baf4b825e 100644
--- a/esp/esp/program/modules/module_ext.py
+++ b/esp/esp/program/modules/module_ext.py
@@ -116,7 +116,7 @@ class StudentClassRegModuleInfo(models.Model):
progress_mode = models.IntegerField(default=1, help_text='Select which to use on student reg: 1=checkboxes, 2=progress bar, 0=neither.')
# Choose whether an email is sent the first time a student confirms registration.
- send_confirmation = models.BooleanField(default=False, help_text='Check this box to send each student an email the first time they confirm their registration. You must define an associated DBReceipt of type "confirmemail".')
+ send_confirmation = models.BooleanField(default=True, help_text='Check this box to send each student an email each time they confirm their registration. You can customize the text of the email using the "Confirmemail" registration receipt below.')
# Choose whether class IDs are shown on catalog.
show_emailcodes = models.BooleanField(default=True, help_text='Uncheck this box to prevent email codes (i.e. E534, H243) from showing up on catalog and fillslot pages.')
diff --git a/esp/esp/users/models/__init__.py b/esp/esp/users/models/__init__.py
index ad1cd5fc13..4cf7a79c69 100644
--- a/esp/esp/users/models/__init__.py
+++ b/esp/esp/users/models/__init__.py
@@ -2948,7 +2948,7 @@ def send_confirmation_email(self):
subject, message = self._confirmation_email_content()
send_mail(subject,
message,
- 'info@' + settings.SITE_INFO[1],
+ Tag.getTag('full_group_name') or '%s %s' % (settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME) + '
+User Information:
+
+
+
+
+
+Program Information:
+
+Payment Information:
+
+ Classes for {{ user.first_name }} {{ user.last_name }} - ID: {{ user.id }} + | +||
---|---|---|
Classes beginning on {{ timeslot.0.start|date:"D M j Y" }} | ||
Block {{ timeslot.2 }} | ||
{{ timeslot.0.short_description }} | +{{ timeslot.0.description }} | +{{ timeslot.0.short_description }} | + {% if timeslot.1|length_is:0 %} ++ No classes + | + {% elif timeslot.1.0.first_meeting_time %} +
+ {% for cls in timeslot.1 %}
+ {% comment %}{% if use_priority %}{% endcomment %}
+ {% if not cls.section.verbs|length_is:0 %}
+ {% for v in cls.section.verbs %}{{ v }}{% if not forloop.last %}, {% endif %}{% endfor %}:
+ {% endif %}
+ {% comment %}{% endif %}{% endcomment %}
+ {{ cls.section }}{% if not cls.first_meeting_time %} (continued){% endif %}
+
+ {% if request.user.onsite_local %}
+ ({{ cls.section.prettyrooms|join:", " }})
+ {% endif %}
+ {% if not forloop.last %} {% endif %} + {% endfor %} + |
+ {% endif %}
+
+ {% endifequal %}
+{% endfor %}
+
Dear {{ user.first_name }},
+ +Thank you for registering for {{ program.niceName }}! This is a preliminary confirmation email for your {{ program.program_type }} registration. Your registration details are included below. You can come back and change your classes until registration closes. We will contact you via email with further information.
+ +{{ program.niceName }} Directors
\ No newline at end of file diff --git a/esp/templates/program/modules/commmodule/step2.html b/esp/templates/program/modules/commmodule/step2.html index b0ca1684c0..5d21d0a49e 100644 --- a/esp/templates/program/modules/commmodule/step2.html +++ b/esp/templates/program/modules/commmodule/step2.html @@ -42,6 +42,7 @@