diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-08 18:10:26 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-08 18:10:26 +0300 |
commit | 6e734c809b18a0470d81c78e1ecd9b3f8278de89 (patch) | |
tree | 882251dce981323bc3c6e305cbc0f41aa4c39aae /app | |
parent | 9157fbe06fde892c647403f477ce31c888cbc822 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
22 files changed, 179 insertions, 102 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 569fbb9ab43..2f3a56ec046 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -13,6 +13,7 @@ const Api = { groupMilestonesPath: '/api/:version/groups/:id/milestones', subgroupsPath: '/api/:version/groups/:id/subgroups', namespacesPath: '/api/:version/namespaces.json', + groupInvitationsPath: '/api/:version/groups/:id/invitations', groupPackagesPath: '/api/:version/groups/:id/packages', projectPackagesPath: '/api/:version/projects/:id/packages', projectPackagePath: '/api/:version/projects/:id/packages/:package_id', @@ -23,6 +24,7 @@ const Api = { projectLabelsPath: '/:namespace_path/:project_path/-/labels', projectFileSchemaPath: '/:namespace_path/:project_path/-/schema/:ref/:filename', projectUsersPath: '/api/:version/projects/:id/users', + projectInvitationsPath: '/api/:version/projects/:id/invitations', projectMembersPath: '/api/:version/projects/:id/members', projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests', projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid', @@ -127,12 +129,18 @@ const Api = { }); }, - inviteGroupMember(id, data) { + addGroupMembersByUserId(id, data) { const url = Api.buildUrl(this.groupMembersPath).replace(':id', encodeURIComponent(id)); return axios.post(url, data); }, + inviteGroupMembersByEmail(id, data) { + const url = Api.buildUrl(this.groupInvitationsPath).replace(':id', encodeURIComponent(id)); + + return axios.post(url, data); + }, + groupMilestones(id, options) { const url = Api.buildUrl(this.groupMilestonesPath).replace(':id', encodeURIComponent(id)); @@ -217,12 +225,18 @@ const Api = { .then(({ data }) => data); }, - inviteProjectMembers(id, data) { + addProjectMembersByUserId(id, data) { const url = Api.buildUrl(this.projectMembersPath).replace(':id', encodeURIComponent(id)); return axios.post(url, data); }, + inviteProjectMembersByEmail(id, data) { + const url = Api.buildUrl(this.projectInvitationsPath).replace(':id', encodeURIComponent(id)); + + return axios.post(url, data); + }, + // Return single project project(projectPath) { const url = Api.buildUrl(Api.projectPath).replace(':id', encodeURIComponent(projectPath)); diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue index 63248a5ad48..50782781538 100644 --- a/app/assets/javascripts/boards/components/board_form.vue +++ b/app/assets/javascripts/boards/components/board_form.vue @@ -10,6 +10,7 @@ import { fullLabelId, fullBoardId } from '../boards_util'; import BoardConfigurationOptions from './board_configuration_options.vue'; import updateBoardMutation from '../graphql/board_update.mutation.graphql'; import createBoardMutation from '../graphql/board_create.mutation.graphql'; +import destroyBoardMutation from '../graphql/board_destroy.mutation.graphql'; const boardDefaults = { id: false, @@ -95,6 +96,9 @@ export default { fullPath: { default: '', }, + rootPath: { + default: '', + }, }, data() { return { @@ -221,8 +225,13 @@ export default { this.isLoading = true; if (this.isDeleteForm) { try { - await boardsStore.deleteBoard(this.currentBoard); - visitUrl(boardsStore.rootPath); + await this.$apollo.mutate({ + mutation: destroyBoardMutation, + variables: { + id: fullBoardId(this.board.id), + }, + }); + visitUrl(this.rootPath); } catch { Flash(this.$options.i18n.deleteErrorMessage); } finally { diff --git a/app/assets/javascripts/boards/graphql/board_destroy.mutation.graphql b/app/assets/javascripts/boards/graphql/board_destroy.mutation.graphql new file mode 100644 index 00000000000..d4b928749de --- /dev/null +++ b/app/assets/javascripts/boards/graphql/board_destroy.mutation.graphql @@ -0,0 +1,7 @@ +mutation destroyBoard($id: BoardID!) { + destroyBoard(input: { id: $id }) { + board { + id + } + } +} diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 18d2c75b7e1..e978eedee7f 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -336,5 +336,6 @@ export default () => { mountMultipleBoardsSwitcher({ fullPath: $boardApp.dataset.fullPath, + rootPath: $boardApp.dataset.boardsEndpoint, }); }; diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index 71463010898..17a12e84a37 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -37,6 +37,7 @@ export default (params = {}) => { }, provide: { fullPath: params.fullPath, + rootPath: params.rootPath, }, render(createElement) { return createElement(BoardsSelector, { diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 3bc077be552..af00c035a91 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -753,10 +753,6 @@ const boardsStore = { return axios.get(this.state.endpoints.recentBoardsEndpoint); }, - deleteBoard({ id }) { - return axios.delete(this.generateBoardsPath(id)); - }, - setCurrentBoard(board) { this.state.currentBoard = board; }, diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue index a548354f257..acaa7ae72d1 100644 --- a/app/assets/javascripts/diffs/components/commit_item.vue +++ b/app/assets/javascripts/diffs/components/commit_item.vue @@ -6,7 +6,7 @@ import { GlButtonGroup, GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui' import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; -import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; @@ -39,7 +39,7 @@ import { setUrlParams } from '../../lib/utils/url_utility'; export default { components: { UserAvatarLink, - ClipboardButton, + ModalCopyButton, TimeAgoTooltip, CommitPipelineStatus, GlButtonGroup, @@ -142,7 +142,7 @@ export default { data-testid="commit-sha-short-id" v-text="commit.short_id" /> - <clipboard-button + <modal-copy-button :text="commit.id" :title="__('Copy commit SHA')" class="input-group-text" diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue index 2b6b8b765a2..192d6e056cd 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue @@ -35,6 +35,7 @@ export default { ...mapGetters([ 'isLoading', 'isImportingAnyRepo', + 'importingRepoCount', 'hasImportableRepos', 'hasIncompatibleRepos', 'importAllCount', @@ -60,13 +61,17 @@ export default { }, importAllButtonText() { - return this.hasIncompatibleRepos - ? n__( - 'Import %d compatible repository', - 'Import %d compatible repositories', - this.importAllCount, - ) - : n__('Import %d repository', 'Import %d repositories', this.importAllCount); + if (this.isImportingAnyRepo) { + return n__('Importing %d repository', 'Importing %d repositories', this.importingRepoCount); + } + + if (this.hasIncompatibleRepos) + return n__( + 'Import %d compatible repository', + 'Import %d compatible repositories', + this.importAllCount, + ); + return n__('Import %d repository', 'Import %d repositories', this.importAllCount); }, emptyStateText() { diff --git a/app/assets/javascripts/import_entities/import_projects/store/getters.js b/app/assets/javascripts/import_entities/import_projects/store/getters.js index 8903133ea12..ef01a67ec94 100644 --- a/app/assets/javascripts/import_entities/import_projects/store/getters.js +++ b/app/assets/javascripts/import_entities/import_projects/store/getters.js @@ -1,14 +1,10 @@ -import { STATUSES } from '../../constants'; -import { isProjectImportable, isIncompatible } from '../utils'; +import { isProjectImportable, isIncompatible, isImporting } from '../utils'; export const isLoading = (state) => state.isLoadingRepos || state.isLoadingNamespaces; -export const isImportingAnyRepo = (state) => - state.repositories.some((repo) => - [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes( - repo.importedProject?.importStatus, - ), - ); +export const importingRepoCount = (state) => state.repositories.filter(isImporting).length; + +export const isImportingAnyRepo = (state) => state.repositories.some(isImporting); export const hasIncompatibleRepos = (state) => state.repositories.some(isIncompatible); diff --git a/app/assets/javascripts/import_entities/import_projects/utils.js b/app/assets/javascripts/import_entities/import_projects/utils.js index 0610117e09b..38bd529321a 100644 --- a/app/assets/javascripts/import_entities/import_projects/utils.js +++ b/app/assets/javascripts/import_entities/import_projects/utils.js @@ -11,3 +11,9 @@ export function getImportStatus(project) { export function isProjectImportable(project) { return !isIncompatible(project) && getImportStatus(project) === STATUSES.NONE; } + +export function isImporting(repo) { + return [STATUSES.SCHEDULING, STATUSES.SCHEDULED, STATUSES.STARTED].includes( + repo.importedProject?.importStatus, + ); +} diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index dbb0ed11e1b..a92289ca8c1 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -9,6 +9,7 @@ import { GlButton, GlFormInput, } from '@gitlab/ui'; +import { partition, isString } from 'lodash'; import eventHub from '../event_hub'; import { s__, __, sprintf } from '~/locale'; import Api from '~/api'; @@ -58,7 +59,7 @@ export default { visible: true, modalId: 'invite-members-modal', selectedAccessLevel: this.defaultAccessLevel, - newUsersToInvite: '', + newUsersToInvite: [], selectedDate: undefined, }; }, @@ -79,13 +80,12 @@ export default { return { onComplete: () => { this.selectedAccessLevel = this.defaultAccessLevel; - this.newUsersToInvite = ''; + this.newUsersToInvite = []; }, }; }, - postData() { + basePostData() { return { - user_id: this.newUsersToInvite, access_level: this.selectedAccessLevel, expires_at: this.selectedDate, format: 'json', @@ -101,6 +101,17 @@ export default { eventHub.$on('openModal', this.openModal); }, methods: { + partitionNewUsersToInvite() { + const [usersToInviteByEmail, usersToAddById] = partition( + this.newUsersToInvite, + (user) => isString(user.id) && user.id.includes('user-defined-token'), + ); + + return [ + usersToInviteByEmail.map((user) => user.name).join(','), + usersToAddById.map((user) => user.id).join(','), + ]; + }, openModal() { this.$root.$emit('bv::show::modal', this.modalId); }, @@ -108,7 +119,7 @@ export default { this.$root.$emit('bv::hide::modal', this.modalId); }, sendInvite() { - this.submitForm(this.postData); + this.submitForm(); this.closeModal(); }, cancelInvite() { @@ -120,15 +131,33 @@ export default { changeSelectedItem(item) { this.selectedAccessLevel = item; }, - submitForm(formData) { - if (this.isProject) { - return Api.inviteProjectMembers(this.id, formData) - .then(this.showToastMessageSuccess) - .catch(this.showToastMessageError); + submitForm() { + const [usersToInviteByEmail, usersToAddById] = this.partitionNewUsersToInvite(); + const promises = []; + + if (usersToInviteByEmail !== '') { + const apiInviteByEmail = this.isProject + ? Api.inviteProjectMembersByEmail.bind(Api) + : Api.inviteGroupMembersByEmail.bind(Api); + + promises.push(apiInviteByEmail(this.id, this.inviteByEmailPostData(usersToInviteByEmail))); } - return Api.inviteGroupMember(this.id, formData) - .then(this.showToastMessageSuccess) - .catch(this.showToastMessageError); + + if (usersToAddById !== '') { + const apiAddByUserId = this.isProject + ? Api.addProjectMembersByUserId.bind(Api) + : Api.addGroupMembersByUserId.bind(Api); + + promises.push(apiAddByUserId(this.id, this.addByUserIdPostData(usersToAddById))); + } + + Promise.all(promises).then(this.showToastMessageSuccess).catch(this.showToastMessageError); + }, + inviteByEmailPostData(usersToInviteByEmail) { + return { ...this.basePostData, email: usersToInviteByEmail }; + }, + addByUserIdPostData(usersToAddById) { + return { ...this.basePostData, user_id: usersToAddById }; }, showToastMessageSuccess() { this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions); diff --git a/app/assets/javascripts/invite_members/components/members_token_select.vue b/app/assets/javascripts/invite_members/components/members_token_select.vue index d839c089f2e..b54812dfd96 100644 --- a/app/assets/javascripts/invite_members/components/members_token_select.vue +++ b/app/assets/javascripts/invite_members/components/members_token_select.vue @@ -1,6 +1,7 @@ <script> import { debounce } from 'lodash'; -import { GlTokenSelector, GlAvatar, GlAvatarLabeled } from '@gitlab/ui'; +import { GlTokenSelector, GlAvatar, GlAvatarLabeled, GlSprintf } from '@gitlab/ui'; +import { __ } from '~/locale'; import { USER_SEARCH_DELAY } from '../constants'; import Api from '~/api'; @@ -9,6 +10,7 @@ export default { GlTokenSelector, GlAvatar, GlAvatarLabeled, + GlSprintf, }, props: { placeholder: { @@ -32,12 +34,10 @@ export default { }; }, computed: { - newUsersToInvite() { - return this.selectedTokens - .map((obj) => { - return obj.id; - }) - .join(','); + emailIsValid() { + const regex = /.+@/; + + return this.query.match(regex) !== null; }, placeholderText() { if (this.selectedTokens.length === 0) { @@ -69,7 +69,7 @@ export default { }); }, USER_SEARCH_DELAY), handleInput() { - this.$emit('input', this.newUsersToInvite); + this.$emit('input', this.selectedTokens); }, handleBlur() { this.hideDropdownWithNoItems = false; @@ -86,6 +86,9 @@ export default { }, }, queryOptions: { exclude_internal: true, active: true }, + i18n: { + inviteTextMessage: __('Invite "%{email}" by email'), + }, }; </script> @@ -94,7 +97,7 @@ export default { v-model="selectedTokens" :dropdown-items="users" :loading="loading" - :allow-user-defined-tokens="false" + :allow-user-defined-tokens="emailIsValid" :hide-dropdown-with-no-items="hideDropdownWithNoItems" :placeholder="placeholderText" :aria-labelledby="ariaLabelledby" @@ -116,5 +119,13 @@ export default { :sub-label="dropdownItem.username" /> </template> + + <template #user-defined-token-content="{ inputText: email }"> + <gl-sprintf :message="$options.i18n.inviteTextMessage"> + <template #email> + <span>{{ email }}</span> + </template> + </gl-sprintf> + </template> </gl-token-selector> </template> diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js index 4c67c310e9e..74c374018de 100644 --- a/app/assets/javascripts/invite_members/init_invite_members_modal.js +++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import { GlToast } from '@gitlab/ui'; +import { parseBoolean } from '~/lib/utils/common_utils'; import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue'; Vue.use(GlToast); @@ -17,6 +18,7 @@ export default function initInviteMembersModal() { createElement(InviteMembersModal, { props: { ...el.dataset, + isProject: parseBoolean(el.dataset.isProject), accessLevels: JSON.parse(el.dataset.accessLevels), }, }), diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index 997551d9659..58f1abb2818 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -34,5 +34,3 @@ module Projects::AlertManagementHelper ) end end - -Projects::AlertManagementHelper.prepend_if_ee('EE::Projects::AlertManagementHelper') diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 8b9055ae289..efe5789e49a 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -730,7 +730,13 @@ module Ci end def any_runners_online? - project.any_runners? { |runner| runner.active? && runner.online? && runner.can_pick?(self) } + project.any_runners? do |runner| + if Feature.enabled?(:ci_build_stuck_badge_performance_experiment, project, type: :development, default_enabled: false) + runner.active? && runner.online? + else + runner.active? && runner.online? && runner.can_pick?(self) + end + end end def stuck? diff --git a/app/models/project_services/alerts_service.rb b/app/models/project_services/alerts_service.rb index 5b7d149ace1..58d507971ca 100644 --- a/app/models/project_services/alerts_service.rb +++ b/app/models/project_services/alerts_service.rb @@ -88,5 +88,3 @@ class AlertsService < Service .execute end end - -AlertsService.prepend_if_ee('EE::AlertsService') diff --git a/app/models/user.rb b/app/models/user.rb index 02092d70d20..c6be3d6a839 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1358,6 +1358,7 @@ class User < ApplicationRecord def hook_attrs { + id: id, name: name, username: username, avatar_url: avatar_url(only_path: false), diff --git a/app/views/admin/users/_admin_notes.html.haml b/app/views/admin/users/_admin_notes.html.haml index 5d91ba1a1ca..4da70a504f7 100644 --- a/app/views/admin/users/_admin_notes.html.haml +++ b/app/views/admin/users/_admin_notes.html.haml @@ -1,7 +1,7 @@ %fieldset %legend= _('Admin notes') .form-group.row - .col-sm-2.col-form-label.text-right + .col-sm-2.col-form-label = f.label :note, s_('AdminNote|Note') .col-sm-10 = f.text_area :note, class: 'form-control' diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml index 3aae81cef8d..bd53f73230e 100644 --- a/app/views/groups/_invite_members_modal.html.haml +++ b/app/views/groups/_invite_members_modal.html.haml @@ -1,7 +1,7 @@ - if invite_members_allowed?(group) .js-invite-members-modal{ data: { id: group.id, name: group.name, - is_project: false, + is_project: 'false', access_levels: GroupMember.access_level_roles.to_json, default_access_level: Gitlab::Access::GUEST, help_link: help_page_url('user/permissions') } } diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index f13c1f29041..9a7cfc0a573 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -51,56 +51,52 @@ %span.badge.badge-pill= @requesters.count .tab-content #tab-members.tab-pane{ class: ('active' unless invited_active) } - .card.card-without-border + - unless filtered_search_enabled + = render 'shared/members/tab_pane/header' do + = render 'shared/members/tab_pane/title' do + = html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } + = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do + .gl-px-3.gl-py-2 + .search-control-wrap.gl-relative + = render 'shared/members/search_field' + - if can_manage_members + = render 'shared/members/tab_pane/form_item' do + = label_tag '2fa', _('2FA'), class: form_item_label_css_class + = render 'shared/members/filter_2fa_dropdown' + = render 'shared/members/tab_pane/form_item' do + = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class + = render 'shared/members/sort_dropdown' + .js-group-members-list{ data: group_members_list_data_attributes(@group, @members) } + .loading + .spinner.spinner-md + = paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil } + - if @group.shared_with_group_links.any? + #tab-groups.tab-pane - unless filtered_search_enabled = render 'shared/members/tab_pane/header' do = render 'shared/members/tab_pane/title' do - = html_escape(_('Members with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } - = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form gl-display-flex gl-md-align-items-center gl-flex-wrap gl-flex-direction-column gl-md-flex-direction-row gl-mx-n3 gl-my-n3', data: { testid: 'user-search-form' } do - .gl-px-3.gl-py-2 - .search-control-wrap.gl-relative - = render 'shared/members/search_field' - - if can_manage_members - = render 'shared/members/tab_pane/form_item' do - = label_tag '2fa', _('2FA'), class: form_item_label_css_class - = render 'shared/members/filter_2fa_dropdown' - = render 'shared/members/tab_pane/form_item' do - = label_tag :sort_by, _('Sort by'), class: form_item_label_css_class - = render 'shared/members/sort_dropdown' - .js-group-members-list{ data: group_members_list_data_attributes(@group, @members) } + = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } + .js-group-linked-list{ data: linked_groups_list_data_attributes(@group) } .loading .spinner.spinner-md - = paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil } - - if @group.shared_with_group_links.any? - #tab-groups.tab-pane - .card.card-without-border - - unless filtered_search_enabled - = render 'shared/members/tab_pane/header' do - = render 'shared/members/tab_pane/title' do - = html_escape(_('Groups with access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } - .js-group-linked-list{ data: linked_groups_list_data_attributes(@group) } - .loading - .spinner.spinner-md - if show_invited_members #tab-invited-members.tab-pane{ class: ('active' if invited_active) } - .card.card-without-border - - unless filtered_search_enabled - = render 'shared/members/tab_pane/header' do - = render 'shared/members/tab_pane/title' do - = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } - = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do - = render 'shared/members/search_field', name: 'search_invited' - .js-group-invited-members-list{ data: group_members_list_data_attributes(@group, @invited_members) } - .loading - .spinner.spinner-md - = paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil } + - unless filtered_search_enabled + = render 'shared/members/tab_pane/header' do + = render 'shared/members/tab_pane/title' do + = html_escape(_('Members invited to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } + = form_tag group_group_members_path(@group), method: :get, class: 'user-search-form', data: { testid: 'user-search-form' } do + = render 'shared/members/search_field', name: 'search_invited' + .js-group-invited-members-list{ data: group_members_list_data_attributes(@group, @invited_members) } + .loading + .spinner.spinner-md + = paginate @invited_members, param_name: 'invited_members_page', theme: 'gitlab', params: { page: nil } - if show_access_requests #tab-access-requests.tab-pane - .card.card-without-border - - unless filtered_search_enabled - = render 'shared/members/tab_pane/header' do - = render 'shared/members/tab_pane/title' do - = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } - .js-group-access-requests-list{ data: group_members_list_data_attributes(@group, @requesters) } - .loading - .spinner.spinner-md + - unless filtered_search_enabled + = render 'shared/members/tab_pane/header' do + = render 'shared/members/tab_pane/title' do + = html_escape(_('Users requesting access to %{strong_start}%{group_name}%{strong_end}')) % { group_name: @group.name, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } + .js-group-access-requests-list{ data: group_members_list_data_attributes(@group, @requesters) } + .loading + .spinner.spinner-md diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml index ad95f39bbfa..e8f61336882 100644 --- a/app/views/projects/_invite_members_modal.html.haml +++ b/app/views/projects/_invite_members_modal.html.haml @@ -1,7 +1,7 @@ - if invite_members_allowed?(project.group) .js-invite-members-modal{ data: { id: project.id, name: project.name, - is_project: true, + is_project: 'true', access_levels: GroupMember.access_level_roles.to_json, default_access_level: Gitlab::Access::GUEST, help_link: help_page_url('user/permissions') } } diff --git a/app/views/projects/jobs/index.html.haml b/app/views/projects/jobs/index.html.haml index cd062fcf675..e14473708af 100644 --- a/app/views/projects/jobs/index.html.haml +++ b/app/views/projects/jobs/index.html.haml @@ -8,10 +8,11 @@ .nav-controls - if can?(current_user, :update_build, @project) - if !@repository.gitlab_ci_yml && !experiment_enabled?(:jobs_empty_state) - = link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn gl-button btn-info js-empty-state-button' + = link_to s_('Pipelines|Get started with Pipelines'), help_page_path('ci/quick_start/README'), class: 'btn gl-button btn-info js-empty-state-button' = link_to project_ci_lint_path(@project), class: 'btn gl-button btn-default' do - %span CI lint + %span + = _('CI Lint') .content-list.builds-content-list = render "table", builds: @builds, project: @project |