Commit 7e76ac08 authored by Alper Akgun's avatar Alper Akgun

Merge branch 'credit-card-verification-email' into 'master'

Account validation email action and view

See merge request gitlab-org/gitlab!75080
parents 1024b284 5b3f7bf0
...@@ -21,6 +21,12 @@ module Emails ...@@ -21,6 +21,12 @@ module Emails
mail_to(to: email, subject: @message.subject_line) mail_to(to: email, subject: @message.subject_line)
end end
def account_validation_email(pipeline, recipient_email)
@message = Gitlab::Email::Message::AccountValidation.new(pipeline)
mail_to(to: recipient_email, subject: @message.subject_line)
end
private private
def mail_to(to:, subject:) def mail_to(to:, subject:)
......
...@@ -55,16 +55,23 @@ ...@@ -55,16 +55,23 @@
.cta_link a { .cta_link a {
font-size: 24px; font-size: 24px;
font-family: 'Source Sans Pro', helvetica, arial, sans-serif; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;
color: #ffffff;
text-decoration: none; text-decoration: none;
display: inline-block;
}
.cta_link_primary a {
color: #ffffff;
border-radius: 5px; border-radius: 5px;
-webkit-border-radius: 5px;
background-color: #6e49cb; background-color: #6e49cb;
border-top: 15px solid #6e49cb; border-top: 15px solid #6e49cb;
border-bottom: 15px solid #6e49cb; border-bottom: 15px solid #6e49cb;
border-right: 40px solid #6e49cb; border-right: 40px solid #6e49cb;
border-left: 40px solid #6e49cb; border-left: 40px solid #6e49cb;
display: inline-block; }
.cta_link_secondary a {
color: #6e49cb;
padding: 25px 40px 15px;
} }
.footernav { .footernav {
......
%tr
%td{ bgcolor: "#ffffff", height: "auto", style: "max-width: 600px; width: 100%; text-align: center; height: 200px; padding: 25px 15px; mso-line-height-rule: exactly; min-height: 40px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;", valign: "middle", width: "100%" }
= inline_image_link(@message.logo_path, { width: '150', style: 'width: 150px;' })
%h1{ style: "font-size: 40px; line-height: 46x; color: #000000; padding: 20px 0 0 0; font-weight: normal;" }
= @message.title
%tr
%td{ style: "padding: 10px 20px 30px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 18px; line-height: 24px;" }
%p{ style: "margin: 0 0 20px 0;" }
= @message.body_line1.html_safe
- @message.body_line2&.tap do |line|
%p{ style: "margin: 0 0 20px 0;" }
= line.html_safe
%tr
%td{ align: "center", style: "padding: 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" }
.cta_link.cta_link_primary= @message.cta_link
.cta_link.cta_link_secondary= @message.cta2_link
<%= @message.title %>
<%= @message.body_line1 %>
<%= @message.body_line2 %>
<%= @message.cta_link %>
<%= @message.cta2_link %>
<%= @message.footer_links %>
<%= @message.address %>
<%= @message.unsubscribe %>
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
- if @message.cta_text - if @message.cta_text
%tr %tr
%td{ align: "center", style: "padding: 10px 20px 80px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" } %td{ align: "center", style: "padding: 10px 20px 80px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif;" }
.cta_link= @message.cta_link .cta_link.cta_link_primary= @message.cta_link
- else - else
%tr %tr
%td{ style: "padding: 10px 20px 10px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 16px; line-height: 20px;" } %td{ style: "padding: 10px 20px 10px 20px; font-family: 'Source Sans Pro', helvetica, arial, sans-serif; color:#000000; font-size: 16px; line-height: 20px;" }
......
# frozen_string_literal: true
module Gitlab
module Email
module Message
class AccountValidation
include Gitlab::Email::Message::InProductMarketing::Helper
include Gitlab::Routing
attr_accessor :pipeline, :format
def initialize(pipeline, format: :html)
@pipeline = pipeline
@format = format
end
def subject_line
s_('AccountValidation|Fix your pipelines by validating your account')
end
def title
s_("AccountValidation|Looks like you’ll need to validate your account to use free pipeline minutes")
end
def body_line1
s_("AccountValidation|In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project.")
end
def body_line2
format_options = strong_options.merge({ learn_more_link: learn_more_link })
s_("AccountValidation|This is required to discourage and reduce the abuse on GitLab infrastructure. %{strong_start}GitLab will not charge or store your card, it will only be used for validation.%{strong_end} %{learn_more_link}").html_safe % format_options
end
def cta_text
s_('AccountValidation|Validate your account')
end
def cta2_text
s_("AccountValidation|I'll bring my own runners")
end
def logo_path
'mailers/in_product_marketing/verify-2.png'
end
def cta_link
url = project_pipeline_url(pipeline.project, pipeline)
case format
when :html
ActionController::Base.helpers.link_to cta_text, url, target: '_blank', rel: 'noopener noreferrer'
else
[cta_text, url].join(' >> ')
end
end
def cta2_link
url = 'https://docs.gitlab.com/runner/install/'
case format
when :html
ActionController::Base.helpers.link_to cta2_text, url, target: '_blank', rel: 'noopener noreferrer'
else
[cta2_text, url].join(' >> ')
end
end
def learn_more_link
link(s_('AccountValidation|Learn more.'), 'https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/')
end
def unsubscribe
parts = [
s_('AccountValidation|If you no longer wish to receive marketing emails from us,'),
s_('AccountValidation|you may %{unsubscribe_link} at any time.') % { unsubscribe_link: mailgun_unsubscribe_link }
]
case format
when :html
parts.join(' ')
else
parts.join("\n" + ' ' * 16)
end
end
private
def mailgun_unsubscribe_link
mailgun_unsubscribe_url = '%tag_unsubscribe_url%'
link(s_('AccountValidation|unsubscribe'), mailgun_unsubscribe_url)
end
end
end
end
end
...@@ -1877,6 +1877,36 @@ msgstr "" ...@@ -1877,6 +1877,36 @@ msgstr ""
msgid "Account: %{account}" msgid "Account: %{account}"
msgstr "" msgstr ""
msgid "AccountValidation|Fix your pipelines by validating your account"
msgstr ""
msgid "AccountValidation|I'll bring my own runners"
msgstr ""
msgid "AccountValidation|If you no longer wish to receive marketing emails from us,"
msgstr ""
msgid "AccountValidation|In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project."
msgstr ""
msgid "AccountValidation|Learn more."
msgstr ""
msgid "AccountValidation|Looks like you’ll need to validate your account to use free pipeline minutes"
msgstr ""
msgid "AccountValidation|This is required to discourage and reduce the abuse on GitLab infrastructure. %{strong_start}GitLab will not charge or store your card, it will only be used for validation.%{strong_end} %{learn_more_link}"
msgstr ""
msgid "AccountValidation|Validate your account"
msgstr ""
msgid "AccountValidation|unsubscribe"
msgstr ""
msgid "AccountValidation|you may %{unsubscribe_link} at any time."
msgstr ""
msgid "Action" msgid "Action"
msgstr "" msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Email::Message::AccountValidation do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
subject(:message) { described_class.new(pipeline) }
it 'contains the correct message', :aggregate_failures do
expect(message.subject_line).to eq 'Fix your pipelines by validating your account'
expect(message.title).to eq "Looks like you’ll need to validate your account to use free pipeline minutes"
expect(message.body_line1).to eq "In order to use free pipeline minutes on shared runners, you'll need to validate your account with a credit or debit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project."
expect(message.body_line2).to include(
'This is required to discourage and reduce the abuse on GitLab infrastructure.',
'<b>GitLab will not charge or store your card, it will only be used for validation.</b>',
'<a href="https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/">Learn more.</a>'
)
expect(message.cta_text).to eq 'Validate your account'
expect(message.cta2_text).to eq "I'll bring my own runners"
expect(message.logo_path).to eq 'mailers/in_product_marketing/verify-2.png'
expect(message.unsubscribe).to include('%tag_unsubscribe_url%')
end
end
...@@ -5,13 +5,35 @@ require 'email_spec' ...@@ -5,13 +5,35 @@ require 'email_spec'
RSpec.describe Emails::InProductMarketing do RSpec.describe Emails::InProductMarketing do
include EmailSpec::Matchers include EmailSpec::Matchers
include Gitlab::Routing.url_helpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
shared_examples 'has custom headers when on gitlab.com' do
context 'when on gitlab.com' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it 'has custom headers' do
aggregate_failures do
expect(subject).to deliver_from(described_class::FROM_ADDRESS)
expect(subject).to reply_to(described_class::FROM_ADDRESS)
expect(subject).to have_header('X-Mailgun-Track', 'yes')
expect(subject).to have_header('X-Mailgun-Track-Clicks', 'yes')
expect(subject).to have_header('X-Mailgun-Track-Opens', 'yes')
expect(subject).to have_header('X-Mailgun-Tag', 'marketing')
expect(subject).to have_body_text('%tag_unsubscribe_url%')
end
end
end
end
describe '#in_product_marketing_email' do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let!(:onboarding_progress) { create(:onboarding_progress, namespace: group) } let!(:onboarding_progress) { create(:onboarding_progress, namespace: group) }
describe '#in_product_marketing_email' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let(:track) { :create } let(:track) { :create }
...@@ -21,6 +43,8 @@ RSpec.describe Emails::InProductMarketing do ...@@ -21,6 +43,8 @@ RSpec.describe Emails::InProductMarketing do
include_context 'gitlab email notification' include_context 'gitlab email notification'
it_behaves_like 'has custom headers when on gitlab.com'
it 'sends to the right user with a link to unsubscribe' do it 'sends to the right user with a link to unsubscribe' do
aggregate_failures do aggregate_failures do
expect(subject).to deliver_to(user.notification_email_or_default) expect(subject).to deliver_to(user.notification_email_or_default)
...@@ -28,24 +52,6 @@ RSpec.describe Emails::InProductMarketing do ...@@ -28,24 +52,6 @@ RSpec.describe Emails::InProductMarketing do
end end
end end
context 'when on gitlab.com' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
it 'has custom headers' do
aggregate_failures do
expect(subject).to deliver_from(described_class::FROM_ADDRESS)
expect(subject).to reply_to(described_class::FROM_ADDRESS)
expect(subject).to have_header('X-Mailgun-Track', 'yes')
expect(subject).to have_header('X-Mailgun-Track-Clicks', 'yes')
expect(subject).to have_header('X-Mailgun-Track-Opens', 'yes')
expect(subject).to have_header('X-Mailgun-Tag', 'marketing')
expect(subject).to have_body_text('%tag_unsubscribe_url%')
end
end
end
where(:track, :series) do where(:track, :series) do
:create | 0 :create | 0
:create | 1 :create | 1
...@@ -102,4 +108,35 @@ RSpec.describe Emails::InProductMarketing do ...@@ -102,4 +108,35 @@ RSpec.describe Emails::InProductMarketing do
end end
end end
end end
describe '#account_validation_email' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
subject { Notify.account_validation_email(pipeline, user.notification_email_or_default) }
it 'sends to the right user with a link to unsubscribe' do
expect(subject).to deliver_to(user.notification_email_or_default)
end
it_behaves_like 'has custom headers when on gitlab.com'
it 'has the correct subject and content' do
message = Gitlab::Email::Message::AccountValidation.new(pipeline)
cta_url = project_pipeline_url(pipeline.project, pipeline)
cta2_url = 'https://docs.gitlab.com/runner/install/'
aggregate_failures do
is_expected.to have_subject(message.subject_line)
is_expected.to have_body_text(message.title)
is_expected.to have_body_text(message.body_line1)
is_expected.to have_body_text(CGI.unescapeHTML(message.body_line2))
is_expected.to have_body_text(CGI.unescapeHTML(message.cta_link))
is_expected.to have_body_text(CGI.unescapeHTML(message.cta2_link))
is_expected.to have_body_text(cta_url)
is_expected.to have_body_text(cta2_url)
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment