diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-12 15:09:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-12 15:09:02 +0300 |
commit | 49d26b2348f2eb9e345eb1f66214678f42f15dd3 (patch) | |
tree | e68c6c2e50aae17d37a4d5508613b3d93627a5e2 /app | |
parent | 7f5e08060f261a63ebf5058a95419da66928173a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
39 files changed, 482 insertions, 109 deletions
diff --git a/app/assets/javascripts/feature_highlight/constants.js b/app/assets/javascripts/feature_highlight/constants.js new file mode 100644 index 00000000000..3e4cd11f7d5 --- /dev/null +++ b/app/assets/javascripts/feature_highlight/constants.js @@ -0,0 +1 @@ +export const POPOVER_TARGET_ID = 'feature-highlight-trigger'; diff --git a/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue b/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue new file mode 100644 index 00000000000..879427eef96 --- /dev/null +++ b/app/assets/javascripts/feature_highlight/feature_highlight_popover.vue @@ -0,0 +1,101 @@ +<script> +import { + GlPopover, + GlSprintf, + GlLink, + GlButton, + GlSafeHtmlDirective as SafeHtml, +} from '@gitlab/ui'; +import clusterPopover from '@gitlab/svgs/dist/illustrations/cluster_popover.svg'; +import { __ } from '~/locale'; +import { dismiss } from './feature_highlight_helper'; +import { POPOVER_TARGET_ID } from './constants'; + +export default { + components: { + GlPopover, + GlSprintf, + GlLink, + GlButton, + }, + directives: { + SafeHtml, + }, + props: { + autoDevopsHelpPath: { + type: String, + required: true, + }, + highlightId: { + type: String, + required: true, + }, + dismissEndpoint: { + type: String, + required: true, + }, + }, + data() { + return { + dismissed: false, + triggerHidden: false, + }; + }, + methods: { + dismiss() { + dismiss(this.dismissEndpoint, this.highlightId); + this.$refs.popover.$emit('close'); + this.dismissed = true; + }, + hideTrigger() { + if (this.dismissed) { + this.triggerHidden = true; + } + }, + }, + clusterPopover, + targetId: POPOVER_TARGET_ID, + i18n: { + highlightMessage: __('Allows you to add and manage Kubernetes clusters.'), + autoDevopsProTipMessage: __( + 'Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!', + ), + dismissButtonLabel: __('Got it!'), + }, +}; +</script> +<template> + <div class="gl-ml-3"> + <span v-if="!triggerHidden" :id="$options.targetId" class="feature-highlight"></span> + <gl-popover + ref="popover" + :target="$options.targetId" + :css-classes="['feature-highlight-popover']" + triggers="hover" + container="body" + placement="right" + boundary="viewport" + @hidden="hideTrigger" + > + <span + v-safe-html="$options.clusterPopover" + class="feature-highlight-illustration gl-display-flex gl-justify-content-center gl-py-4 gl-w-full" + ></span> + <div class="gl-px-4 gl-py-5"> + <p> + {{ $options.i18n.highlightMessage }} + </p> + <p> + <gl-sprintf :message="$options.i18n.autoDevopsProTipMessage"> + <template #link="{ content }"> + <gl-link class="gl-font-sm" :href="autoDevopsHelpPath">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + <gl-button size="small" icon="thumb-up" variant="confirm" @click="dismiss"> + {{ $options.i18n.dismissButtonLabel }} + </gl-button> + </div> + </gl-popover> + </div> +</template> diff --git a/app/assets/javascripts/feature_highlight/index.js b/app/assets/javascripts/feature_highlight/index.js new file mode 100644 index 00000000000..3a8b211b3c5 --- /dev/null +++ b/app/assets/javascripts/feature_highlight/index.js @@ -0,0 +1,28 @@ +import Vue from 'vue'; + +const init = async () => { + const el = document.querySelector('.js-feature-highlight'); + + if (!el) { + return null; + } + + const { autoDevopsHelpPath, highlight: highlightId, dismissEndpoint } = el.dataset; + const { default: FeatureHighlight } = await import( + /* webpackChunkName: 'feature_highlight' */ './feature_highlight_popover.vue' + ); + + return new Vue({ + el, + render: (h) => + h(FeatureHighlight, { + props: { + autoDevopsHelpPath, + highlightId, + dismissEndpoint, + }, + }), + }); +}; + +export default init; diff --git a/app/assets/javascripts/issuable/components/issuable_by_email.vue b/app/assets/javascripts/issuable/components/issuable_by_email.vue new file mode 100644 index 00000000000..330118ce8a6 --- /dev/null +++ b/app/assets/javascripts/issuable/components/issuable_by_email.vue @@ -0,0 +1,169 @@ +<script> +import { + GlButton, + GlModal, + GlModalDirective, + GlTooltipDirective, + GlSprintf, + GlLink, + GlFormInputGroup, + GlIcon, +} from '@gitlab/ui'; +import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; +import { sprintf, __ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; + +export default { + name: 'IssuableByEmail', + components: { + GlButton, + GlModal, + GlSprintf, + GlLink, + GlFormInputGroup, + GlIcon, + ModalCopyButton, + }, + directives: { + GlModal: GlModalDirective, + GlTooltip: GlTooltipDirective, + }, + inject: { + initialEmail: { + default: null, + }, + issuableType: { + default: '', + }, + emailsHelpPagePath: { + default: '', + }, + quickActionsHelpPath: { + default: '', + }, + markdownHelpPath: { + default: '', + }, + resetPath: { + default: '', + }, + }, + data() { + return { + email: this.initialEmail, + // eslint-disable-next-line @gitlab/require-i18n-strings + issuableName: this.issuableType === 'issue' ? 'issue' : 'merge request', + }; + }, + computed: { + mailToLink() { + const subject = sprintf(__('Enter the %{name} title'), { + name: this.issuableName, + }); + const body = sprintf(__('Enter the %{name} description'), { + name: this.issuableName, + }); + // eslint-disable-next-line @gitlab/require-i18n-strings + return `mailto:${this.email}?subject=${subject}&body=${body}`; + }, + }, + methods: { + async resetIncomingEmailToken() { + try { + const { + data: { new_address: newAddress }, + } = await axios.put(this.resetPath); + this.email = newAddress; + } catch { + this.$toast.show(__('There was an error when reseting email token.'), { type: 'error' }); + } + }, + cancelHandler() { + this.$refs.modal.hide(); + }, + }, + modalId: 'issuable-email-modal', +}; +</script> + +<template> + <div> + <gl-button v-gl-modal="$options.modalId" variant="link" data-testid="issuable-email-modal-btn" + ><gl-sprintf :message="__('Email a new %{name} to this project')" + ><template #name>{{ issuableName }}</template></gl-sprintf + ></gl-button + > + <gl-modal ref="modal" :modal-id="$options.modalId"> + <template #modal-title> + <gl-sprintf :message="__('Create new %{name} by email')"> + <template #name>{{ issuableName }}</template> + </gl-sprintf> + </template> + <p> + <gl-sprintf + :message=" + __( + 'You can create a new %{name} inside this project by sending an email to the following email address:', + ) + " + > + <template #name>{{ issuableName }}</template> + </gl-sprintf> + </p> + <gl-form-input-group :value="email" readonly select-on-click class="gl-mb-4"> + <template #append> + <modal-copy-button :text="email" :title="__('Copy')" :modal-id="$options.modalId" /> + <gl-button + v-gl-tooltip.hover + :href="mailToLink" + :title="__('Send email')" + icon="mail" + data-testid="mail-to-btn" + /> + </template> + </gl-form-input-group> + <p> + <gl-sprintf + :message=" + __( + 'The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported.', + ) + " + > + <template #quickActionsLink="{ content }"> + <gl-link :href="quickActionsHelpPath" target="_blank">{{ content }}</gl-link> + </template> + <template #markdownLink="{ content }"> + <gl-link :href="markdownHelpPath" target="_blank">{{ content }}</gl-link> + </template> + </gl-sprintf> + </p> + <p> + <gl-sprintf + :message=" + __( + 'This is a private email address %{helpIcon} generated just for you. Anyone who gets ahold of it can create issues or merge requests as if they were you. You should %{resetLinkStart}reset it%{resetLinkEnd} if that ever happens.', + ) + " + > + <template #helpIcon> + <gl-link :href="emailsHelpPagePath" target="_blank" + ><gl-icon class="gl-text-blue-600" name="question-o" + /></gl-link> + </template> + <template #resetLink="{ content }"> + <gl-button + variant="link" + data-testid="incoming-email-token-reset" + @click="resetIncomingEmailToken" + >{{ content }}</gl-button + > + </template> + </gl-sprintf> + </p> + <template #modal-footer> + <gl-button category="secondary" @click="cancelHandler">{{ s__('Cancel') }}</gl-button> + </template> + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/issuable/init_issuable_by_email.js b/app/assets/javascripts/issuable/init_issuable_by_email.js new file mode 100644 index 00000000000..1fed55c3d8e --- /dev/null +++ b/app/assets/javascripts/issuable/init_issuable_by_email.js @@ -0,0 +1,35 @@ +import Vue from 'vue'; +import { GlToast } from '@gitlab/ui'; +import IssuableByEmail from './components/issuable_by_email.vue'; + +Vue.use(GlToast); + +export default () => { + const el = document.querySelector('.js-issueable-by-email'); + + if (!el) return null; + + const { + initialEmail, + issuableType, + emailsHelpPagePath, + quickActionsHelpPath, + markdownHelpPath, + resetPath, + } = el.dataset; + + return new Vue({ + el, + provide: { + initialEmail, + issuableType, + emailsHelpPagePath, + quickActionsHelpPath, + markdownHelpPath, + resetPath, + }, + render(h) { + return h(IssuableByEmail); + }, + }); +}; diff --git a/app/assets/javascripts/issuable_index.js b/app/assets/javascripts/issuable_index.js index 4f31d26ab5d..4856f9781ce 100644 --- a/app/assets/javascripts/issuable_index.js +++ b/app/assets/javascripts/issuable_index.js @@ -1,35 +1,7 @@ -import $ from 'jquery'; -import axios from './lib/utils/axios_utils'; -import { deprecatedCreateFlash as flash } from './flash'; -import { s__, __ } from './locale'; import issuableInitBulkUpdateSidebar from './issuable_init_bulk_update_sidebar'; export default class IssuableIndex { constructor(pagePrefix) { issuableInitBulkUpdateSidebar.init(pagePrefix); - IssuableIndex.resetIncomingEmailToken(); - } - - static resetIncomingEmailToken() { - const $resetToken = $('.incoming-email-token-reset'); - - $resetToken.on('click', (e) => { - e.preventDefault(); - - $resetToken.text(s__('EmailToken|resetting...')); - - axios - .put($resetToken.attr('href')) - .then(({ data }) => { - $('#issuable_email').val(data.new_address).focus(); - - $resetToken.text(s__('EmailToken|reset it')); - }) - .catch(() => { - flash(__('There was an error when reseting email token.')); - - $resetToken.text(s__('EmailToken|reset it')); - }); - }); } } diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js index 5956933fd99..25790a27888 100644 --- a/app/assets/javascripts/pages/projects/issues/index/index.js +++ b/app/assets/javascripts/pages/projects/issues/index/index.js @@ -9,6 +9,7 @@ import { FILTERED_SEARCH } from '~/pages/constants'; import { ISSUABLE_INDEX } from '~/pages/projects/constants'; import initIssuablesList from '~/issues_list'; import initManualOrdering from '~/manual_ordering'; +import initIssuableByEmail from '~/issuable/init_issuable_by_email'; IssuableFilteredSearchTokenKeys.addExtraTokensForIssues(); @@ -24,3 +25,4 @@ new UsersSelect(); initManualOrdering(); initIssuablesList(); +initIssuableByEmail(); diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js index 94a12cc2706..e9138d6ab4d 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -6,6 +6,7 @@ import initFilteredSearch from '~/pages/search/init_filtered_search'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; import { FILTERED_SEARCH } from '~/pages/constants'; import { ISSUABLE_INDEX } from '~/pages/projects/constants'; +import initIssuableByEmail from '~/issuable/init_issuable_by_email'; new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new @@ -19,3 +20,5 @@ initFilteredSearch({ new UsersSelect(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new + +initIssuableByEmail(); diff --git a/app/assets/javascripts/pages/projects/product_analytics/graphs/index.js b/app/assets/javascripts/pages/projects/product_analytics/graphs/index.js index 0539d318471..ba03fccdb03 100644 --- a/app/assets/javascripts/pages/projects/product_analytics/graphs/index.js +++ b/app/assets/javascripts/pages/projects/product_analytics/graphs/index.js @@ -1,3 +1,3 @@ import initActivityCharts from '~/analytics/product_analytics/activity_charts_bundle'; -document.addEventListener('DOMContentLoaded', () => initActivityCharts()); +initActivityCharts(); diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue index 673c6f4a1eb..d1cfa84248c 100644 --- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue +++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue @@ -83,6 +83,9 @@ export default { alertId: { default: '', }, + isThreatMonitoringPage: { + default: false, + }, projectId: { default: '', }, @@ -364,7 +367,11 @@ export default { </alert-summary-row> <alert-details-table :alert="alert" :loading="loading" /> </gl-tab> - <gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title"> + <gl-tab + v-if="isThreatMonitoringPage" + :data-testid="$options.tabsConfig[1].id" + :title="$options.tabsConfig[1].title" + > <alert-metrics :dashboard-url="alert.metricsDashboardUrl" /> </gl-tab> <gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title"> diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_sidebar.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_sidebar.vue index 12c58b582c5..4584023380c 100644 --- a/app/assets/javascripts/vue_shared/alert_details/components/alert_sidebar.vue +++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_sidebar.vue @@ -19,6 +19,10 @@ export default { projectId: { default: '', }, + // TODO remove this limitation in https://gitlab.com/gitlab-org/gitlab/-/issues/296717 + isThreatMonitoringPage: { + default: false, + }, }, props: { alert: { @@ -62,6 +66,7 @@ export default { @alert-error="$emit('alert-error', $event)" /> <sidebar-status + v-if="!isThreatMonitoringPage" :project-path="projectPath" :alert="alert" @toggle-sidebar="$emit('toggle-sidebar')" diff --git a/app/assets/javascripts/vue_shared/alert_details/constants.js b/app/assets/javascripts/vue_shared/alert_details/constants.js index 56f79410064..2ab5160534c 100644 --- a/app/assets/javascripts/vue_shared/alert_details/constants.js +++ b/app/assets/javascripts/vue_shared/alert_details/constants.js @@ -9,11 +9,10 @@ export const SEVERITY_LEVELS = { UNKNOWN: s__('severity|Unknown'), }; -export const DEFAULT_PAGE = 'OPERATIONS'; - /* eslint-disable @gitlab/require-i18n-strings */ export const PAGE_CONFIG = { OPERATIONS: { + TITLE: 'OPERATIONS', // Tracks snowplow event when user views alert details TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: { category: 'Alert Management', @@ -26,4 +25,7 @@ export const PAGE_CONFIG = { label: 'Status', }, }, + THREAT_MONITORING: { + TITLE: 'THREAT_MONITORING', + }, }; diff --git a/app/assets/javascripts/vue_shared/alert_details/index.js b/app/assets/javascripts/vue_shared/alert_details/index.js index 643d6b3a3fe..eba8adf416a 100644 --- a/app/assets/javascripts/vue_shared/alert_details/index.js +++ b/app/assets/javascripts/vue_shared/alert_details/index.js @@ -6,13 +6,13 @@ import createDefaultClient from '~/lib/graphql'; import AlertDetails from './components/alert_details.vue'; import sidebarStatusQuery from './graphql/queries/alert_sidebar_status.query.graphql'; import createRouter from './router'; -import { DEFAULT_PAGE, PAGE_CONFIG } from './constants'; +import { PAGE_CONFIG } from './constants'; Vue.use(VueApollo); export default (selector) => { const domEl = document.querySelector(selector); - const { alertId, projectPath, projectIssuesPath, projectId, page = DEFAULT_PAGE } = domEl.dataset; + const { alertId, projectPath, projectIssuesPath, projectId, page } = domEl.dataset; const router = createRouter(); const resolvers = { @@ -52,16 +52,19 @@ export default (selector) => { const provide = { projectPath, alertId, + page, projectIssuesPath, projectId, }; - if (page === DEFAULT_PAGE) { + if (page === PAGE_CONFIG.OPERATIONS.TITLE) { const { TRACK_ALERTS_DETAILS_VIEWS_OPTIONS, TRACK_ALERT_STATUS_UPDATE_OPTIONS } = PAGE_CONFIG[ page ]; provide.trackAlertsDetailsViewsOptions = TRACK_ALERTS_DETAILS_VIEWS_OPTIONS; provide.trackAlertStatusUpdateOptions = TRACK_ALERT_STATUS_UPDATE_OPTIONS; + } else if (page === PAGE_CONFIG.THREAT_MONITORING.TITLE) { + provide.isThreatMonitoringPage = true; } // eslint-disable-next-line no-new diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss index 093cba3560f..8d34f35502e 100644 --- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss +++ b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss @@ -5,7 +5,7 @@ $bs-input-focus-border: #80bdff; $bs-input-focus-box-shadow: rgba(0, 123, 255, 0.25); - a:not(.btn):not(.gl-tab-nav-item), + a:not(.btn), .gl-button.btn-link, .gl-button.btn-link:hover, .gl-button.btn-link:focus, @@ -34,6 +34,7 @@ .ide-pipeline .top-bar .controllers .controllers-buttons, .controllers-buttons svg, .nav-links li a.active, + .gl-tabs-nav li a.gl-tab-nav-item-active, .md-area.is-focused { color: var(--ide-text-color, $gl-text-color); } @@ -44,13 +45,15 @@ } .nav-links:not(.quick-links) li:not(.md-header-toolbar) a, + .gl-tabs-nav li a, .dropdown-menu-inner-content, .file-row .file-row-icon svg, .file-row:hover .file-row-icon svg { color: var(--ide-text-color-secondary, $gl-text-color-secondary); } - .nav-links:not(.quick-links) li:not(.md-header-toolbar) { + .nav-links:not(.quick-links) li:not(.md-header-toolbar), + .gl-tabs-nav li { &:hover a, &.active a, a:hover, @@ -61,6 +64,10 @@ border-color: var(--ide-input-border, $gray-darkest); } } + + a.gl-tab-nav-item-active { + box-shadow: inset 0 -2px 0 0 var(--ide-input-border, $gray-darkest); + } } .drag-handle:hover { @@ -142,6 +149,7 @@ .md table:not(.code) tbody td, .md table:not(.code) tr th, .nav-links:not(.quick-links), + .gl-tabs-nav, .common-note-form .md-area.is-focused .nav-links { border-color: var(--ide-border-color-alt, $white-dark); } @@ -175,6 +183,10 @@ border-color: var(--ide-highlight-accent, $gl-text-color); } + .gl-tabs-nav li a.gl-tab-nav-item-active { + box-shadow: inset 0 -2px 0 0 var(--ide-highlight-accent, $gl-text-color); + } + // for other themes, suppress different avatar default colors for simplicity .avatar-container { &, @@ -304,6 +316,11 @@ border-color: var(--ide-dropdown-hover-background, $border-color); } + .gl-tabs-nav { + background-color: var(--ide-dropdown-hover-background, $white); + box-shadow: inset 0 -2px 0 0 var(--ide-dropdown-hover-background, $border-color); + } + .divider { background-color: var(--ide-dropdown-hover-background, $gray-100); border-color: var(--ide-dropdown-hover-background, $gray-100); diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss index b5b34c0a64e..7c4d51ab677 100644 --- a/app/assets/stylesheets/page_bundles/ide.scss +++ b/app/assets/stylesheets/page_bundles/ide.scss @@ -97,7 +97,8 @@ $ide-commit-header-height: 48px; border-right: 1px solid var(--ide-border-color, $white-dark); border-bottom: 1px solid var(--ide-border-color, $white-dark); - &.active { + &.active, + .gl-tab-nav-item-active { background-color: var(--ide-highlight-background, $white); border-bottom-color: transparent; } @@ -114,6 +115,42 @@ $ide-commit-header-height: 48px; } } } + + .gl-tab-content { + padding: 0; + } + + .gl-tabs-nav { + border-width: 0; + + li { + padding: 0 !important; + background: transparent !important; + border: 0 !important; + + a { + display: flex; + align-items: center; + padding: $grid-size $gl-padding !important; + box-shadow: none !important; + font-weight: normal !important; + + background-color: var(--ide-background-hover, $gray-normal); + border-right: 1px solid var(--ide-border-color, $white-dark); + border-bottom: 1px solid var(--ide-border-color, $white-dark); + + &.gl-tab-nav-item-active { + background-color: var(--ide-highlight-background, $white); + border-color: var(--ide-border-color, $white-dark); + border-bottom-color: transparent; + } + + .multi-file-tab-close svg { + top: 0; + } + } + } + } } .multi-file-tab { @@ -634,7 +671,8 @@ $ide-commit-header-height: 48px; height: 100%; } - .nav-links { + .nav-links, + .gl-tabs-nav { height: 30px; } @@ -976,17 +1014,25 @@ $ide-commit-header-height: 48px; } .ide-nav-form { - .nav-links li { + .nav-links li, + .gl-tabs-nav li { width: 50%; padding-left: 0; padding-right: 0; a { text-align: center; + font-size: 14px; + line-height: 30px; - &:not(.active) { + &:not(.active), + &:not(.gl-tab-nav-item-active) { background-color: var(--ide-dropdown-background, $gray-light); } + + &.gl-tab-nav-item-active { + font-weight: bold; + } } } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index fb483ade107..2a8a86615f6 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -108,18 +108,6 @@ ul.related-merge-requests > li { } } -.issuable-email-modal-btn { - padding: 0; - color: $blue-600; - background-color: transparent; - border: 0; - outline: 0; - - &:hover { - text-decoration: underline; - } -} - .email-modal-input-group { margin-bottom: 10px; diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index e6603237676..52b8ac915f1 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -178,8 +178,8 @@ module EventsHelper def event_note_target_url(event) if event.commit_note? project_commit_url(event.project, event.note_target, anchor: dom_id(event.target)) - elsif event.project_snippet_note? - project_snippet_url(event.project, event.note_target, anchor: dom_id(event.target)) + elsif event.snippet_note? + gitlab_snippet_url(event.note_target, anchor: dom_id(event.target)) elsif event.issue_note? project_issue_url(event.project, id: event.note_target, anchor: dom_id(event.target)) elsif event.merge_request_note? diff --git a/app/helpers/projects/alert_management_helper.rb b/app/helpers/projects/alert_management_helper.rb index 5fad38bd32c..b705258f133 100644 --- a/app/helpers/projects/alert_management_helper.rb +++ b/app/helpers/projects/alert_management_helper.rb @@ -20,7 +20,8 @@ module Projects::AlertManagementHelper 'alert-id' => alert_id, 'project-path' => project.full_path, 'project-id' => project.id, - 'project-issues-path' => project_issues_path(project) + 'project-issues-path' => project_issues_path(project), + 'page' => 'OPERATIONS' } end diff --git a/app/models/event.rb b/app/models/event.rb index 671def16151..401dfc4cb02 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -294,10 +294,14 @@ class Event < ApplicationRecord note? && target && target.for_merge_request? end - def project_snippet_note? + def snippet_note? note? && target && target.for_snippet? end + def project_snippet_note? + note? && target && target.for_project_snippet? + end + def personal_snippet_note? note? && target && target.for_personal_snippet? end diff --git a/app/models/note.rb b/app/models/note.rb index d169d88149b..fdc972d9726 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -259,6 +259,10 @@ class Note < ApplicationRecord noteable_type == 'AlertManagement::Alert' end + def for_project_snippet? + noteable.is_a?(ProjectSnippet) + end + def for_personal_snippet? noteable.is_a?(PersonalSnippet) end diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 6034389b897..729e966e48a 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop: disable CodeReuse/ActiveRecord xml.title "#{current_user.name} issues" xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml" diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index 747c53b440e..85709266548 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "Activity" xml.link href: dashboard_projects_url(rss_url_options), rel: "self", type: "application/atom+xml" xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder index 406e8a93194..17bf43a4590 100644 --- a/app/views/events/_event.atom.builder +++ b/app/views/events/_event.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + return unless event.visible_to_user?(current_user) event = event.present diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index 2fd96c9d158..6f29a4e439c 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop: disable CodeReuse/ActiveRecord xml.title "#{@group.name} issues" xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml" diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index 0f67b15c301..5610746ad01 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "#{@group.name} activity" xml.link href: group_url(@group, rss_url_options), rel: "self", type: "application/atom+xml" xml.link href: group_url(@group), rel: "alternate", type: "text/html" diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder index 94c32df7c60..e2ab360a3e4 100644 --- a/app/views/issues/_issue.atom.builder +++ b/app/views/issues/_issue.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.entry do xml.id project_issue_url(issue.project, issue) xml.link href: project_issue_url(issue.project, issue) diff --git a/app/views/issues/_issues_calendar.ics.ruby b/app/views/issues/_issues_calendar.ics.ruby index 94c3099ace2..c21c4dac9f0 100644 --- a/app/views/issues/_issues_calendar.ics.ruby +++ b/app/views/issues/_issues_calendar.ics.ruby @@ -1,3 +1,5 @@ +# frozen_string_literal: true + cal = Icalendar::Calendar.new cal.prodid = '-//GitLab//NONSGML GitLab//EN' cal.x_wr_calname = 'GitLab Issues' diff --git a/app/views/layouts/xml.atom.builder b/app/views/layouts/xml.atom.builder index 4ee09cb87a1..7144b6305a2 100644 --- a/app/views/layouts/xml.atom.builder +++ b/app/views/layouts/xml.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.instruct! xml.feed 'xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:media' => 'http://search.yahoo.com/mrss/' do xml << yield diff --git a/app/views/projects/_issuable_by_email.html.haml b/app/views/projects/_issuable_by_email.html.haml deleted file mode 100644 index c11ee765cca..00000000000 --- a/app/views/projects/_issuable_by_email.html.haml +++ /dev/null @@ -1,49 +0,0 @@ -- name = issuable_type == 'issue' ? 'issue' : 'merge request' - -.issuable-footer.text-center - %button.issuable-email-modal-btn{ type: "button", data: { toggle: "modal", target: "#issuable-email-modal" } } - Email a new #{name} to this project - -#issuable-email-modal.modal.fade{ tabindex: "-1", role: "dialog" } - .modal-dialog{ role: "document" } - .modal-content - .modal-header - %h4.modal-title - Create new #{name} by email - %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } - %span{ "aria-hidden": true } × - .modal-body - %p - You can create a new #{name} inside this project by sending an email to the following email address: - .email-modal-input-group.input-group - = text_field_tag :issuable_email, email, class: "monospace js-select-on-focus form-control", readonly: true - .input-group-append - = clipboard_button(target: '#issuable_email', class: 'btn btn-clipboard input-group-text btn-transparent d-none d-sm-block') - - - if issuable_type == 'issue' - - enter_title_text = _('Enter the issue title') - - enter_description_text = _('Enter the issue description') - - else - - enter_title_text = _('Enter the merge request title') - - enter_description_text = _('Enter the merge request description') - = mail_to email, class: 'btn btn-clipboard btn-transparent', - subject: enter_title_text, - body: enter_description_text, - title: _('Send email'), - data: { toggle: 'tooltip', placement: 'bottom' } do - = sprite_icon('mail') - - %p - = render 'by_email_description' - %p - This is a private email address - %span< - = link_to help_page_path('development/emails', anchor: 'email-namespace'), target: '_blank', rel: 'noopener', aria: { label: 'Learn more about incoming email addresses' } do - = sprite_icon('question-o') - - generated just for you. - - Anyone who gets ahold of it can create issues or merge requests as if they were you. - You should - = link_to 'reset it', new_issuable_address_project_path(@project, issuable_type: issuable_type), class: 'incoming-email-token-reset' - if that ever happens. diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder index 640b5ecf99e..8a27649af50 100644 --- a/app/views/projects/commits/_commit.atom.builder +++ b/app/views/projects/commits/_commit.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.entry do xml.id project_commit_url(@project, id: commit.id) xml.link href: project_commit_url(@project, id: commit.id) diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index a9b77631474..a4db3c47f59 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "#{@project.name}:#{@ref} commits" xml.link href: project_commits_url(@project, @ref, rss_url_options), rel: "self", type: "application/atom+xml" xml.link href: project_commits_url(@project, @ref), rel: "alternate", type: "text/html" diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 6566866be82..4de9c0ed34b 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop: disable CodeReuse/ActiveRecord xml.title "#{@project.name} issues" xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml" diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 842b3432991..dd66e00b813 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -3,6 +3,7 @@ - page_title _("Issues") - new_issue_email = @project.new_issuable_address(current_user, 'issue') - add_page_specific_style 'page_bundles/issues_list' +- issuable_type = 'issue' = content_for :meta_tags do = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues") @@ -24,7 +25,8 @@ .issues-holder = render 'issues' - if new_issue_email - = render 'projects/issuable_by_email', email: new_issue_email, issuable_type: 'issue' + .issuable-footer.text-center + .js-issueable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } - else - new_project_issue_button_path = @project.archived? ? false : new_project_issue_path(@project) = render 'shared/empty_states/issues', new_project_issue_button_path: new_project_issue_button_path, show_import_button: true diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 36b1cf0796f..62a251c7015 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,6 +1,7 @@ - @can_bulk_update = can?(current_user, :admin_merge_request, @project) - merge_project = merge_request_source_project_for_project(@project) - new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project +- issuable_type = 'merge_request' - page_title _("Merge Requests") - new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request') @@ -21,6 +22,7 @@ .merge-requests-holder = render 'merge_requests' - if new_merge_request_email - = render 'projects/issuable_by_email', email: new_merge_request_email, issuable_type: 'merge_request' + .issuable-footer.text-center + .js-issueable-by-email{ data: { initial_email: new_merge_request_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } } - else = render 'shared/empty_states/merge_requests', button_path: new_merge_request_path diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 39f8cb9a0e0..95b49aa0406 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "#{@project.name} activity" xml.link href: project_url(@project, rss_url_options), rel: "self", type: "application/atom+xml" xml.link href: project_url(@project), rel: "alternate", type: "text/html" diff --git a/app/views/projects/tags/_tag.atom.builder b/app/views/projects/tags/_tag.atom.builder index e4b2428d267..1d5b6832357 100644 --- a/app/views/projects/tags/_tag.atom.builder +++ b/app/views/projects/tags/_tag.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + commit = @repository.commit(tag.dereferenced_target) release = @releases.find { |r| r.tag == tag.name } tag_url = project_tag_url(@project, tag.name) diff --git a/app/views/projects/tags/index.atom.builder b/app/views/projects/tags/index.atom.builder index b9b58b7beaa..68d51ebc89c 100644 --- a/app/views/projects/tags/index.atom.builder +++ b/app/views/projects/tags/index.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "#{@project.name} tags" xml.link href: project_tags_url(@project, @ref, rss_url_options), rel: 'self', type: 'application/atom+xml' xml.link href: project_tags_url(@project, @ref), rel: 'alternate', type: 'text/html' diff --git a/app/views/search/results/_issuable.html.haml b/app/views/search/results/_issuable.html.haml index 288ac53a954..8aad4848aa2 100644 --- a/app/views/search/results/_issuable.html.haml +++ b/app/views/search/results/_issuable.html.haml @@ -5,6 +5,6 @@ = link_to issuable_path(issuable), data: { track_event: 'click_text', track_label: "#{issuable.class.name.downcase}_title", track_property: 'search_result' }, class: 'gl-w-full' do %span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issuable.title .gl-text-gray-500.gl-my-3 - = sprintf(s_(' %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author}'), { project_name: issuable.project.full_name, issuable_iid: issuable.iid, issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe + = sprintf(s_(' %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author} · updated %{issuable_updated}'), { project_name: issuable.project.full_name, issuable_iid: issuable.iid, issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), issuable_updated: time_ago_with_tooltip(issuable.updated_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe .description.term.col-sm-10.gl-px-0 = highlight_and_truncate_issuable(issuable, @search_term, @search_highlight) diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index e95814875f1..43e67347cd0 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -1,3 +1,5 @@ +# frozen_string_literal: true + xml.title "#{@user.name} activity" xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml" xml.link href: user_url(@user), rel: "alternate", type: "text/html" |