Skip to content

Commit

Permalink
Merge pull request #33803 from dimagi/ad/use-project-logo-in-invite-e…
Browse files Browse the repository at this point in the history
…mail

FF for using project logo in system emails
  • Loading branch information
AddisonDunn committed Dec 21, 2023
2 parents ff48076 + a2c2e8d commit d31a5d3
Show file tree
Hide file tree
Showing 19 changed files with 273 additions and 67 deletions.
1 change: 1 addition & 0 deletions corehq/apps/domain/deletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ def _delete_demo_user_restores(domain_name):
ModelDeletion('events', 'AttendanceTrackingConfig', 'domain'),
ModelDeletion('geospatial', 'GeoConfig', 'domain'),
ModelDeletion('email', 'EmailSettings', 'domain'),
ModelDeletion('hqmedia', 'LogoForSystemEmailsReference', 'domain'),
]


Expand Down
69 changes: 56 additions & 13 deletions corehq/apps/domain/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
TransferDomainRequest,
all_restricted_ucr_expressions,
)
from corehq.apps.hqmedia.models import CommCareImage, LogoForSystemEmailsReference
from corehq.apps.hqwebapp import crispy as hqcrispy
from corehq.apps.hqwebapp.crispy import DatetimeLocalWidget, HQFormHelper
from corehq.apps.hqwebapp.fields import MultiCharField
Expand All @@ -119,13 +120,15 @@
GeoCoderInput,
Select2Ajax,
)
from corehq.apps.registration.utils import project_logo_emails_context
from corehq.apps.sms.phonenumbers_helper import parse_phone_number
from corehq.apps.users.models import CouchUser, WebUser
from corehq.toggles import (
HIPAA_COMPLIANCE_CHECKBOX,
MOBILE_UCR,
SECURE_SESSION_TIMEOUT,
TWO_STAGE_USER_PROVISIONING_BY_SMS,
USE_LOGO_IN_SYSTEM_EMAILS
)
from corehq.util.timezones.fields import TimeZoneField
from corehq.util.timezones.forms import TimeZoneChoiceField
Expand All @@ -136,6 +139,8 @@
# used to resize uploaded custom logos, aspect ratio is preserved
LOGO_SIZE = (211, 32)

upload_size_limit = f"{settings.MAX_UPLOAD_SIZE_ATTACHMENT/(1024*1024):,.0f}"


