Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default email template #1898

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions esp/esp/program/modules/handlers/commmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from django.template import Context as DjangoContext
from esp.middleware.threadlocalrequest import AutoRequestContext as Context
from esp.middleware import ESPError
from django.template import loader

class CommModule(ProgramModuleObj):
""" Want to email all ESP students within a 60 mile radius of NYC?
Expand All @@ -66,10 +67,12 @@ def commprev(self, request, tl, one, two, module, extra, prog):
from esp.users.models import PersistentQueryFilter
from django.conf import settings

filterid, listcount, subject, body = [request.POST['filterid'],
request.POST['listcount'],
request.POST['subject'],
request.POST['body'] ]
filterid, listcount, subject, msglang, body = [
request.POST['filterid'],
request.POST['listcount'],
request.POST['subject'],
request.POST['msglang'],
request.POST['body'] ]
sendto_fn_name = request.POST.get('sendto_fn_name', MessageRequest.SEND_TO_SELF_REAL)
selected = request.POST.get('selected')

Expand Down Expand Up @@ -100,15 +103,22 @@ def commprev(self, request, tl, one, two, module, extra, prog):

MessageRequest.assert_is_valid_sendto_fn_or_ESPError(sendto_fn_name)

# If they were trying to use HTML, don't sanitize the content.
if '<html>' not in body:
htmlbody = body.replace('<', '&lt;').replace('>', '&gt;').replace('\n', '<br />')
# If they were trying to use plain text, sanitize the content and
# make whitespace WYSIWYG from textbox before rendering HTML.
if msglang == 'plaintext':
htmlbody = body.replace('&', '&amp;').replace('<', '&lt;'
).replace('>', '&gt;').replace('\n', '<br />').replace(
' ', '&nbsp;&nbsp;')
else:
htmlbody = body

contextdict = {'user' : ActionHandler(firstuser, firstuser),
'program': ActionHandler(self.program, firstuser) }

htmlbody = unicode(loader.get_template('email/default_email.html'
).render(DjangoContext({'msgbdy': htmlbody,
'user': ActionHandler(firstuser, firstuser),
'program': ActionHandler(self.program, firstuser)})))
renderedtext = Template(htmlbody).render(DjangoContext(contextdict))

return render_to_response(self.baseDir()+'preview.html', request,
Expand All @@ -119,7 +129,9 @@ def commprev(self, request, tl, one, two, module, extra, prog):
'subject': subject,
'from': fromemail,
'replyto': replytoemail,
'msglang': msglang,
'body': body,
'htmlbody': htmlbody,
'renderedtext': renderedtext})

def approx_num_of_recipients(self, filterObj, sendto_fn):
Expand Down Expand Up @@ -151,12 +163,13 @@ def commfinal(self, request, tl, one, two, module, extra, prog):
from esp.dbmail.models import MessageRequest
from esp.users.models import PersistentQueryFilter

filterid, fromemail, replytoemail, subject, body = [
filterid, fromemail, replytoemail, subject, body, htmlbody = [
request.POST['filterid'],
request.POST['from'],
request.POST['replyto'],
request.POST['subject'],
request.POST['body'] ]
request.POST['body'],
request.POST['htmlbody'] ]
sendto_fn_name = request.POST.get('sendto_fn_name', MessageRequest.SEND_TO_SELF_REAL)

try:
Expand All @@ -176,7 +189,10 @@ def commfinal(self, request, tl, one, two, module, extra, prog):
sendto_fn_name = sendto_fn_name,
sender = fromemail,
creator = request.user,
msgtext = body,
msgtext = unicode(loader.get_template('email/default_email.html').render(DjangoContext(
{'msgbdy': htmlbody,
'user': request.user,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes the template to render user variables in the template itself (not message body) with information about the sender. Not a problem with the default since there's no use of the user, but this is what's causing Yale's bug. Since the template is going to get rendered into one copy for all users, which then gets further transformed later into the individual copies, user information shouldn't be passed here at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to add user information into the template? It would be very useful to put, for instance, the username the email was sent to.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you can still put user information in the template; it will just get interpreted on the second render pass, when we add user variables.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't quite understand what the difference is / what needs to be done in order to make sure the right thing happens instead of the variables being rendered in the template?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This template should get rendered twice. First, here, we render msgbdy into default_email_html.txt to get msgtext. This happens once, and therefore should not include any user data, because we don't yet know what user the email will be sent to. Then, later, below, we the output of that (msgtext) into the actual email, once for each user, injecting their information into the template. Any variables in msgtext will get interpreted on the second pass. When you include request.user the first time, it means that any user variables in default_email_html.txt get rendered according to the user sending the email, because that's who request.user is; instead you want to render them the second time with the email's recipient as the user.

I wrote too quickly above and was a bit incorrect: any template variables that you put in default_email_html.txt that should get rendered on the second pass, i.e., any user variables, need to have their template tags excaped, using {% templatetag openvariable %} instead of {{ and {% templatetag closevariable %} instead of }}. This way, the first rendering pass will convert those to {{ and }} and then the second rendering pass will see them as normal variables, and interpolate the correct user's information.

'program': self.program }))),
special_headers_dict
= { 'Reply-To': replytoemail, }, )

Expand Down Expand Up @@ -276,12 +292,13 @@ def commpanel(self, request, tl, one, two, module, extra, prog):
@needs_admin
def maincomm2(self, request, tl, one, two, module, extra, prog):

filterid, listcount, fromemail, replytoemail, subject, body = [
filterid, listcount, fromemail, replytoemail, subject, msglang, body = [
request.POST['filterid'],
request.POST['listcount'],
request.POST['from'],
request.POST['replyto'],
request.POST['subject'],
request.POST['msglang'],
request.POST['body'] ]
sendto_fn_name = request.POST.get('sendto_fn_name', MessageRequest.SEND_TO_SELF_REAL)
selected = request.POST.get('selected')
Expand All @@ -294,6 +311,7 @@ def maincomm2(self, request, tl, one, two, module, extra, prog):
'from': fromemail,
'replyto': replytoemail,
'subject': subject,
'msglang': msglang,
'body': body})

class Meta:
Expand Down
17 changes: 17 additions & 0 deletions esp/templates/email/default_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html>
{% autoescape off %}
{{ msgbdy|safe }}
{% endautoescape %}
<small>
You received this email because you have signed up for an account on our website,
<a href=>{% templatetag openvariable %}EMAIL_HOST{% templatetag closevariable %}.
This email was sent to the email account associated with the
user {% templatetag openvariable %}user.username{% templatetag closevariable %}.
If you received the same email message multiple times, you may have duplicate accounts;
please email us to delete them. If you no longer wish for {% templatetag openvariable %}user.username{% templatetag closevariable %}
to receive emails from us, email us or click
<a href="/myesp/disableaccount" style="color: #336699;font-weight: normal;text-decoration: underline;">here</a>.
This action will unsubscribe you from receiving emails to {% templatetag openvariable %}user.username{% templatetag closevariable %}.
You will still receive messages to this email address if it is listed in the contact information for other accounts on our site.
</small>
</html>
38 changes: 38 additions & 0 deletions esp/templates/program/modules/commmodule/commpanel_step2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

function showEmailExplanation() {
document.getElementById("from-help").style.display = '';
}

function hideEmailExplanation() {
document.getElementById("from-help").style.display = 'none';
document.getElementById("from").focus();
}


function validateMsgLang() {
var msgTypes = document.getElementsByName("msglang");
var containsTag = /<(\/?((.)|(br ?\/?)))>|(<img)/i.test(
document.getElementById("emailbody").value);
var containsHTag = /<\/?html>/i.test(
document.getElementById("emailbody").value);

if(containsHTag) {
return confirm("Didn't we say not to include <html> tags?!? "
+ "If you're sure you know what you're doing, "
+ "click 'OK' to continue.");
} else if(msgTypes[1].checked && !containsTag) {
return confirm('You selected "HTML" but included no HTML tags. '
+ 'Continuing might squash your formatting. '
+ 'Would you still like to proceed?');
} else if(msgTypes[0].checked && containsTag) {
return confirm('You selected "Plain Text" but have HTML tags '
+ '(such as <p>) in your message. '
+ 'Continuing will leave your tags in your message '
+ '(so you would see "<b>hello</b>" instead of '
+ 'a bold "hello"). '
+ 'Would you still like to proceed?');
}
else {
return true;
}
}
3 changes: 2 additions & 1 deletion esp/templates/program/modules/commmodule/preview.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ <h2>Preview</h2>
<input type="hidden" name="body" value="{{body}}" />
<input type="hidden" name="listcount" value="{{listcount}}" />
<input type="hidden" name="selected" value="{{selected}}" />
<input type="hidden" name="msglang" value="{{msglang}}" />
<input type="submit" value="Edit Your Email" id="submitform" class="btn btn-default" />
</form>
<form action="/manage/{{program.getUrlBase}}/commfinal" method="post" name="mainback">
Expand All @@ -80,7 +81,7 @@ <h2>Preview</h2>
<input type="hidden" name="from" value="{{from}}" />
<input type="hidden" name="replyto" value="{{replyto}}" />
<input type="hidden" name="subject" value="{{subject}}" />
<input type="hidden" name="body" value="{{body}}" />
<input type="hidden" name="body" value="{{htmlbody}}" />
<input type="submit" value="Send Message!" id="submitform" class="btn btn-primary" />
</form>

Expand Down
71 changes: 56 additions & 15 deletions esp/templates/program/modules/commmodule/step2.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,28 @@

{% block xtrajs %}
{{block.super}}
<script type="text/javascript">
<!--

//-->
</script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script type="text/javascript">
{% include "program/modules/commmodule/commpanel_step2.js" %}
</script>
<script type="text/javascript">
<!-- If coming back from preview, check the language used before -->
window.onload = function() {
if ('{{ msglang }}' != '')
{
document.getElementById("{{msglang}}").checked = "checked";
}
};
</script>
{% endblock %}
{% block stylesheets %}
{{block.super}}
<style type="text/css">
.listtable, .listtable th, .listtable td{ border: 1px solid #999; border-collapse: collapse;}
.listtable, .listtable th, .listtable td{ border: 1px solid #999;
border-collapse: collapse;}
#divmaintext .unchosen { background-color: #CCC; }
#divmaintext .chosen { background-color: #FFF; }
#divmaintext td.notchooser { cursor: pointer }
Expand All @@ -30,23 +42,51 @@ <h2>Step 2:</h2>

<br />
<p>
There are <strong>{{listcount}}</strong> distinct users in your query{% if selected %} of <strong>{{ selected }}</strong>{% endif %}. If you feel this to be off, please
There are <strong>{{listcount}}</strong> distinct users in your
query{% if selected %} of <strong>{{ selected }}</strong>{% endif %}.
If you feel this to be off, please
<a href="javascript:history.go(-1);">go back</a> now.<br />
<br />
Please write your email/message now. Note that you can use <tt>{{ parameter }}</tt> to enter a parameter
into the text of the message&mdash;the parameters are listed below.

<p><b>Tip:</b> If you would like to send your message in HTML format, wrap your entire message body in &lt;html&gt; and &lt;/html&gt; tags.</p>
Please write your email message now. Note that you can use
<tt>&#123;&#123;parameter&#125;&#125;</tt> to enter a parameter into the text of the
message&mdash;the parameters are listed below. The default
{{ settings.ORGANIZATION_SHORT_NAME }} template (which you can modify
<a href="/admin/utils/templateoverride/?q=default_email.html">here</a>)
is automatically wrapped around your message below.
You can choose to write the body in either plain text or HTML.
If you choose to use HTML, there is no need for <tt>&lt;html&gt;</tt>
and <tt>&lt;/html&gt;</tt> tags because they're are included in the wrapper
template. If you choose plain text, do <b>not</b> use any HTML tags for
whitespace.</p>

<form action="/manage/{{program.getUrlBase}}/commprev"
onsubmit="return validateMsgLang()" name="comm2" method="post">

<form action="/manage/{{program.getUrlBase}}/commprev" method="post" name="comm2">
<table border="0" cellspacing="0" width="100%">
<tr>
<input type="radio" name="msglang" id="plaintext" value="plaintext">Plain Text
<br />
<input type="radio" name="msglang" id="html" value="html" required>HTML

<td>
<label for="from">
<strong>From:</strong>
<small>(If blank: your email)</small>
<small>(If blank: your email <span class="glyphicon glyphicon-info-sign"
onclick="showEmailExplanation()"></span>)</small>
</label><br />
<input type="text" size="30" name="from" id="from" value="{{from}}" />
<p class="help-block" id="from-help" style="display:none;"><small>"Your email"
is an alias,
<span onclick="hideEmailExplanation()"
style="float:right;cursor:pointer;">&times;</span>
<script type="text/javascript">
document.write(esp_user.cur_username);</script>@{{ settings.SITE_INFO.1 }},
for your listed email (<script type="text/javascript">
document.write(esp_user.cur_email);</script>),
which will receive all replies from and be visible to all
recipients.</small>
</p>
<br />
<label for="replyto">
<strong>Reply-To:</strong>
Expand All @@ -57,12 +97,12 @@ <h2>Step 2:</h2>
<label for="subject">
<strong>Subject:</strong>
</label><br />
<input type="text" size="30" name="subject" id="subject" value="{{subject}}" />
<input type="text" size="30" name="subject" id="subject" value="{{subject}}" required />
<br /><br />
<label for="emailbody">
<strong>Body:</strong>
</label> <br />
<textarea cols="80" rows="20" name="body" id="emailbody">{{body}}</textarea>
<textarea cols="80" rows="20" name="body" id="emailbody" required />{{body}}</textarea>
<br />

</td>
Expand Down Expand Up @@ -148,7 +188,8 @@ <h2>Step 2:</h2>
{% templatetag openvariable %}program.full_classes{% templatetag closevariable %}
</span>
</th>
<td>Newline-separated list of user's classes that have full or nearly-full sections</td>
<td>Newline-separated list of user's classes that have full or
nearly-full sections</td>
</tr>
<tr>
<th><span style="white-space: nowrap;">
Expand Down