From 74d9798736a89f07e047698e5e32964829bf8859 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 10 Feb 2022 15:12:42 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop.yml | 1 + app/assets/javascripts/gfm_auto_complete.js | 55 ++++++++- .../pipeline_wizard/components/step_nav.vue | 54 ++++++++ .../vue_shared/components/markdown/field.vue | 3 + app/controllers/projects/issues_controller.rb | 1 + app/experiments/application_experiment.rb | 2 +- .../combined_registration_experiment.rb | 2 +- .../empty_repo_upload_experiment.rb | 2 +- .../force_company_trial_experiment.rb | 2 +- ...guidance_environments_webide_experiment.rb | 2 +- .../new_project_sast_enabled_experiment.rb | 2 +- ...ation_for_namespace_creation_experiment.rb | 2 +- ...ity_reports_mr_widget_prompt_experiment.rb | 2 +- app/helpers/application_helper.rb | 3 +- app/models/container_repository.rb | 24 +++- app/models/customer_relations/contact.rb | 12 ++ app/models/user.rb | 2 + .../issues/set_crm_contacts_service.rb | 17 ++- .../notes/_more_actions_dropdown.html.haml | 2 +- app/workers/all_queues.yml | 9 ++ .../migration/guard_worker.rb | 47 +++++++ .../development/contacts_autocomplete.yml | 8 ++ config/initializers/1_settings.rb | 4 +- config/sidekiq_queues.yml | 2 + ...ation_indexes_to_container_repositories.rb | 21 ++++ db/schema_migrations/20220202115350 | 1 + db/structure.sql | 6 + doc/administration/audit_events.md | 6 +- doc/ci/variables/index.md | 8 +- doc/user/asciidoc.md | 5 + doc/user/crm/index.md | 26 +++- doc/user/markdown.md | 1 + doc/user/project/issue_board.md | 2 +- .../project/issues/confidential_issues.md | 2 +- doc/user/project/issues/issue_weight.md | 6 +- doc/user/project/issues/managing_issues.md | 8 +- .../issues/multiple_assignees_for_issues.md | 2 +- doc/user/project/issues/related_issues.md | 13 +- .../project/issues/sorting_issue_lists.md | 2 +- doc/user/project/quick_actions.md | 4 +- doc/user/shortcuts.md | 2 +- lib/container_registry/base_client.rb | 4 +- lib/container_registry/gitlab_api_client.rb | 15 ++- lib/container_registry/migration.rb | 2 +- lib/gitlab/project_authorizations.rb | 6 +- lib/gitlab/quick_actions/issue_actions.rb | 4 +- locale/gitlab.pot | 18 +++ spec/factories/group_members.rb | 12 ++ spec/factories/project_members.rb | 12 ++ spec/features/issues/gfm_autocomplete_spec.rb | 33 ++++- .../schemas/public_api/v4/merge_request.json | 71 ++--------- .../components/step_nav_spec.js | 79 ++++++++++++ .../components/markdown/field_spec.js | 5 + spec/helpers/application_helper_spec.rb | 2 +- .../gitlab_api_client_spec.rb | 22 ++-- .../lib/gitlab/project_authorizations_spec.rb | 72 +++++++++++ spec/models/container_repository_spec.rb | 78 ++++++++++-- .../models/customer_relations/contact_spec.rb | 12 ++ .../issues/set_crm_contacts_service_spec.rb | 97 ++++++++------- .../migration/guard_worker_spec.rb | 115 ++++++++++++++++++ 60 files changed, 856 insertions(+), 178 deletions(-) create mode 100644 app/assets/javascripts/pipeline_wizard/components/step_nav.vue create mode 100644 app/workers/container_registry/migration/guard_worker.rb create mode 100644 config/feature_flags/development/contacts_autocomplete.yml create mode 100644 db/migrate/20220202115350_add_migration_indexes_to_container_repositories.rb create mode 100644 db/schema_migrations/20220202115350 create mode 100644 spec/frontend/pipeline_wizard/components/step_nav_spec.js create mode 100644 spec/workers/container_registry/migration/guard_worker_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 5757a273926..1b2e7ea470a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -666,6 +666,7 @@ Gitlab/NamespacedClass: - 'ee/elastic/**/*.rb' - 'scripts/**/*' - 'spec/migrations/**/*.rb' + - 'app/experiments/**/*_experiment.rb' Lint/HashCompareByIdentity: Enabled: true diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 69331ff1a06..d04896bf6e5 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -86,6 +86,7 @@ export const defaultAutocompleteConfig = { labels: true, snippets: true, vulnerabilities: true, + contacts: true, }; class GfmAutoComplete { @@ -127,6 +128,7 @@ class GfmAutoComplete { if (this.enableMap.mergeRequests) this.setupMergeRequests($input); if (this.enableMap.labels) this.setupLabels($input); if (this.enableMap.snippets) this.setupSnippets($input); + if (this.enableMap.contacts) this.setupContacts($input); $input.filter('[data-supports-quick-actions="true"]').atwho({ at: '/', @@ -174,9 +176,16 @@ class GfmAutoComplete { let tpl = '/${name} '; let referencePrefix = null; if (value.params.length > 0) { - [[referencePrefix]] = value.params; - if (/^[@%~]/.test(referencePrefix)) { + const regexp = /\[[a-z]+:/; + const match = regexp.exec(value.params); + if (match) { + [referencePrefix] = match; tpl += '<%- referencePrefix %>'; + } else { + [[referencePrefix]] = value.params; + if (/^[@%~]/.test(referencePrefix)) { + tpl += '<%- referencePrefix %>'; + } } } return template(tpl, { interpolate: /<%=([\s\S]+?)%>/g })({ referencePrefix }); @@ -619,6 +628,42 @@ class GfmAutoComplete { }); } + setupContacts($input) { + $input.atwho({ + at: '[contact:', + suffix: ']', + alias: 'contacts', + searchKey: 'search', + displayTpl(value) { + let tmpl = GfmAutoComplete.Loading.template; + if (value.email != null) { + tmpl = GfmAutoComplete.Contacts.templateFunction(value); + } + return tmpl; + }, + data: GfmAutoComplete.defaultLoadingData, + // eslint-disable-next-line no-template-curly-in-string + insertTpl: '${atwho-at}${email}', + callbacks: { + ...this.getDefaultCallbacks(), + beforeSave(contacts) { + return $.map(contacts, (m) => { + if (m.email == null) { + return m; + } + return { + id: m.id, + email: m.email, + firstName: m.first_name, + lastName: m.last_name, + search: `${m.email}`, + }; + }); + }, + }, + }); + } + getDefaultCallbacks() { const self = this; @@ -790,6 +835,7 @@ GfmAutoComplete.atTypeMap = { '/': 'commands', '[vulnerability:': 'vulnerabilities', $: 'snippets', + '[contact:': 'contacts', }; GfmAutoComplete.typesWithBackendFiltering = ['vulnerabilities']; @@ -883,6 +929,11 @@ GfmAutoComplete.Milestones = { return `
  • ${escape(title)}
  • `; }, }; +GfmAutoComplete.Contacts = { + templateFunction({ email, firstName, lastName }) { + return `
  • ${firstName} ${lastName} ${escape(email)}
  • `; + }, +}; GfmAutoComplete.Loading = { template: '
  • Loading...
  • ', diff --git a/app/assets/javascripts/pipeline_wizard/components/step_nav.vue b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue new file mode 100644 index 00000000000..8f9198855c6 --- /dev/null +++ b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 603ad71adb9..cbf38984e23 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -9,6 +9,7 @@ import axios from '~/lib/utils/axios_utils'; import { stripHtml } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; import Suggestions from '~/vue_shared/components/markdown/suggestions.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import MarkdownHeader from './header.vue'; import MarkdownToolbar from './toolbar.vue'; @@ -23,6 +24,7 @@ export default { GlIcon, Suggestions, }, + mixins: [glFeatureFlagsMixin()], props: { /** * This prop should be bound to the value of the `