diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js index a6faa04b440..689f2f0898e 100644 --- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js +++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js @@ -399,6 +399,12 @@ export const ISSUABLE_CHANGE_LABEL = { defaultKeys: ['l'], }; +export const ISSUABLE_COPY_REF = { + id: 'issuables.copyIssuableRef', + description: __('Copy reference'), + defaultKeys: ['c r'], // eslint-disable-line @gitlab/require-i18n-strings +}; + export const ISSUE_MR_CHANGE_ASSIGNEE = { id: 'issuesMRs.changeAssignee', description: __('Change assignee'), @@ -606,7 +612,12 @@ const PROJECT_FILES_SHORTCUTS_GROUP = { const ISSUABLE_SHORTCUTS_GROUP = { id: 'issuables', name: __('Epics, issues, and merge requests'), - keybindings: [ISSUABLE_COMMENT_OR_REPLY, ISSUABLE_EDIT_DESCRIPTION, ISSUABLE_CHANGE_LABEL], + keybindings: [ + ISSUABLE_COMMENT_OR_REPLY, + ISSUABLE_EDIT_DESCRIPTION, + ISSUABLE_CHANGE_LABEL, + ISSUABLE_COPY_REF, + ], }; const ISSUE_MR_SHORTCUTS_GROUP = { diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js index 0c882ff9ea2..b0e515ac19d 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js @@ -14,6 +14,7 @@ import { ISSUABLE_COMMENT_OR_REPLY, ISSUABLE_EDIT_DESCRIPTION, MR_COPY_SOURCE_BRANCH_NAME, + ISSUABLE_COPY_REF, } from './keybindings'; import Shortcuts from './shortcuts'; @@ -21,15 +22,24 @@ export default class ShortcutsIssuable extends Shortcuts { constructor() { super(); - this.inMemoryButton = document.createElement('button'); - this.clipboardInstance = new ClipboardJS(this.inMemoryButton); - this.clipboardInstance.on('success', () => { + this.branchInMemoryButton = document.createElement('button'); + this.branchClipboardInstance = new ClipboardJS(this.branchInMemoryButton); + this.branchClipboardInstance.on('success', () => { toast(s__('GlobalShortcuts|Copied source branch name to clipboard.')); }); - this.clipboardInstance.on('error', () => { + this.branchClipboardInstance.on('error', () => { toast(s__('GlobalShortcuts|Unable to copy the source branch name at this time.')); }); + this.refInMemoryButton = document.createElement('button'); + this.refClipboardInstance = new ClipboardJS(this.refInMemoryButton); + this.refClipboardInstance.on('success', () => { + toast(s__('GlobalShortcuts|Copied reference to clipboard.')); + }); + this.refClipboardInstance.on('error', () => { + toast(s__('GlobalShortcuts|Unable to copy the reference at this time.')); + }); + this.bindCommands([ [ISSUE_MR_CHANGE_ASSIGNEE, () => ShortcutsIssuable.openSidebarDropdown('assignee')], [ISSUE_MR_CHANGE_MILESTONE, () => ShortcutsIssuable.openSidebarDropdown('milestone')], @@ -37,6 +47,7 @@ export default class ShortcutsIssuable extends Shortcuts { [ISSUABLE_COMMENT_OR_REPLY, ShortcutsIssuable.replyWithSelectedText], [ISSUABLE_EDIT_DESCRIPTION, ShortcutsIssuable.editIssue], [MR_COPY_SOURCE_BRANCH_NAME, () => this.copyBranchName()], + [ISSUABLE_COPY_REF, () => this.copyIssuableRef()], ]); /** @@ -163,9 +174,20 @@ export default class ShortcutsIssuable extends Shortcuts { const branchName = button?.dataset.clipboardText; if (branchName) { - this.inMemoryButton.dataset.clipboardText = branchName; + this.branchInMemoryButton.dataset.clipboardText = branchName; - this.inMemoryButton.dispatchEvent(new CustomEvent('click')); + this.branchInMemoryButton.dispatchEvent(new CustomEvent('click')); + } + } + + async copyIssuableRef() { + const refButton = document.querySelector('.js-copy-reference'); + const copiedRef = refButton?.dataset.clipboardText; + + if (copiedRef) { + this.refInMemoryButton.dataset.clipboardText = copiedRef; + + this.refInMemoryButton.dispatchEvent(new CustomEvent('click')); } } } diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue index 5468e42b6b3..86c0f34215e 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_modal.vue @@ -25,6 +25,7 @@ import { AWS_TOKEN_CONSTANTS, ADD_CI_VARIABLE_MODAL_ID, AWS_TIP_DISMISSED_COOKIE_NAME, + AWS_TIP_TITLE, AWS_TIP_MESSAGE, CONTAINS_VARIABLE_REFERENCE_MESSAGE, defaultVariableState, @@ -62,10 +63,6 @@ export default { }, mixins: [glFeatureFlagsMixin(), trackingMixin], inject: [ - 'awsLogoSvgPath', - 'awsTipCommandsLink', - 'awsTipDeployLink', - 'awsTipLearnLink', 'containsVariableReferenceLink', 'environmentScopeLink', 'isProtectedByDefault', @@ -295,6 +292,7 @@ export default { }, }, i18n: { + awsTipTitle: AWS_TIP_TITLE, awsTipMessage: AWS_TIP_MESSAGE, containsVariableReferenceMessage: CONTAINS_VARIABLE_REFERENCE_MESSAGE, defaultScope: allEnvironments.text, @@ -305,6 +303,9 @@ export default { flagLink: helpPagePath('ci/variables/index', { anchor: 'define-a-cicd-variable-in-the-ui', }), + oidcLink: helpPagePath('ci/cloud_services/index', { + anchor: 'oidc-authorization-with-your-cloud-provider', + }), modalId: ADD_CI_VARIABLE_MODAL_ID, tokens: awsTokens, tokenList: awsTokenList, @@ -322,6 +323,23 @@ export default { @hidden="resetModalHandler" @shown="onShow" > + + + + + + +
- - -
-
-

- - - - -

-

- {{ __('Learn more about deploying to AWS') }} -

-
- -
-
-
+ { const { - awsLogoSvgPath, - awsTipCommandsLink, - awsTipDeployLink, - awsTipLearnLink, containsVariableReferenceLink, endpoint, environmentScopeLink, @@ -57,10 +53,6 @@ const mountCiVariableListApp = (containerEl) => { el: containerEl, apolloProvider, provide: { - awsLogoSvgPath, - awsTipCommandsLink, - awsTipDeployLink, - awsTipLearnLink, containsVariableReferenceLink, endpoint, environmentScopeLink, diff --git a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue index a1b264cfe54..0871d543d46 100644 --- a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue +++ b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue @@ -1,13 +1,5 @@ + diff --git a/app/assets/javascripts/pages/shared/wikis/show.js b/app/assets/javascripts/pages/shared/wikis/show.js index 9906cb595f8..9bc399d07b3 100644 --- a/app/assets/javascripts/pages/shared/wikis/show.js +++ b/app/assets/javascripts/pages/shared/wikis/show.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import Wikis from './wikis'; import WikiContent from './components/wiki_content.vue'; +import WikiExport from './components/wiki_export.vue'; const mountWikiContentApp = () => { const el = document.querySelector('.js-async-wiki-page-content'); @@ -20,8 +21,28 @@ const mountWikiContentApp = () => { } }; +const mountWikiExportApp = () => { + const el = document.querySelector('#js-export-actions'); + + if (!el) return false; + const { target, title, stylesheet } = JSON.parse(el.dataset.options); + + return new Vue({ + el, + provide: { + target, + title, + stylesheet, + }, + render(createElement) { + return createElement(WikiExport); + }, + }); +}; + export const mountApplications = () => { // eslint-disable-next-line no-new new Wikis(); mountWikiContentApp(); + mountWikiExportApp(); }; diff --git a/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue b/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue index f90633c6e03..a1c1b1141a7 100644 --- a/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue +++ b/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue @@ -8,7 +8,9 @@ import { I18N_CARD_TITLE, I18N_GENERIC_ERROR, I18N_FEEDBACK_PARAGRAPH, + I18N_TOAST_SAVED, } from '../custom_email_constants'; +import CustomEmailForm from './custom_email_form.vue'; export default { components: { @@ -18,11 +20,13 @@ export default { GlSprintf, GlLink, GlCard, + CustomEmailForm, }, FEEDBACK_ISSUE_URL, I18N_LOADING_LABEL, I18N_CARD_TITLE, I18N_FEEDBACK_PARAGRAPH, + I18N_TOAST_SAVED, props: { incomingEmail: { type: String, @@ -38,6 +42,7 @@ export default { data() { return { loading: true, + submitting: false, customEmail: null, enabled: false, verificationState: null, @@ -47,6 +52,11 @@ export default { alertMessage: null, }; }, + computed: { + customEmailNotSetUp() { + return !this.enabled && this.verificationState === null && this.customEmail === null; + }, + }, mounted() { this.getCustomEmailDetails(); }, @@ -76,6 +86,21 @@ export default { this.smtpAddress = data.custom_email_smtp_address; this.errorMessage = data.error_message; }, + onSaveCustomEmail(requestData) { + this.alertMessage = null; + this.submitting = true; + + axios + .post(this.customEmailEndpoint, requestData) + .then(({ data }) => { + this.updateData(data); + this.$toast.show(this.$options.I18N_TOAST_SAVED); + }) + .catch(this.handleRequestError) + .finally(() => { + this.submitting = false; + }); + }, }, }; @@ -108,11 +133,20 @@ export default { {{ alertMessage }} + + +