summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-11-12 21:06:30 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-11-12 21:06:30 +0000
commit6e3880e2bb5a404467c64edc916d9edf7556d09d (patch)
tree95cfb0de65623b7015b9d1a8c9ebb559a9358033
parent6d31b8f052d30b7e55128d17b66bceed8c6065a9 (diff)
downloadgitlab-ce-6e3880e2bb5a404467c64edc916d9edf7556d09d.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/stylesheets/mailer.scss117
-rw-r--r--app/assets/stylesheets/mailer_client_specific.scss65
-rw-r--r--app/assets/stylesheets/pages/projects.scss2
-rw-r--r--app/helpers/application_helper.rb9
-rw-r--r--app/mailers/emails/members.rb37
-rw-r--r--app/mailers/emails/pipelines.rb5
-rw-r--r--app/mailers/emails/releases.rb8
-rw-r--r--app/mailers/previews/notify_preview.rb6
-rw-r--r--app/models/release.rb4
-rw-r--r--app/views/layouts/_mailer.html.haml72
-rw-r--r--app/views/notify/member_access_denied_email.html.haml11
-rw-r--r--app/views/notify/member_access_granted_email.html.haml16
-rw-r--r--app/views/notify/member_access_requested_email.html.haml9
-rw-r--r--app/views/notify/member_invite_accepted_email.html.haml13
-rw-r--r--app/views/notify/member_invite_declined_email.html.haml11
-rw-r--r--app/views/notify/member_invited_email.html.haml27
-rw-r--r--app/views/projects/blob/_header.html.haml13
-rw-r--r--changelogs/unreleased/31868-make-name-optional-parameter-of-release-entity.yml5
-rw-r--r--changelogs/unreleased/35537-button-regression-fix.yml5
-rw-r--r--changelogs/unreleased/36113-visual-design-of-edit-and-web-ide-button-in-blob-view.yml5
-rw-r--r--changelogs/unreleased/feat-unify-html-email-layouts.yml5
-rw-r--r--config/application.rb2
-rw-r--r--doc/api/releases/index.md2
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md4
-rw-r--r--doc/development/contributing/issue_workflow.md2
-rw-r--r--doc/user/application_security/sast/analyzers.md2
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/releases.rb2
-rw-r--r--spec/fixtures/api/schemas/release.json3
-rw-r--r--spec/mailers/emails/releases_spec.rb1
-rw-r--r--spec/models/evidence_spec.rb2
-rw-r--r--spec/models/release_spec.rb14
33 files changed, 362 insertions, 123 deletions
diff --git a/app/assets/stylesheets/mailer.scss b/app/assets/stylesheets/mailer.scss
new file mode 100644
index 00000000000..f7d93870a25
--- /dev/null
+++ b/app/assets/stylesheets/mailer.scss
@@ -0,0 +1,117 @@
+@import 'framework/variables';
+
+// Do not use 3-letter hex codes, bgcolor vs css background-color is problematic in emails
+// See https://stackoverflow.com/questions/28551981/why-are-3-digit-hex-color-code-values-interpreted-differently-in-internet-explor
+//
+// stylelint-disable color-hex-length
+
+$mailer-font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+$mailer-text-color: #333333;
+$mailer-bg-color: #fafafa;
+$mailer-link-color: #3777b0;
+$mailer-link-muted-color: #333333;
+$mailer-line-cell-bg-color: #6b4fbb;
+$mailer-wrapper-cell-bg-color: #ffffff;
+$mailer-wrapper-cell-border-color: #ededed;
+$mailer-header-footer-text-color: #5c5c5c;
+
+body {
+ margin: 0 !important;
+ background-color: $mailer-bg-color;
+ padding: 0;
+ text-align: center;
+ min-width: 640px;
+ width: 100%;
+ height: 100%;
+ font-family: $mailer-font;
+}
+
+table#body {
+ background-color: $mailer-bg-color;
+ margin: 0;
+ padding: 0;
+ text-align: center;
+ min-width: 640px;
+ width: 100%;
+}
+
+a {
+ color: $mailer-link-color;
+ text-decoration: none;
+
+ &.muted {
+ color: $mailer-link-muted-color;
+ }
+}
+
+.highlight {
+ font-weight: 500;
+}
+
+tr td {
+ font-family: $mailer-font;
+}
+
+tr.line td {
+ font-family: $mailer-font;
+ background-color: $mailer-line-cell-bg-color;
+ height: 4px;
+ font-size: 4px;
+ line-height: 4px;
+}
+
+tr.header td,
+tr.footer td,
+td.footer-message {
+ font-family: $mailer-font;
+ padding: 25px 0;
+ font-size: 13px;
+ line-height: 1.6;
+ color: $mailer-header-footer-text-color;
+}
+
+table.wrapper {
+ width: 640px;
+ margin: 0 auto;
+ border-collapse: separate;
+ border-spacing: 0;
+
+ td.wrapper-cell {
+ font-family: $mailer-font;
+ background-color: $mailer-wrapper-cell-bg-color;
+ text-align: left;
+ padding: 18px 25px;
+ border: 1px solid $mailer-wrapper-cell-border-color;
+ border-radius: 3px;
+ overflow: hidden;
+ }
+}
+
+table.content {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0;
+
+ td.text-content {
+ font-family: $mailer-font;
+ color: $mailer-text-color;
+ font-size: 15px;
+ font-weight: 400;
+ line-height: 1.4;
+ padding: 15px 5px;
+ text-align: center;
+ }
+}
+
+tr.footer td {
+ img {
+ display: block;
+ margin: 0 auto 1em;
+ }
+
+ .mng-notif-link,
+ .help-link {
+ color: $mailer-link-color;
+ text-decoration: none;
+ }
+}
diff --git a/app/assets/stylesheets/mailer_client_specific.scss b/app/assets/stylesheets/mailer_client_specific.scss
new file mode 100644
index 00000000000..41bedecf90f
--- /dev/null
+++ b/app/assets/stylesheets/mailer_client_specific.scss
@@ -0,0 +1,65 @@
+/* CLIENT-SPECIFIC STYLES */
+
+// These are client-specific rules, ignore some linting rules
+//
+// stylelint-disable property-no-vendor-prefix, property-no-unknown, length-zero-no-unit
+// scss-lint:disable PropertySpelling, ZeroUnit
+
+body,
+table,
+td,
+a {
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+table,
+td {
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+}
+
+img {
+ -ms-interpolation-mode: bicubic;
+}
+
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
+}
+
+/* iOS BLUE LINKS */
+a[x-apple-data-detectors] {
+ color: inherit !important;
+ text-decoration: none !important;
+ font-size: inherit !important;
+ font-family: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+}
+
+/* ANDROID MARGIN HACK */
+div[style*='margin: 16px 0'] {
+ margin: 0 !important;
+}
+
+@media only screen and (max-width: 639px) {
+ body,
+ #body {
+ min-width: 320px !important;
+ }
+
+ table.wrapper {
+ width: 100% !important;
+ min-width: 320px !important;
+ }
+
+ table.wrapper td.wrapper-cell {
+ border-left: 0 !important;
+ border-right: 0 !important;
+ border-radius: 0 !important;
+ padding-left: 10px !important;
+ padding-right: 10px !important;
+ }
+}
+
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index a50d31f15df..d96cc163738 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -270,7 +270,7 @@
}
.count-badge,
- .btn {
+ .btn-xs {
height: 24px;
}
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index ecaeb7060c8..dcd7434b9ea 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -324,6 +324,15 @@ module ApplicationHelper
}
end
+ def asset_to_string(name)
+ app = Rails.application
+ if Rails.configuration.assets.compile
+ app.assets.find_asset(name).to_s
+ else
+ controller.view_context.render(file: File.join('public/assets', app.assets_manifest.assets[name]))
+ end
+ end
+
private
def appearance
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index ea8032324aa..06d2219d6a9 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -15,16 +15,18 @@ module Emails
user = User.find(recipient_id)
- mail(to: user.notification_email_for(notification_group),
- subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
+ member_email_with_layout(
+ to: user.notification_email_for(notification_group),
+ subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end
def member_access_granted_email(member_source_type, member_id)
@member_source_type = member_source_type
@member_id = member_id
- mail(to: member.user.notification_email_for(notification_group),
- subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted"))
+ member_email_with_layout(
+ to: member.user.notification_email_for(notification_group),
+ subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted"))
end
def member_access_denied_email(member_source_type, source_id, user_id)
@@ -33,8 +35,9 @@ module Emails
user = User.find(user_id)
- mail(to: user.notification_email_for(notification_group),
- subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied"))
+ member_email_with_layout(
+ to: user.notification_email_for(notification_group),
+ subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied"))
end
def member_invited_email(member_source_type, member_id, token)
@@ -42,8 +45,9 @@ module Emails
@member_id = member_id
@token = token
- mail(to: member.invite_email,
- subject: subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}"))
+ member_email_with_layout(
+ to: member.invite_email,
+ subject: subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end
def member_invite_accepted_email(member_source_type, member_id)
@@ -51,8 +55,9 @@ module Emails
@member_id = member_id
return unless member.created_by
- mail(to: member.created_by.notification_email_for(notification_group),
- subject: subject('Invitation accepted'))
+ member_email_with_layout(
+ to: member.created_by.notification_email_for(notification_group),
+ subject: subject('Invitation accepted'))
end
def member_invite_declined_email(member_source_type, source_id, invite_email, created_by_id)
@@ -64,8 +69,9 @@ module Emails
user = User.find(created_by_id)
- mail(to: user.notification_email_for(notification_group),
- subject: subject('Invitation declined'))
+ member_email_with_layout(
+ to: user.notification_email_for(notification_group),
+ subject: subject('Invitation declined'))
end
def member
@@ -85,5 +91,12 @@ module Emails
def member_source_class
@member_source_type.classify.constantize
end
+
+ def member_email_with_layout(to:, subject:)
+ mail(to: to, subject: subject) do |format|
+ format.html { render layout: 'mailer' }
+ format.text { render layout: 'mailer' }
+ end
+ end
end
end
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index 34e12a5fa6d..95bb52d8f97 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -18,12 +18,11 @@ module Emails
@merge_request = pipeline.all_merge_requests.first
add_headers
- # We use bcc here because we don't want to generate this emails for a
+ # We use bcc here because we don't want to generate these emails for a
# thousand times. This could be potentially expensive in a loop, and
# recipients would contain all project watchers so it could be a lot.
mail(bcc: recipients,
- subject: pipeline_subject(status),
- skip_premailer: true) do |format|
+ subject: pipeline_subject(status)) do |format|
format.html { render layout: 'mailer' }
format.text { render layout: 'mailer' }
end
diff --git a/app/mailers/emails/releases.rb b/app/mailers/emails/releases.rb
index 137858d31e8..c9c77ab9333 100644
--- a/app/mailers/emails/releases.rb
+++ b/app/mailers/emails/releases.rb
@@ -21,7 +21,13 @@ module Emails
private
def release_email_subject
- release_info = [@release.name, @release.tag].select(&:presence).join(' - ')
+ release_info =
+ if @release.name == @release.tag
+ @release.tag
+ else
+ [@release.name, @release.tag].select(&:presence).join(' - ')
+ end
+
"New release: #{release_info}"
end
end
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index 3d42423ba46..381a4f54d9e 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -77,7 +77,7 @@ class NotifyPreview < ActionMailer::Preview
end
def import_issues_csv_email
- Notify.import_issues_csv_email(user, project, { success: 3, errors: [5, 6, 7], valid_file: true })
+ Notify.import_issues_csv_email(user.id, project.id, { success: 3, errors: [5, 6, 7], valid_file: true })
end
def closed_merge_request_email
@@ -109,11 +109,11 @@ class NotifyPreview < ActionMailer::Preview
end
def member_access_requested_email
- Notify.member_access_requested_email('group', user.id, user.id).message
+ Notify.member_access_requested_email(member.source_type, member.id, user.id).message
end
def member_invite_accepted_email
- Notify.member_invite_accepted_email('project', user.id).message
+ Notify.member_invite_accepted_email(member.source_type, member.id).message
end
def member_invite_declined_email
diff --git a/app/models/release.rb b/app/models/release.rb
index ec40e8ec3e2..401e8359f47 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -69,6 +69,10 @@ class Release < ApplicationRecord
released_at.present? && released_at > Time.zone.now
end
+ def name
+ self.read_attribute(:name) || tag
+ end
+
private
def actual_sha
diff --git a/app/views/layouts/_mailer.html.haml b/app/views/layouts/_mailer.html.haml
index 6e8294d6adc..24b8138078d 100644
--- a/app/views/layouts/_mailer.html.haml
+++ b/app/views/layouts/_mailer.html.haml
@@ -1,80 +1,48 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
%html{ lang: "en" }
%head
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
%title= message.subject
- :css
- /* CLIENT-SPECIFIC STYLES */
- body, table, td, a { -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
- table, td { mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
- img { -ms-interpolation-mode: bicubic; }
- .hidden {
- display: none !important;
- visibility: hidden !important;
- }
- /* iOS BLUE LINKS */
- a[x-apple-data-detectors] {
- color: inherit !important;
- text-decoration: none !important;
- font-size: inherit !important;
- font-family: inherit !important;
- font-weight: inherit !important;
- line-height: inherit !important;
- }
+ -# Avoid premailer processing of client-specific styles (@media tag not supported)
+ -# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook)
+ -# do not support linked stylesheets.
+ %style{ type: 'text/css', 'data-premailer': 'ignore' }
+ = asset_to_string('mailer_client_specific.css').html_safe
- /* ANDROID MARGIN HACK */
- body { margin:0 !important; }
- div[style*="margin: 16px 0"] { margin:0 !important; }
-
- @media only screen and (max-width: 639px) {
- body, #body {
- min-width: 320px !important;
- }
- table.wrapper {
- width: 100% !important;
- min-width: 320px !important;
- }
- table.wrapper > tbody > tr > td {
- border-left: 0 !important;
- border-right: 0 !important;
- border-radius: 0 !important;
- padding-left: 10px !important;
- padding-right: 10px !important;
- }
- }
- %body{ style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;height:100%;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table#body{ border: "0", cellpadding: "0", cellspacing: "0", style: "background-color:#fafafa;margin:0;padding:0;text-align:center;min-width:640px;width:100%;" }
+ = stylesheet_link_tag 'mailer.css'
+ %body
+ %table#body{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody
%tr.line
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }
+ %td
%tr.header
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %td
= html_header_message
= header_logo
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
- %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:640px;margin:0 auto;border-collapse:separate;border-spacing:0;" }
+ %td
+ %table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#ffffff;text-align:left;padding:18px 25px;border:1px solid #ededed;border-radius:3px;overflow:hidden;" }
- %table.content{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;border-collapse:separate;border-spacing:0;" }
+ %td.wrapper-cell
+ %table.content{ border: "0", cellpadding: "0", cellspacing: "0" }
%tbody
= yield
= render_if_exists 'layouts/mailer/additional_text'
%tr.footer
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
- %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
+ %td
+ %img{ alt: "GitLab", height: "33", width: "90", src: image_url('mailers/gitlab_footer_logo.gif') }
%div
- - manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, style: "color:#3777b0;text-decoration:none;")
- - help_link = link_to(_("Help"), help_url, style: "color:#3777b0;text-decoration:none;")
+ - manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, class: 'mng-notif-link')
+ - help_link = link_to(_("Help"), help_url, class: 'help-link')
= _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
= yield :additional_footer
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ %td.footer-message
= html_footer_message
diff --git a/app/views/notify/member_access_denied_email.html.haml b/app/views/notify/member_access_denied_email.html.haml
index 71c9c50071a..11661a423dd 100644
--- a/app/views/notify/member_access_denied_email.html.haml
+++ b/app/views/notify/member_access_denied_email.html.haml
@@ -1,4 +1,7 @@
-%p
- Your request to join the
- #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}
- has been denied.
+%tr
+ %td.text-content
+ %p
+ Your request to join the
+ #{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}
+ has been #{content_tag :span, 'denied', class: :highlight}.
+
diff --git a/app/views/notify/member_access_granted_email.html.haml b/app/views/notify/member_access_granted_email.html.haml
index 1c50dba9c97..e28a10a243f 100644
--- a/app/views/notify/member_access_granted_email.html.haml
+++ b/app/views/notify/member_access_granted_email.html.haml
@@ -1,10 +1,14 @@
- link_end = '</a>'.html_safe
- source_type = member_source.model_name.singular
- leave_link = polymorphic_url([member_source], leave: 1)
-- source_link = link_to(member_source.human_name, member_source.web_url, target: '_blank', rel: 'noopener noreferrer')
+- source_link = link_to(member_source.human_name, member_source.web_url, target: '_blank', rel: 'noopener noreferrer', class: :highlight)
+- access_level = content_tag(:span, member.human_access, class: :highlight)
+
+%tr
+ %td.text-content
+ %p
+ = _('You have been granted %{access_level} access to the %{source_link} %{source_type}.').html_safe % { access_level: access_level, source_link: source_link, source_type: source_type }
+ %p
+ - leave_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: leave_link }
+ = _('If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}.').html_safe % { source_type: source_type, leave_link_start: leave_link_start, link_end: link_end }
-%p
- = _('You have been granted %{access_level} access to the %{source_link} %{source_type}.').html_safe % { access_level: member.human_access, source_link: source_link, source_type: source_type }
-%p
- - leave_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: leave_link }
- = _('If this was a mistake you can %{leave_link_start}leave the %{source_type}%{link_end}.').html_safe % { source_type: source_type, leave_link_start: leave_link_start, link_end: link_end }
diff --git a/app/views/notify/member_access_requested_email.html.haml b/app/views/notify/member_access_requested_email.html.haml
index 76f1f08a0cb..43f25af3dba 100644
--- a/app/views/notify/member_access_requested_email.html.haml
+++ b/app/views/notify/member_access_requested_email.html.haml
@@ -1,3 +1,6 @@
-%p
- #{link_to member.user.name, member.user} requested #{member.human_access}
- access to the #{link_to member_source.human_name, polymorphic_url([member_source, :members])} #{member_source.model_name.singular}.
+%tr
+ %td.text-content
+ %p
+ #{link_to member.user.name, member.user, class: :highlight} requested #{content_tag :span, member.human_access, class: :highlight}
+ access to the #{link_to member_source.human_name, polymorphic_url([member_source, :members]), class: :highlight} #{member_source.model_name.singular}.
+
diff --git a/app/views/notify/member_invite_accepted_email.html.haml b/app/views/notify/member_invite_accepted_email.html.haml
index 2d1d40881eb..0abb79000e0 100644
--- a/app/views/notify/member_invite_accepted_email.html.haml
+++ b/app/views/notify/member_invite_accepted_email.html.haml
@@ -1,5 +1,8 @@
-%p
- #{member.invite_email}, now known as
- #{link_to member.user.name, user_url(member.user)},
- has accepted your invitation to join the
- #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}.
+%tr
+ %td.text-content
+ %p
+ #{content_tag :span, member.invite_email, class: :highlight}, now known as
+ #{link_to member.user.name, user_url(member.user)},
+ has accepted your invitation to join the
+ #{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}.
+
diff --git a/app/views/notify/member_invite_declined_email.html.haml b/app/views/notify/member_invite_declined_email.html.haml
index aa1b373d1a6..5e626767235 100644
--- a/app/views/notify/member_invite_declined_email.html.haml
+++ b/app/views/notify/member_invite_declined_email.html.haml
@@ -1,4 +1,7 @@
-%p
- #{@invite_email}
- has declined your invitation to join the
- #{link_to member_source.human_name, member_source.web_url} #{member_source.model_name.singular}.
+%tr
+ %td.text-content
+ %p
+ #{content_tag :span, @invite_email, class: :highlight}
+ has #{content_tag :span, 'declined', class: :highlight} your invitation to join the
+ #{link_to member_source.human_name, member_source.web_url, class: :highlight} #{member_source.model_name.singular}.
+
diff --git a/app/views/notify/member_invited_email.html.haml b/app/views/notify/member_invited_email.html.haml
index 6730172242b..ae3fecf404a 100644
--- a/app/views/notify/member_invited_email.html.haml
+++ b/app/views/notify/member_invited_email.html.haml
@@ -1,13 +1,16 @@
-%p
- You have been invited
- - if member.created_by
- by
- = link_to member.created_by.name, user_url(member.created_by)
- to join the
- = link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token)
- #{member_source.model_name.singular} as #{member.human_access}.
+%tr
+ %td.text-content
+ %p
+ You have been invited
+ - if member.created_by
+ by
+ = link_to member.created_by.name, user_url(member.created_by)
+ to join the
+ = link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token), class: :highlight
+ #{member_source.model_name.singular} as #{content_tag :span, member.human_access, class: :highlight}.
+
+ %p
+ = link_to 'Accept invitation', invite_url(@token)
+ or
+ = link_to 'decline', decline_invite_url(@token)
-%p
- = link_to 'Accept invitation', invite_url(@token)
- or
- = link_to 'decline', decline_invite_url(@token)
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 84ccd816d80..77245114772 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -6,17 +6,18 @@
= render 'projects/blob/viewer_switcher', blob: blob unless blame
.btn-group{ role: "group" }<
- = copy_blob_source_button(blob) unless blame
- = open_raw_blob_button(blob)
- = download_blob_button(blob)
- = view_on_environment_button(@commit.sha, @path, @environment) if @environment
- .btn-group{ role: "group" }<
- = render_if_exists 'projects/blob/header_file_locks_link'
= edit_blob_button
= ide_edit_button
+ .btn-group{ role: "group" }<
+ = render_if_exists 'projects/blob/header_file_locks_link'
- if current_user
= replace_blob_link
= delete_blob_link
+ .btn-group{ role: "group" }<
+ = copy_blob_source_button(blob) unless blame
+ = open_raw_blob_button(blob)
+ = download_blob_button(blob)
+ = view_on_environment_button(@commit.sha, @path, @environment) if @environment
= render 'projects/fork_suggestion'
= render_if_exists 'projects/blob/header_file_locks', project: @project, path: @path
diff --git a/changelogs/unreleased/31868-make-name-optional-parameter-of-release-entity.yml b/changelogs/unreleased/31868-make-name-optional-parameter-of-release-entity.yml
new file mode 100644
index 00000000000..4ba2af01dfa
--- /dev/null
+++ b/changelogs/unreleased/31868-make-name-optional-parameter-of-release-entity.yml
@@ -0,0 +1,5 @@
+---
+title: Made `name` optional parameter of Release entity
+merge_request: 19705
+author:
+type: changed
diff --git a/changelogs/unreleased/35537-button-regression-fix.yml b/changelogs/unreleased/35537-button-regression-fix.yml
new file mode 100644
index 00000000000..05c2e7a5a62
--- /dev/null
+++ b/changelogs/unreleased/35537-button-regression-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Revert btn-xs styling in projects scss
+merge_request: 19640
+author:
+type: fixed
diff --git a/changelogs/unreleased/36113-visual-design-of-edit-and-web-ide-button-in-blob-view.yml b/changelogs/unreleased/36113-visual-design-of-edit-and-web-ide-button-in-blob-view.yml
new file mode 100644
index 00000000000..30583144a9e
--- /dev/null
+++ b/changelogs/unreleased/36113-visual-design-of-edit-and-web-ide-button-in-blob-view.yml
@@ -0,0 +1,5 @@
+---
+title: Visual design for edit buttons in blob view
+merge_request: 19932
+author:
+type: other
diff --git a/changelogs/unreleased/feat-unify-html-email-layouts.yml b/changelogs/unreleased/feat-unify-html-email-layouts.yml
new file mode 100644
index 00000000000..ff3a54e9c17
--- /dev/null
+++ b/changelogs/unreleased/feat-unify-html-email-layouts.yml
@@ -0,0 +1,5 @@
+---
+title: Unify html email layout for member html emails
+merge_request: 17699
+author: Diego Louzán
+type: added
diff --git a/config/application.rb b/config/application.rb
index 894e107e7da..1d32ebcaa54 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -157,6 +157,8 @@ module Gitlab
config.assets.paths << "#{config.root}/vendor/assets/fonts"
config.assets.precompile << "print.css"
+ config.assets.precompile << "mailer.css"
+ config.assets.precompile << "mailer_client_specific.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "page_bundles/ide.css"
diff --git a/doc/api/releases/index.md b/doc/api/releases/index.md
index ee2df3e4c5d..7f41e237401 100644
--- a/doc/api/releases/index.md
+++ b/doc/api/releases/index.md
@@ -303,7 +303,7 @@ POST /projects/:id/releases
| Attribute | Type | Required | Description |
| -------------------| --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
-| `name` | string | yes | The release name. |
+| `name` | string | no | The release name. |
| `tag_name` | string | yes | The tag where the release will be created from. |
| `description` | string | yes | The description of the release. You can use [markdown](../../user/markdown.md). |
| `ref` | string | yes, if `tag_name` doesn't exist | If `tag_name` doesn't exist, the release will be created from `ref`. It can be a commit SHA, another tag name, or a branch name. |
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index e1c59f3b025..ffcc8195395 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -38,7 +38,7 @@ that's tested and deployed on every push to the `master` branch of the [codebase
This will also provide
boilerplate code for starting a browser-based game with the following components:
-- Written in [Typescript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io)
+- Written in [TypeScript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io)
- Building, running, and testing with [Gulp](https://gulpjs.com)
- Unit tests with [Chai](https://www.chaijs.com) and [Mocha](https://mochajs.org/)
- CI/CD with GitLab
@@ -508,7 +508,7 @@ deploy:
## Conclusion
Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you can also find a handful of boilerplate code to get
-[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing
+[TypeScript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](https://www.darknova.io).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code.
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 349bb371835..f32400d44a2 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -268,7 +268,7 @@ Each issue scheduled for the current milestone should be labeled ~Deliverable
or ~"Stretch". Any open issue for a previous milestone should be labeled
~"Next Patch Release", or otherwise rescheduled to a different milestone.
-#### Priority labels
+### Priority labels
Priority labels help us define the time a ~bug fix should be completed. Priority determines how quickly the defect turnaround time must be.
If there are multiple defects, the priority decides which defect has to be fixed immediately versus later.
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index 04dd75446a9..6eb2ca71e71 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -25,7 +25,7 @@ SAST supports the following official analyzers:
- [`security-code-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan) (Security Code Scan (.NET))
- [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix))
- [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT))
-- [`tslint`](https://gitlab.com/gitlab-org/security-products/analyzers/tslint) (TSLint (Typescript))
+- [`tslint`](https://gitlab.com/gitlab-org/security-products/analyzers/tslint) (TSLint (TypeScript))
The analyzers are published as Docker images that SAST will use to launch
dedicated containers for each analysis.
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index ab466e535d9..615eb072ea7 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -78,7 +78,7 @@ The following table shows which languages, package managers and frameworks are s
| Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/PyCQA/bandit) | 10.3 |
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
| Scala ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
-| Typescript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 |
+| TypeScript | [TSLint config security](https://github.com/webschik/tslint-config-security/) | 11.9 |
NOTE: **Note:**
The Java analyzers can also be used for variants like the
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 5819b366f7f..5aa16b117fd 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1299,7 +1299,9 @@ module API
class Release < Grape::Entity
include ::API::Helpers::Presentable
- expose :name
+ expose :name do |release, _|
+ can_download_code? ? release.name : "Release-#{release.id}"
+ end
expose :tag, as: :tag_name, if: ->(_, _) { can_download_code? }
expose :description
expose :description_html do |entity|
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 4238529142c..3f600ef4a04 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -45,7 +45,7 @@ module API
end
params do
requires :tag_name, type: String, desc: 'The name of the tag', as: :tag
- requires :name, type: String, desc: 'The name of the release'
+ optional :name, type: String, desc: 'The name of the release'
requires :description, type: String, desc: 'The release notes'
optional :ref, type: String, desc: 'The commit sha or branch name'
optional :assets, type: Hash do
diff --git a/spec/fixtures/api/schemas/release.json b/spec/fixtures/api/schemas/release.json
index 86f0f27606c..b0296e5e62d 100644
--- a/spec/fixtures/api/schemas/release.json
+++ b/spec/fixtures/api/schemas/release.json
@@ -1,9 +1,10 @@
{
"type": "object",
- "required": ["name", "tag_name"],
+ "required": ["tag_name", "description"],
"properties": {
"name": { "type": "string" },
"tag_name": { "type": "string" },
+ "ref": { "type": "string "},
"description": { "type": "string" },
"description_html": { "type": "string" },
"created_at": { "type": "date" },
diff --git a/spec/mailers/emails/releases_spec.rb b/spec/mailers/emails/releases_spec.rb
index 19f404db2a6..c614c009434 100644
--- a/spec/mailers/emails/releases_spec.rb
+++ b/spec/mailers/emails/releases_spec.rb
@@ -18,6 +18,7 @@ describe Emails::Releases do
context 'when the release has a name' do
it 'shows the correct subject' do
+ release.name = 'beta-1'
expected_subject = "#{release.project.name} | New release: #{release.name} - #{release.tag}"
is_expected.to have_subject(expected_subject)
end
diff --git a/spec/models/evidence_spec.rb b/spec/models/evidence_spec.rb
index 00788c2c391..8f534517fc1 100644
--- a/spec/models/evidence_spec.rb
+++ b/spec/models/evidence_spec.rb
@@ -27,7 +27,7 @@ describe Evidence do
let(:release) { create(:release, project: project, name: nil) }
it 'creates a valid JSON object' do
- expect(release.name).to be_nil
+ expect(release.name).to eq(release.tag)
expect(summary_json).to match_schema(schema_file)
end
end
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index e1622888d79..f9c7a14f1f3 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Release do
expect(existing_release_without_name).to be_valid
expect(existing_release_without_name.description).to eq("change")
- expect(existing_release_without_name.name).to be_nil
+ expect(existing_release_without_name.name).not_to be_nil
end
end
@@ -129,4 +129,16 @@ RSpec.describe Release do
end
end
end
+
+ describe '#name' do
+ context 'name is nil' do
+ before do
+ release.update(name: nil)
+ end
+
+ it 'returns tag' do
+ expect(release.name).to eq(release.tag)
+ end
+ end
+ end
end