diff options
Diffstat (limited to 'app')
15 files changed, 85 insertions, 185 deletions
diff --git a/app/assets/javascripts/branches/branches_delete_modal.js b/app/assets/javascripts/branches/branches_delete_modal.js deleted file mode 100644 index f4c3fa185d8..00000000000 --- a/app/assets/javascripts/branches/branches_delete_modal.js +++ /dev/null @@ -1,53 +0,0 @@ -import $ from 'jquery'; - -const MODAL_SELECTOR = '#modal-delete-branch'; - -class DeleteModal { - constructor() { - this.$modal = $(MODAL_SELECTOR); - this.$toggleBtns = $(`[data-target="${MODAL_SELECTOR}"]`); - this.$branchName = $('.js-branch-name', this.$modal); - this.$confirmInput = $('.js-delete-branch-input', this.$modal); - this.$deleteBtn = $('.js-delete-branch', this.$modal); - this.$notMerged = $('.js-not-merged', this.$modal); - this.bindEvents(); - } - - bindEvents() { - this.$toggleBtns.on('click', this.setModalData.bind(this)); - this.$confirmInput.on('input', this.setDeleteDisabled.bind(this)); - this.$deleteBtn.on('click', this.setDisableDeleteButton.bind(this)); - } - - setModalData(e) { - const branchData = e.currentTarget.dataset; - this.branchName = branchData.branchName || ''; - this.deletePath = branchData.deletePath || ''; - this.isMerged = Boolean(branchData.isMerged); - this.updateModal(); - } - - setDeleteDisabled(e) { - this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName); - } - - setDisableDeleteButton(e) { - if (this.$deleteBtn.is('[disabled]')) { - e.preventDefault(); - e.stopPropagation(); - return false; - } - - return true; - } - - updateModal() { - this.$branchName.text(this.branchName); - this.$confirmInput.val(''); - this.$deleteBtn.attr('href', this.deletePath); - this.$deleteBtn.attr('disabled', true); - this.$notMerged.toggleClass('hidden', this.isMerged); - } -} - -export default DeleteModal; diff --git a/app/assets/javascripts/clusters/agents/components/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue index da455c6fd87..a53bba6992d 100644 --- a/app/assets/javascripts/clusters/agents/components/show.vue +++ b/app/assets/javascripts/clusters/agents/components/show.vue @@ -122,12 +122,12 @@ export default { </p> <gl-tabs sync-active-tab-with-query-params lazy> - <slot name="ee-security-tab" :cluster-agent-id="clusterAgent.id"></slot> - <gl-tab :title="$options.i18n.activity" query-param-value="activity"> <activity-events :agent-name="agentName" :project-path="projectPath" /> </gl-tab> + <slot name="ee-security-tab" :cluster-agent-id="clusterAgent.id"></slot> + <gl-tab query-param-value="tokens"> <template #title> <span data-testid="cluster-agent-token-count"> diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js index 97dc76908af..d279c4cbb08 100644 --- a/app/assets/javascripts/pages/projects/branches/index/index.js +++ b/app/assets/javascripts/pages/projects/branches/index/index.js @@ -1,13 +1,11 @@ import initDeprecatedRemoveRowBehavior from '~/behaviors/deprecated_remove_row_behavior'; import AjaxLoadingSpinner from '~/branches/ajax_loading_spinner'; import BranchSortDropdown from '~/branches/branch_sort_dropdown'; -import DeleteModal from '~/branches/branches_delete_modal'; import initDiverganceGraph from '~/branches/divergence_graph'; import initDeleteBranchButton from '~/branches/init_delete_branch_button'; import initDeleteBranchModal from '~/branches/init_delete_branch_modal'; AjaxLoadingSpinner.init(); -new DeleteModal(); // eslint-disable-line no-new const { divergingCountsEndpoint, defaultBranch } = document.querySelector( '.js-branch-list', diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue index 2bb4ae7d34e..69b3e543ed5 100644 --- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue +++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue @@ -22,6 +22,7 @@ import { I18N_FETCH_ERROR, } from '../constants'; import getRunnersQuery from '../graphql/get_runners.query.graphql'; +import getRunnersCountQuery from '../graphql/get_runners_count.query.graphql'; import { fromUrlQueryToSearch, fromSearchToUrl, @@ -29,6 +30,17 @@ import { } from '../runner_search_utils'; import { captureException } from '../sentry_utils'; +const runnersCountSmartQuery = { + query: getRunnersCountQuery, + fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, + update(data) { + return data?.runners?.count; + }, + error(error) { + this.reportToSentry(error); + }, +}; + export default { name: 'AdminRunnersApp', components: { @@ -51,22 +63,6 @@ export default { type: String, required: true, }, - allRunnersCount: { - type: String, - required: true, - }, - instanceRunnersCount: { - type: String, - required: true, - }, - groupRunnersCount: { - type: String, - required: true, - }, - projectRunnersCount: { - type: String, - required: true, - }, }, data() { return { @@ -100,11 +96,49 @@ export default { this.reportToSentry(error); }, }, + allRunnersCount: { + ...runnersCountSmartQuery, + variables() { + return this.countVariables; + }, + }, + instanceRunnersCount: { + ...runnersCountSmartQuery, + variables() { + return { + ...this.countVariables, + type: INSTANCE_TYPE, + }; + }, + }, + groupRunnersCount: { + ...runnersCountSmartQuery, + variables() { + return { + ...this.countVariables, + type: GROUP_TYPE, + }; + }, + }, + projectRunnersCount: { + ...runnersCountSmartQuery, + variables() { + return { + ...this.countVariables, + type: PROJECT_TYPE, + }; + }, + }, }, computed: { variables() { return fromSearchToVariables(this.search); }, + countVariables() { + // Exclude pagination variables, leave only filters variables + const { sort, before, last, after, first, ...countVariables } = this.variables; + return countVariables; + }, runnersLoading() { return this.$apollo.queries.runners.loading; }, @@ -125,7 +159,7 @@ export default { search: { deep: true, handler() { - // TODO Implement back button reponse using onpopstate + // TODO Implement back button response using onpopstate updateHistory({ url: fromSearchToUrl(this.search), title: document.title, @@ -174,7 +208,7 @@ export default { > <template #title="{ tab }"> {{ tab.title }} - <gl-badge v-if="tabCount(tab)" class="gl-ml-1" size="sm"> + <gl-badge v-if="typeof tabCount(tab) == 'number'" class="gl-ml-1" size="sm"> {{ tabCount(tab) }} </gl-badge> </template> diff --git a/app/assets/javascripts/runner/admin_runners/index.js b/app/assets/javascripts/runner/admin_runners/index.js index 1fdbf7542c5..5ca87aa7a68 100644 --- a/app/assets/javascripts/runner/admin_runners/index.js +++ b/app/assets/javascripts/runner/admin_runners/index.js @@ -27,16 +27,7 @@ export const initAdminRunners = (selector = '#js-admin-runners') => { // TODO `activeRunnersCount` should be implemented using a GraphQL API // https://gitlab.com/gitlab-org/gitlab/-/issues/333806 - const { - runnerInstallHelpPage, - registrationToken, - - activeRunnersCount, - allRunnersCount, - instanceRunnersCount, - groupRunnersCount, - projectRunnersCount, - } = el.dataset; + const { runnerInstallHelpPage, registrationToken, activeRunnersCount } = el.dataset; const apolloProvider = new VueApollo({ defaultClient: createDefaultClient(), @@ -53,13 +44,9 @@ export const initAdminRunners = (selector = '#js-admin-runners') => { props: { registrationToken, - // All runner counts are returned as formatted + // Runner counts are returned as formatted // strings, we do not use `parseInt`. activeRunnersCount, - allRunnersCount, - instanceRunnersCount, - groupRunnersCount, - projectRunnersCount, }, }); }, diff --git a/app/assets/javascripts/runner/graphql/get_runners_count.query.graphql b/app/assets/javascripts/runner/graphql/get_runners_count.query.graphql new file mode 100644 index 00000000000..181a4495cae --- /dev/null +++ b/app/assets/javascripts/runner/graphql/get_runners_count.query.graphql @@ -0,0 +1,10 @@ +query getRunnersCount( + $status: CiRunnerStatus + $type: CiRunnerType + $tagList: [String!] + $search: String +) { + runners(status: $status, type: $type, tagList: $tagList, search: $search) { + count + } +} diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_tabs.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_tabs.vue index 3ff87ba3c4f..9bf54e98cc4 100644 --- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_tabs.vue +++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_tabs.vue @@ -1,5 +1,6 @@ <script> import { GlTabs, GlTab, GlBadge } from '@gitlab/ui'; +import { formatNumber } from '~/locale'; export default { components: { @@ -29,6 +30,9 @@ export default { isTabCountNumeric(tab) { return Number.isInteger(this.tabCounts[tab.name]); }, + formatNumber(count) { + return formatNumber(count); + }, }, }; </script> @@ -55,7 +59,7 @@ export default { size="sm" class="gl-tab-counter-badge" > - {{ tabCounts[tab.name] }} + {{ formatNumber(tabCounts[tab.name]) }} </gl-badge> </template> </gl-tab> diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index ab2b4fe5a23..fb75a3e15d6 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -67,12 +67,8 @@ module Ci runner_install_help_page: 'https://docs.gitlab.com/runner/install/', registration_token: Gitlab::CurrentSettings.runners_registration_token, - # All runner counts are returned as formatted strings - active_runners_count: Ci::Runner.online.count.to_s, - all_runners_count: limited_counter_with_delimiter(Ci::Runner), - instance_runners_count: limited_counter_with_delimiter(Ci::Runner.instance_type), - group_runners_count: limited_counter_with_delimiter(Ci::Runner.group_type), - project_runners_count: limited_counter_with_delimiter(Ci::Runner.project_type) + # Runner counts are returned as formatted strings + active_runners_count: Ci::Runner.online.count.to_s } end diff --git a/app/models/customer_relations/contact.rb b/app/models/customer_relations/contact.rb index 01ed620baf9..168f1c48a6c 100644 --- a/app/models/customer_relations/contact.rb +++ b/app/models/customer_relations/contact.rb @@ -26,10 +26,10 @@ class CustomerRelations::Contact < ApplicationRecord validate :validate_email_format validate :unique_email_for_group_hierarchy - def self.find_ids_by_emails(group_id, emails) + def self.find_ids_by_emails(group, emails) raise ArgumentError, "Cannot lookup more than #{MAX_PLUCK} emails" if emails.length > MAX_PLUCK - where(group_id: group_id, email: emails) + where(group_id: group.self_and_ancestor_ids, email: emails) .pluck(:id) end diff --git a/app/models/customer_relations/issue_contact.rb b/app/models/customer_relations/issue_contact.rb index 78f662b6a58..89dac6bad22 100644 --- a/app/models/customer_relations/issue_contact.rb +++ b/app/models/customer_relations/issue_contact.rb @@ -6,7 +6,7 @@ class CustomerRelations::IssueContact < ApplicationRecord belongs_to :issue, optional: false, inverse_of: :customer_relations_contacts belongs_to :contact, optional: false, inverse_of: :issue_contacts - validate :contact_belongs_to_issue_group + validate :contact_belongs_to_issue_group_or_ancestor def self.find_contact_ids_by_emails(issue_id, emails) raise ArgumentError, "Cannot lookup more than #{MAX_PLUCK} emails" if emails.length > MAX_PLUCK @@ -18,11 +18,11 @@ class CustomerRelations::IssueContact < ApplicationRecord private - def contact_belongs_to_issue_group + def contact_belongs_to_issue_group_or_ancestor return unless contact&.group_id return unless issue&.project&.namespace_id - return if contact.group_id == issue.project.namespace_id + return if issue.project.group&.self_and_ancestor_ids&.include?(contact.group_id) - errors.add(:base, _('The contact does not belong to the same group as the issue')) + errors.add(:base, _('The contact does not belong to the issue group or an ancestor')) end end diff --git a/app/services/issues/set_crm_contacts_service.rb b/app/services/issues/set_crm_contacts_service.rb index c435ab81b4d..947d46f0809 100644 --- a/app/services/issues/set_crm_contacts_service.rb +++ b/app/services/issues/set_crm_contacts_service.rb @@ -48,7 +48,7 @@ module Issues end def add_by_email - contact_ids = ::CustomerRelations::Contact.find_ids_by_emails(project_group.id, params[:add_emails]) + contact_ids = ::CustomerRelations::Contact.find_ids_by_emails(project_group, params[:add_emails]) add_by_id(contact_ids) end diff --git a/app/services/projects/overwrite_project_service.rb b/app/services/projects/overwrite_project_service.rb index 2612001eb95..c58fba33b2a 100644 --- a/app/services/projects/overwrite_project_service.rb +++ b/app/services/projects/overwrite_project_service.rb @@ -11,7 +11,9 @@ module Projects move_before_destroy_relationships(source_project) # Reset is required in order to get the proper # uncached fork network method calls value. - destroy_old_project(source_project.reset) + ::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.allow_cross_database_modification_within_transaction(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/340256') do + destroy_old_project(source_project.reset) + end rename_project(source_project.name, source_project.path) @project diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 9ecd184e2a3..f5e61c010cc 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -45,38 +45,4 @@ = render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name], class: 'gl-vertical-align-top' - - if Feature.enabled?(:delete_branch_confirmation_modals, @project, default_enabled: :yaml) - = render 'projects/branches/delete_branch_modal_button', project: @project, branch: branch, merged: merged - - - elsif can?(current_user, :push_code, @project) - - if branch.name == @project.repository.root_ref - - delete_default_branch_tooltip = s_('Branches|The default branch cannot be deleted') - %span.gl-display-inline-block.has-tooltip{ title: delete_default_branch_tooltip } - %button{ class: 'gl-button btn btn-default btn-icon disabled', disabled: true, 'aria-label' => delete_default_branch_tooltip } - = sprite_icon('remove', css_class: 'gl-button-icon gl-icon') - - elsif protected_branch?(@project, branch) - - if can?(current_user, :push_to_delete_protected_branch, @project) - - delete_protected_branch_tooltip = s_('Branches|Delete protected branch') - %button{ class: 'gl-button btn btn-default btn-icon has-tooltip', - title: delete_protected_branch_tooltip, - 'aria-label' => delete_protected_branch_tooltip, - data: { toggle: 'modal', - target: '#modal-delete-branch', - delete_path: project_branch_path(@project, branch.name), - branch_name: branch.name, - is_merged: ('true' if merged) } } - = sprite_icon('remove', css_class: 'gl-button-icon gl-icon') - - else - - delete_protected_branch_disabled_tooltip = s_('Branches|Only a project maintainer or owner can delete a protected branch') - %span.has-tooltip{ title: delete_protected_branch_disabled_tooltip } - %button{ class: 'gl-button btn btn-default btn-icon disabled', disabled: true, 'aria-label' => delete_protected_branch_disabled_tooltip, data: { testid: 'remove-protected-branch' } } - = sprite_icon('remove', css_class: 'gl-button-icon gl-icon') - - else - = link_to project_branch_path(@project, branch.name), - class: 'gl-button btn btn-default btn-icon js-remove-row qa-remove-btn js-ajax-loading-spinner has-tooltip', - title: s_('Branches|Delete branch'), - method: :delete, - data: { confirm: s_("Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?") % { branch_name: branch.name } }, - remote: true, - 'aria-label' => s_('Branches|Delete branch') do - = sprite_icon('remove', css_class: 'gl-button-icon gl-icon') + = render 'projects/branches/delete_branch_modal_button', project: @project, branch: branch, merged: merged diff --git a/app/views/projects/branches/_delete_protected_modal.html.haml b/app/views/projects/branches/_delete_protected_modal.html.haml deleted file mode 100644 index 2b45b4eddcc..00000000000 --- a/app/views/projects/branches/_delete_protected_modal.html.haml +++ /dev/null @@ -1,42 +0,0 @@ -#modal-delete-branch.modal{ tabindex: -1 } - .modal-dialog - .modal-content - .modal-header - %h3.page-title - - title_branch_name = capture do - %span.js-branch-name.ref-name>[branch name] - = s_("Branches|Delete protected branch '%{branch_name}'?").html_safe % { branch_name: title_branch_name } - %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } - %span{ "aria-hidden": "true" } × - - .modal-body - %p - - branch_name = capture do - %strong.js-branch-name.ref-name>[branch name] - = s_('Branches|You’re about to permanently delete the protected branch %{branch_name}.').html_safe % { branch_name: branch_name } - %p.js-not-merged - - default_branch = capture do - %span.ref-name= @repository.root_ref - = s_('Branches|This branch hasn’t been merged into %{default_branch}.').html_safe % { default_branch: default_branch } - = s_('Branches|To avoid data loss, consider merging this branch before deleting it.') - %p - - delete_protected_branch = capture do - %strong - = s_('Branches|Delete protected branch') - = s_('Branches|Once you confirm and press %{delete_protected_branch}, it cannot be undone or recovered.').html_safe % { delete_protected_branch: delete_protected_branch } - %p - - branch_name_confirmation = capture do - %kbd.js-branch-name [branch name] - %strong - = s_('Branches|To confirm, type %{branch_name_confirmation}:').html_safe % { branch_name_confirmation: branch_name_confirmation } - - .form-group - = text_field_tag 'delete_branch_input', '', class: 'form-control js-delete-branch-input' - - .modal-footer - %button.gl-button.btn.btn-default{ data: { dismiss: 'modal' } } Cancel - = link_to s_('Branches|Delete protected branch'), '', - class: "gl-button btn btn-danger js-delete-branch", - title: s_('Branches|Delete branch'), - method: :delete, - 'aria-label' => s_('Branches|Delete branch') diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 1c543d47ecf..2121d15643c 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -50,7 +50,5 @@ .nothing-here-block = s_('Branches|No branches to show') -- if Feature.enabled?(:delete_branch_confirmation_modals, @project, default_enabled: :yaml) && can?(current_user, :push_code, @project) +- if can?(current_user, :push_code, @project) .js-delete-branch-modal -- elsif can?(current_user, :push_code, @project) - = render 'projects/branches/delete_protected_modal' |