From 1c7411c597334e20d2e92cc948f0699d339d2710 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 11 Nov 2021 12:10:41 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- .../confirm_via_gl_modal/confirm_modal.vue | 47 ++++++++++++ .../confirm_via_gl_modal.js | 47 ++++++++++++ app/assets/javascripts/lib/utils/rails_ujs.js | 38 ++++++++++ .../shared/wikis/components/wiki_form.vue | 14 +++- .../pages/shared/wikis/constants.js | 5 +- .../components/service_desk_setting.vue | 20 +++++- .../runner/components/runner_tag.vue | 35 ++++++++- .../runner/components/runner_tags.vue | 10 ++- app/assets/javascripts/runner/constants.js | 2 +- .../components/notes/system_note.vue | 65 ++++++++++++++++- app/controllers/projects/notes_controller.rb | 8 +++ .../resolvers/concerns/resolves_groups.rb | 34 +++++++++ app/graphql/resolvers/groups_resolver.rb | 12 ++-- .../resolvers/users/groups_resolver.rb | 15 ++-- app/graphql/types/group_type.rb | 6 +- app/models/group.rb | 11 +-- app/presenters/ci/build_runner_presenter.rb | 2 +- app/serializers/note_entity.rb | 4 ++ .../outdated_discussion_diff_lines_service.rb | 55 ++++++++++++++ .../bootstrap_confirmation_modals.yml | 8 +++ .../display_outdated_line_diff.yml | 8 +++ config/routes/project.rb | 1 + doc/administration/postgresql/index.md | 2 +- doc/integration/jira/connect-app.md | 2 +- lib/gitlab/ci/pipeline/seed/build.rb | 2 +- lib/gitlab/ci/variables/collection.rb | 2 +- .../diff/position_tracer/line_strategy.rb | 8 +-- lib/gitlab/gon_helper.rb | 1 + locale/gitlab.pot | 3 + .../projects/notes_controller_spec.rb | 29 ++++++++ .../admin/admin_disables_two_factor_spec.rb | 1 + spec/features/admin/admin_groups_spec.rb | 1 + spec/features/admin/admin_hooks_spec.rb | 1 + spec/features/admin/admin_labels_spec.rb | 1 + .../admin_users_impersonation_tokens_spec.rb | 1 + .../admin_uses_repository_checks_spec.rb | 1 + spec/features/admin/users/user_spec.rb | 1 + spec/features/admin/users/users_spec.rb | 1 + spec/features/boards/boards_spec.rb | 1 + .../groups/members/leave_group_spec.rb | 1 + .../user_comments_on_diff_spec.rb | 1 + .../user_posts_diff_notes_spec.rb | 1 + .../merge_request/user_posts_notes_spec.rb | 2 + .../user_sees_avatar_on_diff_notes_spec.rb | 1 + .../user_sees_deployment_widget_spec.rb | 1 + spec/features/profile_spec.rb | 2 + .../features/profiles/active_sessions_spec.rb | 4 ++ .../profiles/oauth_applications_spec.rb | 1 + .../profiles/personal_access_tokens_spec.rb | 1 + .../branches/user_deletes_branch_spec.rb | 1 + spec/features/projects/branches_spec.rb | 1 + .../comments/user_deletes_comments_spec.rb | 1 + .../commit/user_comments_on_commit_spec.rb | 2 + .../environments/environments_spec.rb | 2 + .../projects/jobs/user_browses_job_spec.rb | 1 + .../members/member_leaves_project_spec.rb | 1 + .../members/user_requests_access_spec.rb | 1 + .../projects/pages/user_adds_domain_spec.rb | 2 + .../user_edits_lets_encrypt_settings_spec.rb | 1 + .../pages/user_edits_settings_spec.rb | 1 + .../projects/pipeline_schedules_spec.rb | 1 + .../projects/pipelines/pipelines_spec.rb | 1 + .../projects/settings/access_tokens_spec.rb | 1 + .../user_searches_in_settings_spec.rb | 1 + .../notes_on_personal_snippets_spec.rb | 1 + .../snippets/user_creates_snippet_spec.rb | 1 + spec/features/triggers_spec.rb | 1 + .../confirm_modal_spec.js | 59 +++++++++++++++ .../shared/wikis/components/wiki_form_spec.js | 26 ++++--- .../components/service_desk_setting_spec.js | 23 +++++- .../runner/components/runner_tag_spec.js | 46 ++++++++++-- .../runner/components/runner_tags_spec.js | 10 +-- .../components/notes/system_note_spec.js | 50 +++++++++++-- .../concerns/resolves_groups_spec.rb | 71 +++++++++++++++++++ .../gitlab/ci/variables/collection_spec.rb | 4 +- .../position_tracer/line_strategy_spec.rb | 7 +- spec/models/group_spec.rb | 2 + .../features/2fa_shared_examples.rb | 1 + 79 files changed, 759 insertions(+), 83 deletions(-) create mode 100644 app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue create mode 100644 app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js create mode 100644 app/graphql/resolvers/concerns/resolves_groups.rb create mode 100644 app/services/merge_requests/outdated_discussion_diff_lines_service.rb create mode 100644 config/feature_flags/development/bootstrap_confirmation_modals.yml create mode 100644 config/feature_flags/development/display_outdated_line_diff.yml create mode 100644 spec/frontend/lib/utils/confirm_via_gl_modal/confirm_modal_spec.js create mode 100644 spec/graphql/resolvers/concerns/resolves_groups_spec.rb diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 87da2143dd2..27fb81c9ced 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -9de3dd28a5c8248903160ea35d9f718899f51c89 +4892c8502cc45217903a8a584a7b5edb15edf86e diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue new file mode 100644 index 00000000000..733d0f69f5d --- /dev/null +++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue @@ -0,0 +1,47 @@ + + + diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js new file mode 100644 index 00000000000..f0908a60ac5 --- /dev/null +++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js @@ -0,0 +1,47 @@ +import Vue from 'vue'; + +export function confirmViaGlModal(message, element) { + return new Promise((resolve) => { + let confirmed = false; + + const props = {}; + + const confirmBtnVariant = element.getAttribute('data-confirm-btn-variant'); + + if (confirmBtnVariant) { + props.primaryVariant = confirmBtnVariant; + } + const screenReaderText = + element.querySelector('.gl-sr-only')?.textContent || + element.querySelector('.sr-only')?.textContent || + element.getAttribute('aria-label'); + + if (screenReaderText) { + props.primaryText = screenReaderText; + } + + const component = new Vue({ + components: { + ConfirmModal: () => import('./confirm_modal.vue'), + }, + render(h) { + return h( + 'confirm-modal', + { + props, + on: { + confirmed() { + confirmed = true; + }, + closed() { + component.$destroy(); + resolve(confirmed); + }, + }, + }, + [message], + ); + }, + }).$mount(); + }); +} diff --git a/app/assets/javascripts/lib/utils/rails_ujs.js b/app/assets/javascripts/lib/utils/rails_ujs.js index 8b40cc7bd11..6b1985a23ba 100644 --- a/app/assets/javascripts/lib/utils/rails_ujs.js +++ b/app/assets/javascripts/lib/utils/rails_ujs.js @@ -1,4 +1,42 @@ import Rails from '@rails/ujs'; +import { confirmViaGlModal } from './confirm_via_gl_modal/confirm_via_gl_modal'; + +function monkeyPatchConfirmModal() { + /** + * This function is used to replace the `Rails.confirm` which uses `window.confirm` + * + * This function opens a confirmation modal which will resolve in a promise. + * Because the `Rails.confirm` API is synchronous, we go with a little hack here: + * + * 1. User clicks on something with `data-confirm` + * 2. We open the modal and return `false`, ending the "Rails" event chain + * 3. If the modal is closed and the user "confirmed" the action + * 1. replace the `Rails.confirm` with a function that always returns `true` + * 2. click the same element programmatically + * + * @param message {String} Message to be shown in the modal + * @param element {HTMLElement} Element that was clicked on + * @returns {boolean} + */ + function confirmViaModal(message, element) { + confirmViaGlModal(message, element) + .then((confirmed) => { + if (confirmed) { + Rails.confirm = () => true; + element.click(); + Rails.confirm = confirmViaModal; + } + }) + .catch(() => {}); + return false; + } + + Rails.confirm = confirmViaModal; +} + +if (gon?.features?.bootstrapConfirmationModals) { + monkeyPatchConfirmModal(); +} export const initRails = () => { // eslint-disable-next-line no-underscore-dangle diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index 2ce1f0366c1..6f19a9f4379 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -16,9 +16,11 @@ import { __, s__, sprintf } from '~/locale'; import Tracking from '~/tracking'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import { - WIKI_CONTENT_EDITOR_TRACKING_LABEL, CONTENT_EDITOR_LOADED_ACTION, SAVED_USING_CONTENT_EDITOR_ACTION, + WIKI_CONTENT_EDITOR_TRACKING_LABEL, + WIKI_FORMAT_LABEL, + WIKI_FORMAT_UPDATED_ACTION, } from '../constants'; const trackingMixin = Tracking.mixin({ @@ -219,6 +221,8 @@ export default { this.trackFormSubmit(); } + this.trackWikiFormat(); + // Wait until form field values are refreshed await this.$nextTick(); @@ -304,6 +308,14 @@ export default { } }, + trackWikiFormat() { + this.track(WIKI_FORMAT_UPDATED_ACTION, { + label: WIKI_FORMAT_LABEL, + value: this.format, + extra: { project_path: this.pageInfo.path, old_format: this.pageInfo.format }, + }); + }, + dismissContentEditorAlert() { this.isContentEditorAlertDismissed = true; }, diff --git a/app/assets/javascripts/pages/shared/wikis/constants.js b/app/assets/javascripts/pages/shared/wikis/constants.js index b358ac9cf52..94d086158f1 100644 --- a/app/assets/javascripts/pages/shared/wikis/constants.js +++ b/app/assets/javascripts/pages/shared/wikis/constants.js @@ -1,4 +1,5 @@ -export const WIKI_CONTENT_EDITOR_TRACKING_LABEL = 'wiki_content_editor'; - export const CONTENT_EDITOR_LOADED_ACTION = 'content_editor_loaded'; export const SAVED_USING_CONTENT_EDITOR_ACTION = 'saved_using_content_editor'; +export const WIKI_CONTENT_EDITOR_TRACKING_LABEL = 'wiki_content_editor'; +export const WIKI_FORMAT_LABEL = 'wiki_format'; +export const WIKI_FORMAT_UPDATED_ACTION = 'wiki_format_updated'; diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue index d964a701b08..b8053bf9ab5 100644 --- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue @@ -75,6 +75,7 @@ export default { outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), projectKey: this.initialProjectKey, searchTerm: '', + projectKeyError: null, }; }, computed: { @@ -104,6 +105,14 @@ export default { this.selectedFileTemplateProjectId = selectedFileTemplateProjectId; this.selectedTemplate = selectedTemplate; }, + validateProjectKey() { + if (this.projectKey && !new RegExp(/^[a-z0-9_]+$/).test(this.projectKey)) { + this.projectKeyError = __('Only use lowercase letters, numbers, and underscores.'); + return; + } + + this.projectKeyError = null; + }, }, }; @@ -169,8 +178,17 @@ export default { v-model.trim="projectKey" data-testid="project-suffix" class="form-control" + :state="!projectKeyError" + @blur="validateProjectKey" /> - + + {{ projectKeyError }} + + {{ __('A string appended to the project path to form the Service Desk email address.') }} diff --git a/app/assets/javascripts/runner/components/runner_tag.vue b/app/assets/javascripts/runner/components/runner_tag.vue index 06562e618a8..6ad2023a866 100644 --- a/app/assets/javascripts/runner/components/runner_tag.vue +++ b/app/assets/javascripts/runner/components/runner_tag.vue @@ -1,11 +1,15 @@ diff --git a/app/assets/javascripts/runner/components/runner_tags.vue b/app/assets/javascripts/runner/components/runner_tags.vue index aec0d8e2c66..8da5e33076f 100644 --- a/app/assets/javascripts/runner/components/runner_tags.vue +++ b/app/assets/javascripts/runner/components/runner_tags.vue @@ -14,13 +14,19 @@ export default { size: { type: String, required: false, - default: 'md', + default: 'sm', }, }, }; diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js index c0b256ec7fa..3952e2398e0 100644 --- a/app/assets/javascripts/runner/constants.js +++ b/app/assets/javascripts/runner/constants.js @@ -27,7 +27,7 @@ export const I18N_NOT_CONNECTED_RUNNER_DESCRIPTION = s__( export const I18N_LOCKED_RUNNER_DESCRIPTION = s__('Runners|You cannot assign to other projects'); export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run jobs'); -export const RUNNER_TAG_BADGE_VARIANT = 'info'; +export const RUNNER_TAG_BADGE_VARIANT = 'neutral'; export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100'; // Filtered search parameter names diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue index 755e6f1f224..8877cfa39fb 100644 --- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue +++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue @@ -26,6 +26,7 @@ import { import $ from 'jquery'; import { mapGetters, mapActions, mapState } from 'vuex'; import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history'; +import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import initMRPopovers from '~/mr_popover/'; import noteHeader from '~/notes/components/note_header.vue'; @@ -61,6 +62,9 @@ export default { data() { return { expanded: false, + lines: [], + showLines: false, + loadingDiff: false, }; }, computed: { @@ -94,10 +98,25 @@ export default { }, methods: { ...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']), + async toggleDiff() { + this.showLines = !this.showLines; + + if (!this.lines.length) { + this.loadingDiff = true; + const { data } = await axios.get(this.note.outdated_line_change_path); + + this.lines = data.map((l) => ({ + ...l, + rich_text: l.rich_text.replace(/^[+ -]/, ''), + })); + this.loadingDiff = false; + } + }, }, safeHtmlConfig: { ADD_TAGS: ['use'], // to support icon SVGs }, + userColorSchemeClass: window.gon.user_color_scheme, }; @@ -112,15 +131,28 @@ export default {
-