def tf_choices(true_txt, false_txt):
return (('false', false_txt), ('true', true_txt))
Expand Down Expand Up @@ -343,13 +348,21 @@ class DomainGlobalSettingsForm(forms.Form):
"Upload a custom image to display instead of the "
"CommCare HQ logo. It will be automatically resized to "
"a height of 32 pixels. Upload size limit is {size_limit} MB."
).format(size_limit=f"{settings.MAX_UPLOAD_SIZE_ATTACHMENT/(1024*1024):,.0f}")
).format(size_limit=upload_size_limit)
)
delete_logo = BooleanField(
label=gettext_lazy("Delete Logo"),
required=False,
help_text=gettext_lazy("Delete your custom logo and use the standard one.")
)
logo_for_system_emails = ImageField(
label=gettext_lazy("Logo to use in systems emails"),
required=False,
help_text=gettext_lazy(
"Upload an image to display in system emails from CommCare. It will be displayed in a square format. "
"The upload size limit is {size_limit} MB."
).format(size_limit=upload_size_limit)
)
call_center_enabled = BooleanField(
label=gettext_lazy("Call Center Application"),
required=False,
Expand Down Expand Up @@ -457,9 +470,9 @@ def __init__(self, *args, **kwargs):
super(DomainGlobalSettingsForm, self).__init__(*args, **kwargs)
self.helper = hqcrispy.HQFormHelper(self)
self.helper[5] = twbscrispy.PrependedText('delete_logo', '')
self.helper[6] = twbscrispy.PrependedText('call_center_enabled', '')
self.helper[14] = twbscrispy.PrependedText('release_mode_visibility', '')
self.helper[15] = twbscrispy.PrependedText('orphan_case_alerts_warning', '')
self.helper[7] = twbscrispy.PrependedText('call_center_enabled', '')
self.helper[15] = twbscrispy.PrependedText('release_mode_visibility', '')
self.helper[16] = twbscrispy.PrependedText('orphan_case_alerts_warning', '')
self.helper.all().wrap_together(crispy.Fieldset, _('Edit Basic Information'))
self.helper.layout.append(
hqcrispy.FormActions(
Expand All @@ -475,6 +488,9 @@ def __init__(self, *args, **kwargs):
if not self.can_use_custom_logo:
del self.fields['logo']
del self.fields['delete_logo']
self.system_emails_logo_enabled = USE_LOGO_IN_SYSTEM_EMAILS.enabled(self.domain)
if not self.system_emails_logo_enabled:
del self.fields['logo_for_system_emails']

if self.project:
if not self.project.call_center_config.enabled:
Expand Down Expand Up @@ -549,16 +565,28 @@ def clean_default_geocoder_location(self):
return data
return json.loads(data or '{}')

def clean_logo(self):
logo = self.cleaned_data['logo']
if self.can_use_custom_logo and logo:
if logo.size > settings.MAX_UPLOAD_SIZE_ATTACHMENT:
def _clean_image(self, field_name, permission, error_message):
image = self.cleaned_data[field_name]
if permission and image:
if image.size > settings.MAX_UPLOAD_SIZE_ATTACHMENT:
raise ValidationError(
_("Logo exceeds {} MB size limit").format(
f"{settings.MAX_UPLOAD_SIZE_ATTACHMENT/(1024*1024):,.0f}"
)
_(error_message)
)
return logo
return image

def clean_logo(self):
return self._clean_image(
'logo',
self.can_use_custom_logo,
_("Logo exceeds {} MB size limit").format(upload_size_limit)
)

def clean_logo_for_system_emails(self):
return self._clean_image(
'logo_for_system_emails',
self.system_emails_logo_enabled,
_("Logo for systems emails exceeds {} MB size limit").format(upload_size_limit)
)

def clean_confirmation_link_expiry(self):
data = self.cleaned_data['confirmation_link_expiry']
Expand Down Expand Up @@ -614,6 +642,18 @@ def _save_logo_configuration(self, domain):
elif self.cleaned_data['delete_logo']:
domain.delete_attachment(LOGO_ATTACHMENT)

def _save_logo_for_system_emails(self, domain_obj):
logo = self.cleaned_data['logo_for_system_emails']
if logo:
image_data = logo.read()
image = CommCareImage.get_by_data(image_data)
image.attach_data(image_data, original_filename='logo_for_systems_emails.png')
image.add_domain(domain_obj.name)
image.save()
ref, created = LogoForSystemEmailsReference.objects.get_or_create(domain=domain_obj.name)
ref.image_id = image._id
ref.save()

def _save_call_center_configuration(self, domain):
cc_config = domain.call_center_config
cc_config.enabled = self.cleaned_data.get('call_center_enabled', False)
Expand Down Expand Up @@ -674,8 +714,10 @@ def save(self, request, domain):
setting_obj.save()
try:
self._save_logo_configuration(domain)
if self.system_emails_logo_enabled:
self._save_logo_for_system_emails(domain)
except IOError as err:
messages.error(request, _('Unable to save custom logo: {}').format(err))
messages.error(request, _('Unable to save logo: {}').format(err))
self._save_call_center_configuration(domain)
self._save_timezone_configuration(domain)
self._save_account_confirmation_settings(domain)
Expand Down Expand Up @@ -1455,6 +1497,7 @@ def save(self, domain_override=None,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
}
c.update(project_logo_emails_context(None, couch_user=couch_user))
subject = render_to_string(subject_template_name, c)
# Email subject *must not* contain newlines
subject = ''.join(subject.splitlines())
Expand Down
1 change: 1 addition & 0 deletions corehq/apps/dump_reload/sql/dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@
FilteredModelIteratorBuilder('fixtures.LookupTable', SimpleFilter('domain')),
FilteredModelIteratorBuilder('fixtures.LookupTableRow', SimpleFilter('domain')),
FilteredModelIteratorBuilder('fixtures.LookupTableRowOwner', SimpleFilter('domain')),
FilteredModelIteratorBuilder('hqmedia.LogoForSystemEmailsReference', SimpleFilter('domain')),
FilteredModelIteratorBuilder('generic_inbound.ConfigurableAPI', SimpleFilter('domain')),
FilteredModelIteratorBuilder('generic_inbound.ConfigurableApiValidation', SimpleFilter('api__domain')),
FilteredModelIteratorBuilder('generic_inbound.RequestLog', SimpleFilter('domain')),
Expand Down
22 changes: 22 additions & 0 deletions corehq/apps/hqmedia/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 3.2.23 on 2023-11-22 15:36

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='LogoForSystemEmailsReference',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('domain', models.CharField(max_length=255, unique=True)),
('image_id', models.CharField(max_length=255)),
],
),
]
Empty file.
13 changes: 13 additions & 0 deletions corehq/apps/hqmedia/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime
from io import BytesIO

