Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-12 15:09:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-12 15:09:02 +0300
commit49d26b2348f2eb9e345eb1f66214678f42f15dd3 (patch)
treee68c6c2e50aae17d37a4d5508613b3d93627a5e2 /app
parent7f5e08060f261a63ebf5058a95419da66928173a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/feature_highlight/constants.js1
-rw-r--r--app/assets/javascripts/feature_highlight/feature_highlight_popover.vue101
-rw-r--r--app/assets/javascripts/feature_highlight/index.js28
-rw-r--r--app/assets/javascripts/issuable/components/issuable_by_email.vue169
-rw-r--r--app/assets/javascripts/issuable/init_issuable_by_email.js35
-rw-r--r--app/assets/javascripts/issuable_index.js28
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/product_analytics/graphs/index.js2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue9
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_sidebar.vue5
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/constants.js6
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/index.js9
-rw-r--r--app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss21
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss54
-rw-r--r--app/assets/stylesheets/pages/issues.scss12
-rw-r--r--app/helpers/events_helper.rb4
-rw-r--r--app/helpers/projects/alert_management_helper.rb3
-rw-r--r--app/models/event.rb6
-rw-r--r--app/models/note.rb4
-rw-r--r--app/views/dashboard/issues.atom.builder2
-rw-r--r--app/views/dashboard/projects/index.atom.builder2
-rw-r--r--app/views/events/_event.atom.builder2
-rw-r--r--app/views/groups/issues.atom.builder2
-rw-r--r--app/views/groups/show.atom.builder2
-rw-r--r--app/views/issues/_issue.atom.builder2
-rw-r--r--app/views/issues/_issues_calendar.ics.ruby2
-rw-r--r--app/views/layouts/xml.atom.builder2
-rw-r--r--app/views/projects/_issuable_by_email.html.haml49
-rw-r--r--app/views/projects/commits/_commit.atom.builder2
-rw-r--r--app/views/projects/commits/show.atom.builder2
-rw-r--r--app/views/projects/issues/index.atom.builder2
-rw-r--r--app/views/projects/issues/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/index.html.haml4
-rw-r--r--app/views/projects/show.atom.builder2
-rw-r--r--app/views/projects/tags/_tag.atom.builder2
-rw-r--r--app/views/projects/tags/index.atom.builder2
-rw-r--r--app/views/search/results/_issuable.html.haml2
-rw-r--r--app/views/users/show.atom.builder2
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 } &times;
- .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} &middot; 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} &middot; opened %{issuable_created} by %{author} &middot; 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"