diff --git a/app/app/urls.py b/app/app/urls.py
index 94632f365e4..e6118502ede 100644
--- a/app/app/urls.py
+++ b/app/app/urls.py
@@ -540,6 +540,7 @@
path('_administration/email/wallpost', retail.emails.wallpost, name='wallpost_email'),
path('_administration/email/grant_update', retail.emails.grant_update, name='grant_update_email'),
path('_administration/email/grant_recontribute', retail.emails.grant_recontribute, name='grant_recontribute_email'),
+ path('_administration/email/grant_txn_failed', retail.emails.grant_txn_failed, name='grant_txn_failed_email'),
path(
'_administration/email/new_bounty_acceptance',
retail.emails.new_bounty_acceptance,
diff --git a/app/dashboard/management/commands/grant_txn_failed_email.py b/app/dashboard/management/commands/grant_txn_failed_email.py
new file mode 100644
index 00000000000..cea356c3898
--- /dev/null
+++ b/app/dashboard/management/commands/grant_txn_failed_email.py
@@ -0,0 +1,32 @@
+'''
+ Copyright (C) 2019 Gitcoin Core
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+'''
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+from grants.models import Contribution
+from marketing.mails import grant_txn_failed
+
+
+class Command(BaseCommand):
+ help = 'sends emails for grant contributors whose contribution txns have failed'
+
+ def handle(self, *args, **options):
+ if settings.DEBUG:
+ print("not active in non prod environments")
+ return
+
+ failed_contribs = Contribution.objects.exclude(validator_passed=True)
+
+ for failed_contrib in failed_contribs:
+ grant_txn_failed(failed_contrib.subscription.contributor_profile, failed_contrib.subscription.grant, failed_contrib.tx_id)
diff --git a/app/marketing/mails.py b/app/marketing/mails.py
index 99ba2fb662e..a36d3bdd938 100644
--- a/app/marketing/mails.py
+++ b/app/marketing/mails.py
@@ -36,7 +36,7 @@
render_bounty_expire_warning, render_bounty_feedback, render_bounty_request, render_bounty_startwork_expire_warning,
render_bounty_unintersted, render_comment, render_faucet_rejected, render_faucet_request,
render_featured_funded_bounty, render_funder_payout_reminder, render_funder_stale, render_gdpr_reconsent,
- render_gdpr_update, render_grant_cancellation_email, render_grant_recontribute, render_grant_update,
+ render_gdpr_update, render_grant_cancellation_email, render_grant_recontribute, render_grant_txn_failed, render_grant_update,
render_kudos_email, render_match_distribution, render_match_email, render_mention, render_new_bounty,
render_new_bounty_acceptance, render_new_bounty_rejection, render_new_bounty_roundup, render_new_grant_email,
render_new_supporter_email, render_new_work_submission, render_no_applicant_reminder, render_nth_day_email_campaign,
@@ -288,6 +288,25 @@ def grant_cancellation(grant, subscription):
finally:
translation.activate(cur_language)
+def grant_txn_failed(profile, grant, tx_id):
+ from_email = settings.CONTACT_EMAIL
+ to_email = profile.email
+ if not to_email:
+ if profile and profile.user:
+ to_email = profile.user.email
+ if not to_email:
+ return
+
+ cur_language = translation.get_language()
+
+ subject = f"Your Grant transaction failed. Wanna try again?"
+
+ try:
+ setup_lang(to_email)
+ html, text = render_grant_txn_failed(to_email, grant, tx_id)
+ send_mail(from_email, to_email, subject, text, html, categories=['transactional', func_name()])
+ finally:
+ translation.activate(cur_language)
def subscription_terminated(grant, subscription):
if subscription and subscription.negative:
diff --git a/app/retail/emails.py b/app/retail/emails.py
index 70b8ef49888..3b7f70db64a 100644
--- a/app/retail/emails.py
+++ b/app/retail/emails.py
@@ -69,6 +69,7 @@
('comment', _('Comment Emails'), _('Only when you are sent a comment')),
('wall_post', _('Wall Post Emails'), _('Only when someone writes on your wall')),
('grant_updates', _('Grant Update Emails'), _('Updates from Grant Owners about grants you\'ve funded.')),
+ ('grant_txn_failed', _('Grant Transaction Failed Emails'), _('Notifies Grant contributors when their contribution txn has failed.')),
]
@@ -887,6 +888,24 @@ def render_grant_recontribute(to_email, prev_round_start=(2020, 3, 23), prev_rou
return response_html, response_txt
+def render_grant_txn_failed(to_email, grant, tx_id):
+ email_style = 27
+
+ params = {
+ 'id': grant.id,
+ 'grant_title': grant.title,
+ 'tx_id': tx_id,
+ 'tx_url': "https://etherscan.io/tx/"+tx_id,
+ 'bulk_add_url': "https://gitcoin.co/grants/cart/bulk-add/" + str(grant.id),
+ 'email_style': email_style,
+ 'hide_bottom_logo': True,
+ }
+
+ response_html = premailer_transform(render_to_string("emails/grant_txn_failed.html", params))
+ response_txt = render_to_string("emails/grant_txn_failed.txt", params)
+
+ return response_html, response_txt
+
def render_wallpost(to_email, activity):
params = {
'activity': activity,
@@ -1375,6 +1394,11 @@ def grant_update(request):
def grant_recontribute(request):
response_html, _ = render_grant_recontribute(settings.CONTACT_EMAIL)
return HttpResponse(response_html)
+
+def grant_txn_failed(request):
+ failed_contrib = Contribution.objects.filter(subscription__contributor_profile__user__email=settings.CONTACT_EMAIL).exclude(validator_passed=True).first()
+ response_html, _ = render_grant_txn_failed(settings.CONTACT_EMAIL, failed_contrib.subscription.grant, failed_contrib.tx_id)
+ return HttpResponse(response_html)
@staff_member_required
def wallpost(request):
diff --git a/app/retail/templates/emails/grant_txn_failed.html b/app/retail/templates/emails/grant_txn_failed.html
new file mode 100644
index 00000000000..be3b3d837c5
--- /dev/null
+++ b/app/retail/templates/emails/grant_txn_failed.html
@@ -0,0 +1,59 @@
+{% extends 'emails/template.html' %}
+{% comment %}
+ Copyright (C) 2018 Gitcoin Core
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+{% endcomment %}
+{% load i18n humanize %}
+
+{% block content %}
+
+
+
+
Grants transaction failed, try again?
+
+
+
+ Your contribution to {{ grant_title }} failed due to a technical issue.
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/retail/templates/emails/grant_txn_failed.txt b/app/retail/templates/emails/grant_txn_failed.txt
new file mode 100644
index 00000000000..8a0f1241089
--- /dev/null
+++ b/app/retail/templates/emails/grant_txn_failed.txt
@@ -0,0 +1,7 @@
+Grants transaction failed, try again?
+
+ Your contribution to {{ grant_title }} failed due to a technical issue.
+
+ Transaction ID: {{ tx_id }}
+
+ View on Etherscan
\ No newline at end of file
diff --git a/app/retail/templates/emails/template.html b/app/retail/templates/emails/template.html
index fb85fb71768..78288e1f1f7 100644
--- a/app/retail/templates/emails/template.html
+++ b/app/retail/templates/emails/template.html
@@ -323,6 +323,18 @@
}
}
+ @media (min-width: 768px) {
+ .grant-txn-msg {
+ width: 70%;
+ }
+ }
+
+ @media (min-width: 1200px) {
+ .grant-txn-msg {
+ width: 60%;
+ }
+ }
+
/* Bootstrap styles used for activities -- START */
*,