...@@ -3,7 +3,7 @@ import Sortable from 'sortablejs'; ...@@ -3,7 +3,7 @@ import Sortable from 'sortablejs';
import Vue from 'vue'; import Vue from 'vue';
import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui'; import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits'; import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import { n__, s__ } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip'; import Tooltip from '~/vue_shared/directives/tooltip';
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
...@@ -67,10 +67,13 @@ export default Vue.extend({ ...@@ -67,10 +67,13 @@ export default Vue.extend({
!this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank !this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank
); );
}, },
counterTooltip() { issuesTooltip() {
const { issuesSize } = this.list; const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`;
return sprintf(__('%{issuesSize} issues'), { issuesSize });
}, },
// Only needed to make karma pass.
weightCountToolTip() {}, // eslint-disable-line vue/return-in-computed-property
caretTooltip() { caretTooltip() {
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
}, },
... ...
......
...@@ -82,7 +82,7 @@ export default { ...@@ -82,7 +82,7 @@ export default {
}; };
</script> </script>
<template> <template>
<form name="service-credentials-form" @submit.prevent="createRole({ roleArn, externalId })"> <form name="service-credentials-form">
<h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2> <h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2>
<p> <p>
{{ {{
...@@ -136,6 +136,7 @@ export default { ...@@ -136,6 +136,7 @@ export default {
:disabled="submitButtonDisabled" :disabled="submitButtonDisabled"
:loading="isCreatingRole" :loading="isCreatingRole"
:label="submitButtonLabel" :label="submitButtonLabel"
@click.prevent="createRole({ roleArn, externalId })"
/> />
</form> </form>
</template> </template>
...@@ -235,18 +235,23 @@ export default { ...@@ -235,18 +235,23 @@ export default {
>{{ error.tags.logger }} >{{ error.tags.logger }}
</gl-badge> </gl-badge>
</template> </template>
<h3>{{ __('Error details') }}</h3>
<ul> <ul>
<li v-if="GQLerror.gitlabCommit">
<strong class="bold">{{ __('GitLab commit') }}:</strong>
<gl-link :href="GQLerror.gitlabCommitPath">
<span>{{ GQLerror.gitlabCommit.substr(0, 10) }}</span>
</gl-link>
</li>
<li v-if="error.gitlab_issue"> <li v-if="error.gitlab_issue">
<span class="bold">{{ __('GitLab Issue') }}:</span> <strong class="bold">{{ __('GitLab Issue') }}:</strong>
<gl-link :href="error.gitlab_issue"> <gl-link :href="error.gitlab_issue">
<span>{{ error.gitlab_issue }}</span> <span>{{ error.gitlab_issue }}</span>
</gl-link> </gl-link>
</li> </li>
<li> <li>
<span class="bold">{{ __('Sentry event') }}:</span> <strong class="bold">{{ __('Sentry event') }}:</strong>
<gl-link <gl-link
class="d-inline-flex align-items-center"
v-track-event="trackClickErrorLinkToSentryOptions(GQLerror.externalUrl)" v-track-event="trackClickErrorLinkToSentryOptions(GQLerror.externalUrl)"
:href="GQLerror.externalUrl" :href="GQLerror.externalUrl"
target="_blank" target="_blank"
...@@ -256,25 +261,25 @@ export default { ...@@ -256,25 +261,25 @@ export default {
</gl-link> </gl-link>
</li> </li>
<li v-if="GQLerror.firstReleaseShortVersion"> <li v-if="GQLerror.firstReleaseShortVersion">
<span class="bold">{{ __('First seen') }}:</span> <strong class="bold">{{ __('First seen') }}:</strong>
{{ formatDate(GQLerror.firstSeen) }} {{ formatDate(GQLerror.firstSeen) }}
<gl-link :href="firstReleaseLink" target="_blank"> <gl-link :href="firstReleaseLink" target="_blank">
<span>{{ __('Release') }}: {{ GQLerror.firstReleaseShortVersion }}</span> <span>{{ __('Release') }}: {{ GQLerror.firstReleaseShortVersion.substr(0, 10) }}</span>
</gl-link> </gl-link>
</li> </li>
<li v-if="GQLerror.lastReleaseShortVersion"> <li v-if="GQLerror.lastReleaseShortVersion">
<span class="bold">{{ __('Last seen') }}:</span> <strong class="bold">{{ __('Last seen') }}:</strong>
{{ formatDate(GQLerror.lastSeen) }} {{ formatDate(GQLerror.lastSeen) }}
<gl-link :href="lastReleaseLink" target="_blank"> <gl-link :href="lastReleaseLink" target="_blank">
<span>{{ __('Release') }}: {{ GQLerror.lastReleaseShortVersion }}</span> <span>{{ __('Release') }}: {{ GQLerror.lastReleaseShortVersion.substr(0, 10) }}</span>
</gl-link> </gl-link>
</li> </li>
<li> <li>
<span class="bold">{{ __('Events') }}:</span> <strong class="bold">{{ __('Events') }}:</strong>
<span>{{ GQLerror.count }}</span> <span>{{ GQLerror.count }}</span>
</li> </li>
<li> <li>
<span class="bold">{{ __('Users') }}:</span> <strong class="bold">{{ __('Users') }}:</strong>
<span>{{ GQLerror.userCount }}</span> <span>{{ GQLerror.userCount }}</span>
</li> </li>
</ul> </ul>
... ...
......
...@@ -13,6 +13,8 @@ query errorDetails($fullPath: ID!, $errorId: ID!) { ...@@ -13,6 +13,8 @@ query errorDetails($fullPath: ID!, $errorId: ID!) {
externalUrl externalUrl
firstReleaseShortVersion firstReleaseShortVersion
lastReleaseShortVersion lastReleaseShortVersion
gitlabCommit
gitlabCommitPath
} }
} }
} }
...@@ -60,6 +60,11 @@ export default { ...@@ -60,6 +60,11 @@ export default {
); );
} }
}, },
lastCommitMsg() {
this.isCompact =
this.currentActivityView !== activityBarViews.commit && this.lastCommitMsg === '';
},
}, },
methods: { methods: {
...mapActions(['updateActivityBarView']), ...mapActions(['updateActivityBarView']),
... ...
......
...@@ -60,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -60,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController
end end
def update_registration def update_registration
user_params = params.require(:user).permit(:name, :role, :setup_for_company) user_params = params.require(:user).permit(:role, :setup_for_company)
result = ::Users::SignupService.new(current_user, user_params).execute result = ::Users::SignupService.new(current_user, user_params).execute
if result[:status] == :success if result[:status] == :success
...@@ -152,13 +152,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -152,13 +152,7 @@ class RegistrationsController < Devise::RegistrationsController
end end
def sign_up_params def sign_up_params
clean_params = params.require(:user).permit(:username, :email, :email_confirmation, :name, :password) params.require(:user).permit(:username, :email, :email_confirmation, :name, :first_name, :last_name, :password)
if experiment_enabled?(:signup_flow)
clean_params[:name] = clean_params[:username]
end
clean_params
end end
def resource_name def resource_name
... ...
......
...@@ -164,9 +164,9 @@ class User < ApplicationRecord ...@@ -164,9 +164,9 @@ class User < ApplicationRecord
# Validations # Validations
# #
# Note: devise :validatable above adds validations for :email and :password # Note: devise :validatable above adds validations for :email and :password
validates :name, presence: true, length: { maximum: 128 } validates :name, presence: true, length: { maximum: 255 }
validates :first_name, length: { maximum: 255 } validates :first_name, length: { maximum: 127 }
validates :last_name, length: { maximum: 255 } validates :last_name, length: { maximum: 127 }
validates :email, confirmation: true validates :email, confirmation: true
validates :notification_email, presence: true validates :notification_email, presence: true
validates :notification_email, devise_email: true, if: ->(user) { user.notification_email != user.email } validates :notification_email, devise_email: true, if: ->(user) { user.notification_email != user.email }
... ...
......
...@@ -24,7 +24,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -24,7 +24,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
commits_anchor_data, commits_anchor_data,
branches_anchor_data, branches_anchor_data,
tags_anchor_data, tags_anchor_data,
files_anchor_data files_anchor_data,
releases_anchor_data
].compact.select(&:is_link) ].compact.select(&:is_link)
end end
...@@ -153,6 +154,22 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -153,6 +154,22 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
empty_repo? ? nil : project_tree_path(project)) empty_repo? ? nil : project_tree_path(project))
end end
def releases_anchor_data
return unless can?(current_user, :read_release, project)
releases_count = project.releases.count
return if releases_count < 1
AnchorData.new(true,
statistic_icon('rocket') +
n_('%{strong_start}%{release_count}%{strong_end} Release', '%{strong_start}%{release_count}%{strong_end} Releases', releases_count).html_safe % {
release_count: number_with_delimiter(releases_count),
strong_start: '<strong class="project-stat-value">'.html_safe,
strong_end: '</strong>'.html_safe
},
project_releases_path(project))
end
def commits_anchor_data def commits_anchor_data
AnchorData.new(true, AnchorData.new(true,
statistic_icon('commit') + statistic_icon('commit') +
... ...
......
...@@ -86,6 +86,8 @@ module Users ...@@ -86,6 +86,8 @@ module Users
:email_confirmation, :email_confirmation,
:password_automatically_set, :password_automatically_set,
:name, :name,
:first_name,
:last_name,
:password, :password,
:username :username
] ]
...@@ -107,6 +109,12 @@ module Users ...@@ -107,6 +109,12 @@ module Users
if user_params[:skip_confirmation].nil? if user_params[:skip_confirmation].nil?
user_params[:skip_confirmation] = skip_user_confirmation_email_from_setting user_params[:skip_confirmation] = skip_user_confirmation_email_from_setting
end end
fallback_name = "#{user_params[:first_name]} #{user_params[:last_name]}"
if user_params[:name].blank? && fallback_name.present?
user_params = user_params.merge(name: fallback_name)
end
end end
if user_default_internal_regex_enabled? && !user_params.key?(:external) if user_default_internal_regex_enabled? && !user_params.key?(:external)
... ...
......
- content_for(:page_title, _('Register for GitLab')) - content_for(:page_title, _('Register for GitLab'))
- max_first_name_length = max_last_name_length = 127
- max_username_length = 255 - max_username_length = 255
.signup-box.p-3.mb-2 .signup-box.p-3.mb-2
.signup-body .signup-body
...@@ -7,9 +8,16 @@ ...@@ -7,9 +8,16 @@
= render "devise/shared/error_messages", resource: resource = render "devise/shared/error_messages", resource: resource
- if Feature.enabled?(:invisible_captcha) - if Feature.enabled?(:invisible_captcha)
= invisible_captcha = invisible_captcha
.name.form-row
.col.form-group
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
= f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First Name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_firstname_field' }, required: true, title: _("This field is required.")
.col.form-group
= f.label :last_name, _('Last name'), for: 'new_user_last_name', class: 'label-bold'
= f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last Name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_lastname_field' }, required: true, title: _("This field is required.")
.username.form-group .username.form-group
= f.label :username, class: 'label-bold' = f.label :username, class: 'label-bold'
= f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") = f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :max_length => max_username_length, :max_length_message => _("Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.")
%p.validation-error.gl-field-error-ignore.field-validation.mt-1.hide.cred= _('Username is already taken.') %p.validation-error.gl-field-error-ignore.field-validation.mt-1.hide.cred= _('Username is already taken.')
%p.validation-success.gl-field-error-ignore.field-validation.mt-1.hide.cgreen= _('Username is available.') %p.validation-success.gl-field-error-ignore.field-validation.mt-1.hide.cgreen= _('Username is available.')
%p.validation-pending.gl-field-error-ignore.field-validation.mt-1.hide= _('Checking username availability...') %p.validation-pending.gl-field-error-ignore.field-validation.mt-1.hide= _('Checking username availability...')
... ...
......
- max_name_length = 128 - max_name_length = 255
- max_username_length = 255 - max_username_length = 255
#register-pane.tab-pane.login-box{ role: 'tabpanel' } #register-pane.tab-pane.login-box{ role: 'tabpanel' }
.login-body .login-body
... ...
......
- content_for(:page_title, _('Welcome to GitLab @%{username}!') % { username: current_user.username }) - content_for(:page_title, _('Welcome to GitLab %{name}!') % { name: current_user.name })
- max_name_length = 128
.text-center.mb-3 .text-center.mb-3
= _('In order to tailor your experience with GitLab we<br>would like to know a bit more about you.').html_safe = _('In order to tailor your experience with GitLab we<br>would like to know a bit more about you.').html_safe
.signup-box.p-3.mb-2 .signup-box.p-3.mb-2
...@@ -7,9 +6,6 @@ ...@@ -7,9 +6,6 @@
= form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| = form_for(current_user, url: users_sign_up_update_registration_path, html: { class: 'new_new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f|
.devise-errors.mt-0 .devise-errors.mt-0
= render 'devise/shared/error_messages', resource: current_user = render 'devise/shared/error_messages', resource: current_user
.name.form-group
= f.label :name, _('Full name'), class: 'label-bold'
= f.text_field :name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_name_length, :max_length_message => s_('Name is too long (maximum is %{max_length} characters).') % { max_length: max_name_length }, :qa_selector => 'new_user_name_field' }, required: true, title: _('This field is required.')
.form-group .form-group
= f.label :role, _('Role'), class: 'label-bold' = f.label :role, _('Role'), class: 'label-bold'
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control' = f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, {}, class: 'form-control'
... ...
......
...@@ -42,9 +42,10 @@ ...@@ -42,9 +42,10 @@
%button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" } %button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash") = icon("trash")
.issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo", ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } } .issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo" }
%span.d-inline-flex %span.d-inline-flex
%span.issue-count-badge-count %gl-tooltip{ ":target" => "() => $refs.issueCount", ":title" => "issuesTooltip" }
%span.issue-count-badge-count{ "ref" => "issueCount" }
%icon.mr-1{ name: "issues" } %icon.mr-1{ name: "issues" }
%issue-count{ ":maxIssueCount" => "list.maxIssueCount", %issue-count{ ":maxIssueCount" => "list.maxIssueCount",
":issuesSize" => "list.issuesSize" } ":issuesSize" => "list.issuesSize" }
... ...
......
---
title: Fix unexpected behaviour of the commit form after committing in Web IDE
merge_request: 23238
author:
type: fixed
---
title: Add release count to project homepage
merge_request: 21350
author:
type: added
---
title: 'fix: EKS credentials form does not reset after error'
merge_request: 21958
author:
type: other
---
title: Link to GitLab commit in Sentry error details page
merge_request: 22431
author:
type: added
---
title: Update name max length
merge_request: 22840
author:
type: changed
# GitLab instance administration project # GitLab instance administration project
NOTE: **Note:** NOTE: **Note:**
This feature is not yet available and is [planned for 12.6](https://gitlab.com/gitlab-org/gitlab/issues/32351). This feature is available behind a feature flag called `self_monitoring_project`
since [12.7](https://gitlab.com/gitlab-org/gitlab/issues/32351). The feature flag
will be removed once we [add dashboards to display metrics](https://gitlab.com/groups/gitlab-org/-/epics/2367).
GitLab has been adding the ability for administrators to see insights into the health of GitLab has been adding the ability for administrators to see insights into the health of
their GitLab instance. In order to surface this experience in a native way, similar to how their GitLab instance. In order to surface this experience in a native way, similar to how
... ...
......
...@@ -305,7 +305,7 @@ the report JSON unless stated otherwise. Presence of optional fields depends on ...@@ -305,7 +305,7 @@ the report JSON unless stated otherwise. Presence of optional fields depends on
| `vulnerabilities[].location.dependency.package.name` | Name of the package where the vulnerability is located. | | `vulnerabilities[].location.dependency.package.name` | Name of the package where the vulnerability is located. |
| `vulnerabilities[].location.dependency.version` | Version of the vulnerable package. Optional. | | `vulnerabilities[].location.dependency.version` | Version of the vulnerable package. Optional. |
| `vulnerabilities[].location.operating_system` | The operating system that contains the vulnerable package. | | `vulnerabilities[].location.operating_system` | The operating system that contains the vulnerable package. |
| `vulnerabilities[].location.image` | The Docker image that was analyzed. Optional. | | `vulnerabilities[].location.image` | The Docker image that was analyzed. |
| `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. | | `vulnerabilities[].identifiers` | An ordered array of references that identify a vulnerability on internal or external DBs. |
| `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`). | | `vulnerabilities[].identifiers[].type` | Type of the identifier. Possible values: common identifier types (among `cve`, `cwe`, `osvdb`, and `usn`). |
| `vulnerabilities[].identifiers[].name` | Name of the identifier for display purpose. | | `vulnerabilities[].identifiers[].name` | Name of the identifier for display purpose. |
... ...
......