from django.db import models
from django.template.defaultfilters import filesizeformat
from django.urls import reverse
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -38,6 +39,7 @@
from corehq.apps.domain.models import LICENSE_LINKS, LICENSES
from corehq.apps.hqmedia.exceptions import BadMediaFileException
from corehq.blobs.mixin import CODES, BlobMixin
from corehq.util.view_utils import absolute_reverse

MULTIMEDIA_PREFIX = "jr://file/"
LOGO_ARCHIVE_KEY = 'logos'
Expand Down Expand Up @@ -1030,3 +1032,14 @@ def restore_logos(self):
has_restored = True
del self.archived_media[LOGO_ARCHIVE_KEY][slug]
return has_restored


class LogoForSystemEmailsReference(models.Model):
domain = models.CharField(max_length=255, unique=True, null=False)
# Doc ID of the CommCareImage the object references
image_id = models.CharField(max_length=255, null=False)

def full_url_to_image(self):
from corehq.apps.hqmedia.views import ViewMultimediaFile
image = CommCareImage.get(self.image_id)
return absolute_reverse(ViewMultimediaFile.urlname, args=[image.doc_type, image._id])
2 changes: 2 additions & 0 deletions corehq/apps/registration/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ def activation_24hr_reminder_email():
"first_name": user.first_name,
'url_prefix': get_static_url_prefix(),
}
from corehq.apps.registration.utils import project_logo_emails_context
email_context.update(project_logo_emails_context(request.domain))

