diff options
Diffstat (limited to 'lib/gitlab/email')
11 files changed, 161 insertions, 25 deletions
diff --git a/lib/gitlab/email/handler/service_desk_handler.rb b/lib/gitlab/email/handler/service_desk_handler.rb index 74c8d0a1fd7..8d73aa842be 100644 --- a/lib/gitlab/email/handler/service_desk_handler.rb +++ b/lib/gitlab/email/handler/service_desk_handler.rb @@ -15,16 +15,14 @@ module Gitlab PROJECT_KEY_PATTERN = /\A(?<slug>.+)-(?<key>[a-z0-9_]+)\z/.freeze def initialize(mail, mail_key, service_desk_key: nil) - super(mail, mail_key) - - if service_desk_key.present? + if service_desk_key + mail_key ||= service_desk_key @service_desk_key = service_desk_key - elsif !mail_key&.include?('/') && (matched = HANDLER_REGEX.match(mail_key.to_s)) - @project_slug = matched[:project_slug] - @project_id = matched[:project_id]&.to_i - elsif matched = HANDLER_REGEX_LEGACY.match(mail_key.to_s) - @project_path = matched[:project_path] end + + super(mail, mail_key) + + match_project_slug || match_legacy_project_slug end def can_handle? @@ -42,15 +40,29 @@ module Gitlab end end + def match_project_slug + return if mail_key&.include?('/') + return unless matched = HANDLER_REGEX.match(mail_key.to_s) + + @project_slug = matched[:project_slug] + @project_id = matched[:project_id]&.to_i + end + + def match_legacy_project_slug + return unless matched = HANDLER_REGEX_LEGACY.match(mail_key.to_s) + + @project_path = matched[:project_path] + end + def metrics_event :receive_email_service_desk end def project strong_memoize(:project) do - @project = service_desk_key ? project_from_key : super - @project = nil unless @project&.service_desk_enabled? - @project + project_record = super + project_record ||= project_from_key if service_desk_key + project_record&.service_desk_enabled? ? project_record : nil end end @@ -96,7 +108,7 @@ module Gitlab end def message_including_template - description = message_including_reply + description = process_message(trim_reply: false, allow_only_quotes: true) template_content = service_desk_setting&.issue_template_content if template_content.present? diff --git a/lib/gitlab/email/message/in_product_marketing.rb b/lib/gitlab/email/message/in_product_marketing.rb index fb4315e74b2..ac9585bcd1a 100644 --- a/lib/gitlab/email/message/in_product_marketing.rb +++ b/lib/gitlab/email/message/in_product_marketing.rb @@ -7,7 +7,8 @@ module Gitlab UnknownTrackError = Class.new(StandardError) def self.for(track) - raise UnknownTrackError unless Namespaces::InProductMarketingEmailsService::TRACKS.key?(track) + valid_tracks = [Namespaces::InviteTeamEmailService::TRACK, Namespaces::InProductMarketingEmailsService::TRACKS.keys].flatten + raise UnknownTrackError unless valid_tracks.include?(track) "Gitlab::Email::Message::InProductMarketing::#{track.to_s.classify}".constantize end diff --git a/lib/gitlab/email/message/in_product_marketing/admin_verify.rb b/lib/gitlab/email/message/in_product_marketing/admin_verify.rb index 234b93594b5..19d9cf99cdb 100644 --- a/lib/gitlab/email/message/in_product_marketing/admin_verify.rb +++ b/lib/gitlab/email/message/in_product_marketing/admin_verify.rb @@ -36,6 +36,10 @@ module Gitlab def progress super(track_name: 'Admin') end + + def invite_members? + invite_members_for_task_experiment_enabled? + end end end end diff --git a/lib/gitlab/email/message/in_product_marketing/base.rb b/lib/gitlab/email/message/in_product_marketing/base.rb index c4895d35a14..7cd54390b9f 100644 --- a/lib/gitlab/email/message/in_product_marketing/base.rb +++ b/lib/gitlab/email/message/in_product_marketing/base.rb @@ -7,16 +7,17 @@ module Gitlab class Base include Gitlab::Email::Message::InProductMarketing::Helper include Gitlab::Routing + include Gitlab::Experiment::Dsl attr_accessor :format def initialize(group:, user:, series:, format: :html) - raise ArgumentError, "Only #{total_series} series available for this track." unless series.between?(0, total_series - 1) - + @series = series @group = group @user = user - @series = series @format = format + + validate_series! end def subject_line @@ -56,6 +57,18 @@ module Gitlab end end + def invite_members? + false + end + + def invite_text + s_('InProductMarketing|Do you have a teammate who would be perfect for this task?') + end + + def invite_link + action_link(s_('InProductMarketing|Invite them to help out.'), group_url(group, open_modal: 'invite_members_for_task')) + end + def unsubscribe parts = Gitlab.com? ? unsubscribe_com : unsubscribe_self_managed(track, series) @@ -102,6 +115,10 @@ module Gitlab ["mailers/in_product_marketing", "#{track}-#{series}.png"].join('/') end + def series? + total_series > 0 + end + protected attr_reader :group, :user, :series @@ -148,6 +165,20 @@ module Gitlab link(s_('InProductMarketing|update your preferences'), preference_link) end + + def invite_members_for_task_experiment_enabled? + return unless user.can?(:admin_group_member, group) + + experiment(:invite_members_for_task, namespace: group) do |e| + e.candidate { true } + e.record! + e.run + end + end + + def validate_series! + raise ArgumentError, "Only #{total_series} series available for this track." unless @series.between?(0, total_series - 1) + end end end end diff --git a/lib/gitlab/email/message/in_product_marketing/create.rb b/lib/gitlab/email/message/in_product_marketing/create.rb index 4b0c4af4911..2c396775374 100644 --- a/lib/gitlab/email/message/in_product_marketing/create.rb +++ b/lib/gitlab/email/message/in_product_marketing/create.rb @@ -61,6 +61,10 @@ module Gitlab ][series] end + def invite_members? + invite_members_for_task_experiment_enabled? + end + private def project_link diff --git a/lib/gitlab/email/message/in_product_marketing/experience.rb b/lib/gitlab/email/message/in_product_marketing/experience.rb index 4156a737517..7520de6d2a3 100644 --- a/lib/gitlab/email/message/in_product_marketing/experience.rb +++ b/lib/gitlab/email/message/in_product_marketing/experience.rb @@ -43,7 +43,9 @@ module Gitlab survey_id: EASE_SCORE_SURVEY_ID } - "#{Gitlab::Saas.com_url}/-/survey_responses?#{params.to_query}" + params[:show_incentive] = true if show_incentive? + + "#{gitlab_com_root_url}/-/survey_responses?#{params.to_query}" end def feedback_ratings(rating) @@ -70,9 +72,19 @@ module Gitlab def show_invite_link strong_memoize(:show_invite_link) do - group.member_count > 1 && group.max_member_access_for_user(user) >= GroupMember::DEVELOPER && user.preferred_language == 'en' + group.max_member_access_for_user(user) >= GroupMember::DEVELOPER && user.preferred_language == 'en' end end + + def show_incentive? + show_invite_link && group.member_count > 1 + end + + def gitlab_com_root_url + return root_url.chomp('/') if Rails.env.development? + + Gitlab::Saas.com_url + end end end end diff --git a/lib/gitlab/email/message/in_product_marketing/helper.rb b/lib/gitlab/email/message/in_product_marketing/helper.rb index cec0aad44a6..bffa90ed4ec 100644 --- a/lib/gitlab/email/message/in_product_marketing/helper.rb +++ b/lib/gitlab/email/message/in_product_marketing/helper.rb @@ -36,6 +36,15 @@ module Gitlab "#{text} (#{link})" end end + + def action_link(text, link) + case format + when :html + ActionController::Base.helpers.link_to text, link, target: '_blank', rel: 'noopener noreferrer' + else + [text, link].join(' >> ') + end + end end end end diff --git a/lib/gitlab/email/message/in_product_marketing/invite_team.rb b/lib/gitlab/email/message/in_product_marketing/invite_team.rb new file mode 100644 index 00000000000..e9334b687f4 --- /dev/null +++ b/lib/gitlab/email/message/in_product_marketing/invite_team.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Gitlab + module Email + module Message + module InProductMarketing + class InviteTeam < Base + def subject_line + s_('InProductMarketing|Invite your teammates to GitLab') + end + + def tagline + '' + end + + def title + s_('InProductMarketing|GitLab is better with teammates to help out!') + end + + def subtitle + '' + end + + def body_line1 + s_('InProductMarketing|Invite your teammates today and build better code together. You can even assign tasks to new teammates such as setting up CI/CD, to help get projects up and running.') + end + + def body_line2 + '' + end + + def cta_text + s_('InProductMarketing|Invite your teammates to help') + end + + def logo_path + 'mailers/in_product_marketing/team-0.png' + end + + def series? + false + end + + private + + def validate_series! + raise ArgumentError, "Only one email is sent for this track. Value of `series` should be 0." unless @series == 0 + end + end + end + end + end +end diff --git a/lib/gitlab/email/message/in_product_marketing/verify.rb b/lib/gitlab/email/message/in_product_marketing/verify.rb index e731c65121e..daf0c969f2b 100644 --- a/lib/gitlab/email/message/in_product_marketing/verify.rb +++ b/lib/gitlab/email/message/in_product_marketing/verify.rb @@ -65,6 +65,10 @@ module Gitlab ][series] end + def invite_members? + invite_members_for_task_experiment_enabled? + end + private def ci_link diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index 242def826be..526f1188065 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -44,6 +44,10 @@ module Gitlab } end + def mail + strong_memoize(:mail) { build_mail } + end + private def handler @@ -54,10 +58,6 @@ module Gitlab Handler.for(mail, mail_key) end - def mail - strong_memoize(:mail) { build_mail } - end - def build_mail Mail::Message.new(@raw) rescue Encoding::UndefinedConversionError, diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb index 0f0f4800062..d39fa139abb 100644 --- a/lib/gitlab/email/reply_parser.rb +++ b/lib/gitlab/email/reply_parser.rb @@ -4,12 +4,13 @@ module Gitlab module Email class ReplyParser - attr_accessor :message + attr_accessor :message, :allow_only_quotes - def initialize(message, trim_reply: true, append_reply: false) + def initialize(message, trim_reply: true, append_reply: false, allow_only_quotes: false) @message = message @trim_reply = trim_reply @append_reply = append_reply + @allow_only_quotes = allow_only_quotes end def execute @@ -25,7 +26,12 @@ module Gitlab # NOTE: We currently don't support empty quotes. # EmailReplyTrimmer allows this as a special case, # so we detect it manually here. - return "" if body.lines.all? { |l| l.strip.empty? || l.start_with?('>') } + # + # If allow_only_quotes is true a message where all lines starts with ">" is allowed. + # This could happen if an email has an empty quote, forwarded without any new content. + return "" if body.lines.all? do |l| + l.strip.empty? || (!allow_only_quotes && l.start_with?('>')) + end encoded_body = body.force_encoding(encoding).encode("UTF-8") return encoded_body unless @append_reply |