message_plaintext = render_to_string(
'registration/email/confirm_account_reminder.txt', email_context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,64 +70,65 @@
            
</div>{# /preview-text #}

<div class="container" style="background-image: url('https://s3.amazonaws.com/dimagidotcom-staging-staticfiles/test/hero-bg.jpg'); line-height: 1.5em; font-size: 15px; font-weight: 400; color: #1c2126; width: 680px; background-color: #ffffff; margin: 0 auto; overflow: hidden; ">
<div class="hero" style="background-image: url('{{ url_prefix }}{% static 'registration/images/hero-bg.jpg' %}'); background-color: #333333; height: 330px; width: 100%; background-position: center; background-repeat: no-repeat; background-size: cover;" class="hero">

{# START Dimagi Logo #}
<div style="text-align: center; padding: 20px 0;">
<img src="{{ url_prefix }}{% static 'registration/images/dimagi.png' %}" width="53" height="25" />
</div>
{# END Dimagi Logo #}

{% block hero %}
{# START CommCare HQ Logo #}
<div style="text-align: center; padding-bottom: 10px;">
<img src="{{ url_prefix }}{% static 'registration/images/commcare.png' %}" width="250" height="52" />
</div>
{# END CommCare HQ Logo #}
{% block base_container %}
<div class="container" style="background-image: url('https://s3.amazonaws.com/dimagidotcom-staging-staticfiles/test/hero-bg.jpg'); line-height: 1.5em; font-size: 15px; font-weight: 400; color: #1c2126; width: 680px; background-color: #ffffff; margin: 0 auto; overflow: hidden; ">
<div class="hero" style="background-image: url('{{ url_prefix }}{% static 'registration/images/hero-bg.jpg' %}'); background-color: #333333; height: 330px; width: 100%; background-position: center; background-repeat: no-repeat; background-size: cover;">

{# START CTA Headline #}
<div style="text-align: center; font-weight: 800; color: #ffffff; font-size: 30px; line-height: 1.1em; padding-bottom: 15px;">
{% block cta_headline %}
CTA HEADLINE -- Replace!
{% endblock %}
{# START Dimagi Logo #}
<div style="text-align: center; padding: 20px 0;">
<img src="{{ url_prefix }}{% static 'registration/images/dimagi.png' %}" width="53" height="25" />
</div>
{# END CTA Headline #}

{# START CTA Button #}
<div style="text-align: center; margin-top: 0px;">
<a class="btn"
href="{% block cta_url %}#{% endblock %}"
style="color: #ffffff; text-decoration: none; padding: 15px 20px; background-color: #5c6ac5; border-radius: 5px; font-size: 14px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; -moz-border-radius: 5px; -webkit-border-radius: 5px; display: inline-block; cursor: pointer;">
{% block cta_text %}
CTA Text -- Replace!
{# END Dimagi Logo #}

{% block hero %}
{# START CommCare HQ Logo #}
<div style="text-align: center; padding-bottom: 10px;">
<img src="{{ url_prefix }}{% static 'registration/images/commcare.png' %}" width="250" height="52" />
</div>
{# END CommCare HQ Logo #}

{# START CTA Headline #}
<div style="text-align: center; font-weight: 800; color: #ffffff; font-size: 30px; line-height: 1.1em; padding-bottom: 15px;">
{% block cta_headline %}
CTA HEADLINE -- Replace!
{% endblock %}
</a>
</div>
{# END CTA Button #}
</div>
{# END CTA Headline #}

{% endblock %}
{# START CTA Button #}
<div style="text-align: center; margin-top: 0px;">
<a class="btn"
href="{% block cta_url %}#{% endblock %}"
style="color: #ffffff; text-decoration: none; padding: 15px 20px; background-color: #5c6ac5; border-radius: 5px; font-size: 14px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; -moz-border-radius: 5px; -webkit-border-radius: 5px; display: inline-block; cursor: pointer;">
{% block cta_text %}
CTA Text -- Replace!
{% endblock %}
</a>
</div>
{# END CTA Button #}

</div>{# /hero #}
{% endblock %}

<div class="content" style="padding: 20px 45px 35px; line-height: 1.2em; background-color: #f4f5fa; color: #1c2126;">
</div>{# /hero #}

<div style="text-align: center; font-size: 1.6em; line-height: 1.2em; margin: 15px 0;">
{% block lead_text %}Lead Text -- Replace!{% endblock %}
</div>
<div class="content" style="padding: 20px 45px 35px; line-height: 1.2em; background-color: #f4f5fa; color: #1c2126;">

{% block content %}
<div style="text-align: center; line-height: 1.2em; margin: 15px 0;">
{% block secondary_text %}
Secondary Text -- Replace!
{% endblock %}
<div style="text-align: center; font-size: 1.6em; line-height: 1.2em; margin: 15px 0;">
{% block lead_text %}Lead Text -- Replace!{% endblock %}
</div>
{% endblock %}

</div>{# /content #}
{% block content %}
<div style="text-align: center; line-height: 1.2em; margin: 15px 0;">
{% block secondary_text %}
Secondary Text -- Replace!
{% endblock %}
</div>
{% endblock %}

</div>{# /container #}
</div>{# /content #}

</div>{# /container #}
{% endblock %}

<div class="footer" style="width: 680px; margin: 0 auto; font-size: 13px; text-align: left; background-color: #ffffff;">
<div class="footer-content" style="padding: 20px 45px; line-height: 1.2em; color: #999999; font-size: .8em;">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{% extends 'registration/email/base_templates/_base.html' %}
{% load hq_shared_tags %}
{% block base_container %}
<div class="container" style="line-height: 1.5em; font-size: 15px; font-weight: 400; color: #1c2126; width: 680px; background-color: #ffffff; margin: 0 auto; overflow: hidden;">
<div style="background-color: #f4f5fa; width: 100%; background-repeat: no-repeat; background-size: cover; padding-top: 40px; padding-bottom: 40px">
<img src="{{ link_to_logo }}" style="height: 200px; width: 200px; display:block; margin:auto;"/>
</div>

<div class="content" style="padding: 0 45px 35px; line-height: 1.2em; background-color: #f4f5fa; color: #1c2126;">
{# START CTA Button #}
<div style="text-align: center; padding: 20px 0 20px; ">
<a class="btn"
href="{% block cta_url %}#{% endblock %}"
style="color: #ffffff; text-decoration: none; padding: 13px 21px; background-color: #5d70d2; border-radius: 5px; font-size: 14px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px; -moz-border-radius: 5px; -webkit-border-radius: 5px; display: inline-block; cursor: pointer;">
{% block cta_text %}
CTA Text -- Replace!
{% endblock %}
</a>
</div>
{# END CTA Button #}

{# START CTA Headline #}
<div style="text-align: center; font-weight: 800; font-size: 30px; line-height: 1.1em; padding: 10px 0;">
{% block cta_headline %}
CTA HEADLINE -- Replace!
{% endblock %}
</div>
{# END CTA Headline #}

<div style="text-align: center; font-size: 1.6em; line-height: 1.2em; margin: 15px 0;">
{% block lead_text %}Lead Text -- Replace!{% endblock %}
</div>

{% block content %}
<div style="text-align: center; line-height: 1.2em; margin: 15px 0;">
{% block secondary_text %}
Secondary Text -- Replace!
{% endblock %}
</div>
{% endblock %}

</div>{# /content #}

</div>{# /container #}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% extends "registration/email/_base.html" %}
{% extends base_container_template|default:"registration/email/base_templates/_base.html"%}
{% load i18n %}
{% load hq_shared_tags %}

Expand Down

0 comments on commit d31a5d3

Please sign in to comment.