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>2023-12-21 21:16:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-12-21 21:16:10 +0300
commitd2f2219fd58e572c10d77183e2f65de8fcc8df96 (patch)
tree2cde75cd3d994b7febe1349ab61b7a94a95c78cf /app
parent6323146895db2be6f04846b3c98060b7349207b9 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/environments/components/new_environment_item.vue1
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js13
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue76
-rw-r--r--app/assets/javascripts/lib/utils/constants.js5
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js45
-rw-r--r--app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue8
-rw-r--r--app/assets/javascripts/search/store/actions.js51
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue4
-rw-r--r--app/assets/stylesheets/page_bundles/login.scss3
-rw-r--r--app/controllers/projects/environments_controller.rb2
-rw-r--r--app/graphql/mutations/issues/set_assignees.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb12
-rw-r--r--app/graphql/types/project_type.rb12
-rw-r--r--app/helpers/environments_helper.rb11
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/project.rb12
-rw-r--r--app/models/project_statistics.rb5
-rw-r--r--app/services/issuable_base_service.rb3
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/devise_empty.html.haml2
-rw-r--r--app/views/layouts/signup_onboarding.html.haml2
-rw-r--r--app/views/projects/environments/folder.html.haml2
22 files changed, 188 insertions, 91 deletions
diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue
index aacb460a817..8bd55e697fa 100644
--- a/app/assets/javascripts/environments/components/new_environment_item.vue
+++ b/app/assets/javascripts/environments/components/new_environment_item.vue
@@ -368,6 +368,7 @@ export default {
</div>
<div v-if="clusterAgent" :class="$options.kubernetesOverviewClasses">
<kubernetes-overview
+ :class="{ 'gl-ml-7': inFolder }"
:cluster-agent="clusterAgent"
:namespace="kubernetesNamespace"
:flux-resource-path="fluxResourcePath"
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 0201fb53f77..05a2eba6af3 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import Translate from '~/vue_shared/translate';
+import { removeLastSlashInUrlPath } from '~/lib/utils/url_utility';
import { apolloProvider } from '../graphql/client';
import EnvironmentsFolderView from './environments_folder_view.vue';
import EnvironmentsFolderApp from './environments_folder_app.vue';
@@ -20,10 +21,9 @@ export default () => {
if (gon.features.environmentsFolderNewLook) {
Vue.use(VueRouter);
- const folderName = environmentsData.environmentsDataFolderName;
- const folderPath = environmentsData.environmentsDataEndpoint.replace('.json', '');
- const projectPath = environmentsData.environmentsDataProjectPath;
- const helpPagePath = environmentsData.environmentsDataHelpPagePath;
+ const folderPath = environmentsData.endpoint.replace('.json', '');
+ const kasTunnelUrl = removeLastSlashInUrlPath(environmentsData.kasTunnelUrl);
+ const { projectPath, folderName, helpPagePath } = environmentsData;
const router = new VueRouter({
mode: 'history',
@@ -54,6 +54,7 @@ export default () => {
provide: {
projectPath,
helpPagePath,
+ kasTunnelUrl,
},
apolloProvider,
router,
@@ -74,8 +75,8 @@ export default () => {
},
data() {
return {
- endpoint: environmentsData.environmentsDataEndpoint,
- folderName: environmentsData.environmentsDataFolderName,
+ endpoint: environmentsData.endpoint,
+ folderName: environmentsData.folderName,
cssContainerClass: environmentsData.cssClass,
};
},
diff --git a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
index 52a12cc7771..e3fa0ce8073 100644
--- a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
@@ -1,20 +1,20 @@
<script>
import { GlAvatarLabeled, GlCollapsibleListbox } from '@gitlab/ui';
import { debounce } from 'lodash';
+import produce from 'immer';
import { __ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { PROJECTS_PER_PAGE } from '../constants';
import getProjectsQuery from '../graphql/queries/get_projects.query.graphql';
export default {
- PROJECTS_PER_PAGE,
- projectQueryPageInfo: {
- endCursor: '',
- },
+ name: 'ProjectDropdown',
+
components: {
GlAvatarLabeled,
GlCollapsibleListbox,
},
+
props: {
selectedProject: {
type: Object,
@@ -22,27 +22,29 @@ export default {
default: null,
},
},
+
data() {
return {
initialProjectsLoading: true,
+ isLoadingMore: false,
projectSearchQuery: '',
selectedProjectId: this.selectedProject?.id,
};
},
+
apollo: {
projects: {
query: getProjectsQuery,
variables() {
return {
- search: this.projectSearchQuery,
- first: this.$options.PROJECTS_PER_PAGE,
- after: this.$options.projectQueryPageInfo.endCursor,
- searchNamespaces: true,
- sort: 'similarity',
+ ...this.queryVariables,
};
},
update(data) {
- return data?.projects?.nodes.filter((project) => !project.repository?.empty) ?? [];
+ return {
+ nodes: data?.projects?.nodes.filter((project) => !project.repository?.empty) ?? [],
+ pageInfo: data?.projects?.pageInfo,
+ };
},
result() {
this.initialProjectsLoading = false;
@@ -52,24 +54,37 @@ export default {
},
},
},
+
computed: {
- projectsLoading() {
- return Boolean(this.$apollo.queries.projects.loading);
+ queryVariables() {
+ return {
+ search: this.projectSearchQuery,
+ first: PROJECTS_PER_PAGE,
+ searchNamespaces: true,
+ sort: 'similarity',
+ };
+ },
+ isLoading() {
+ return this.$apollo.queries.projects.loading && !this.isLoadingMore;
},
projectDropdownText() {
return this.selectedProject?.nameWithNamespace || this.$options.i18n.selectProjectText;
},
projectList() {
- return (this.projects || []).map((project) => ({
+ return (this.projects?.nodes || []).map((project) => ({
...project,
text: project.nameWithNamespace,
value: String(project.id),
}));
},
+ hasNextPage() {
+ return this.projects?.pageInfo?.hasNextPage;
+ },
},
+
methods: {
findProjectById(id) {
- return this.projects.find((project) => id === project.id);
+ return this.projects?.nodes?.find((project) => id === project.id);
},
onProjectSelect(projectId) {
this.$emit('change', this.findProjectById(projectId));
@@ -77,13 +92,41 @@ export default {
onError({ message } = {}) {
this.$emit('error', { message });
},
+ async onBottomReached() {
+ if (!this.hasNextPage) return;
+
+ this.isLoadingMore = true;
+
+ try {
+ await this.$apollo.queries.projects.fetchMore({
+ variables: {
+ ...this.queryVariables,
+ after: this.projects.pageInfo?.endCursor,
+ },
+ updateQuery: (previousResult, { fetchMoreResult }) => {
+ return produce(fetchMoreResult, (draftData) => {
+ draftData.projects.nodes = [
+ ...previousResult.projects.nodes,
+ ...draftData.projects.nodes,
+ ];
+ });
+ },
+ });
+ } catch (error) {
+ this.onError({ message: __('Failed to load projects') });
+ } finally {
+ this.isLoadingMore = false;
+ }
+ },
onSearch: debounce(function debouncedSearch(query) {
this.projectSearchQuery = query;
}, 250),
},
+
i18n: {
selectProjectText: __('Select a project'),
},
+
AVATAR_SHAPE_OPTION_RECT,
};
</script>
@@ -97,8 +140,11 @@ export default {
:header-text="$options.i18n.selectProjectText"
:loading="initialProjectsLoading"
:searchable="true"
- :searching="projectsLoading"
+ :searching="isLoading"
fluid-width
+ infinite-scroll
+ :infinite-scroll-loading="isLoadingMore"
+ @bottom-reached="onBottomReached"
@search="onSearch"
@select="onProjectSelect"
>
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js
index d9ac0abf7b3..77986539403 100644
--- a/app/assets/javascripts/lib/utils/constants.js
+++ b/app/assets/javascripts/lib/utils/constants.js
@@ -19,10 +19,5 @@ export const DRAWER_Z_INDEX = 252;
export const MIN_USERNAME_LENGTH = 2;
-export const BYTES_FORMAT_BYTES = 'B';
-export const BYTES_FORMAT_KIB = 'KiB';
-export const BYTES_FORMAT_MIB = 'MiB';
-export const BYTES_FORMAT_GIB = 'GiB';
-
export const DEFAULT_CI_CONFIG_PATH = '.gitlab-ci.yml';
export const CI_CONFIG_PATH_EXTENSION = /(\.gitlab-ci\.yml)/;
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index d17719c0bc0..01c5bc1f1fc 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -1,12 +1,5 @@
import { sprintf, __ } from '~/locale';
-import {
- BYTES_IN_KIB,
- THOUSAND,
- BYTES_FORMAT_BYTES,
- BYTES_FORMAT_KIB,
- BYTES_FORMAT_MIB,
- BYTES_FORMAT_GIB,
-} from './constants';
+import { BYTES_IN_KIB, THOUSAND } from './constants';
/**
* Function that allows a number with an X amount of decimals
@@ -73,47 +66,47 @@ export function bytesToGiB(number) {
/**
* Formats the bytes in number into a more understandable
* representation. Returns an array with the first value being the human size
- * and the second value being the format (e.g., [1.5, 'KiB']).
+ * and the second value being the label (e.g., [1.5, 'KiB']).
*
- * @param {Number} size
- * @param {Number} digits - The number of digits to appear after the decimal point
- * @returns {String}
+ * @param {number} size
+ * @param {number} [digits=2] - The number of digits to appear after the decimal point
+ * @returns {string[]}
*/
export function numberToHumanSizeSplit(size, digits = 2) {
const abs = Math.abs(size);
if (abs < BYTES_IN_KIB) {
- return [size.toString(), BYTES_FORMAT_BYTES];
+ return [size.toString(), __('B')];
}
if (abs < BYTES_IN_KIB ** 2) {
- return [bytesToKiB(size).toFixed(digits), BYTES_FORMAT_KIB];
+ return [bytesToKiB(size).toFixed(digits), __('KiB')];
}
if (abs < BYTES_IN_KIB ** 3) {
- return [bytesToMiB(size).toFixed(digits), BYTES_FORMAT_MIB];
+ return [bytesToMiB(size).toFixed(digits), __('MiB')];
}
- return [bytesToGiB(size).toFixed(digits), BYTES_FORMAT_GIB];
+ return [bytesToGiB(size).toFixed(digits), __('GiB')];
}
/**
* Port of rails number_to_human_size
* Formats the bytes in number into a more understandable
- * representation (e.g., giving it 1500 yields 1.5 KB).
+ * representation (e.g., giving it 1536 yields 1.5 KiB).
*
- * @param {Number} size
- * @param {Number} digits - The number of digits to appear after the decimal point
- * @returns {String}
+ * @param {number} size
+ * @param {number} [digits=2] - The number of digits to appear after the decimal point
+ * @returns {string}
*/
export function numberToHumanSize(size, digits = 2) {
- const [humanSize, format] = numberToHumanSizeSplit(size, digits);
+ const [humanSize, label] = numberToHumanSizeSplit(size, digits);
- switch (format) {
- case BYTES_FORMAT_BYTES:
+ switch (label) {
+ case __('B'):
return sprintf(__('%{size} B'), { size: humanSize });
- case BYTES_FORMAT_KIB:
+ case __('KiB'):
return sprintf(__('%{size} KiB'), { size: humanSize });
- case BYTES_FORMAT_MIB:
+ case __('MiB'):
return sprintf(__('%{size} MiB'), { size: humanSize });
- case BYTES_FORMAT_GIB:
+ case __('GiB'):
return sprintf(__('%{size} GiB'), { size: humanSize });
default:
return '';
diff --git a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
index 874803a720d..eb56113a4dd 100644
--- a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
+++ b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
@@ -1,6 +1,6 @@
<script>
// eslint-disable-next-line no-restricted-imports
-import { mapActions, mapState, mapGetters } from 'vuex';
+import { mapActions, mapGetters } from 'vuex';
import { s__ } from '~/locale';
import eventHub from '~/super_sidebar/event_hub';
import NavItem from '~/super_sidebar/components/nav_item.vue';
@@ -15,15 +15,11 @@ export default {
NavItem,
},
computed: {
- ...mapState(['navigation', 'urlQuery']),
...mapGetters(['navigationItems']),
},
created() {
eventHub.$emit('toggle-menu-header', false);
-
- if (this.urlQuery?.search) {
- this.fetchSidebarCount();
- }
+ this.fetchSidebarCount();
},
methods: {
...mapActions(['fetchSidebarCount']),
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index 211bbaf82cd..d5e275b8a19 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -1,7 +1,13 @@
import Api from '~/api';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
-import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import {
+ visitUrl,
+ setUrlParams,
+ getBaseURL,
+ queryToObject,
+ objectToQuery,
+} from '~/lib/utils/url_utility';
import { logError } from '~/lib/logger';
import { __ } from '~/locale';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
@@ -135,19 +141,38 @@ export const setLabelFilterSearch = ({ commit }, { value }) => {
commit(types.SET_LABEL_SEARCH_STRING, value);
};
+const injectWildCardSearch = (state, link) => {
+ const urlObject = new URL(`${getBaseURL()}${link}`);
+ if (!state.urlQuery.search) {
+ const queryObject = queryToObject(urlObject.search);
+ urlObject.search = objectToQuery({ ...queryObject, search: '*' });
+ }
+
+ return urlObject.href;
+};
+
export const fetchSidebarCount = ({ commit, state }) => {
- const promises = Object.values(state.navigation).map((navItem) => {
- // active nav item has count already so we skip it
- if (!navItem.active && navItem.count_link) {
- return axios
- .get(navItem.count_link)
- .then(({ data: { count } }) => {
- commit(types.RECEIVE_NAVIGATION_COUNT, { key: navItem.scope, count });
- })
- .catch((e) => logError(e));
- }
- return Promise.resolve();
- });
+ const items = Object.values(state.navigation)
+ .filter((navigationItem) => !navigationItem.active && navigationItem.count_link)
+ .map((navItem) => {
+ const navigationItem = { ...navItem };
+
+ if (navigationItem.count_link) {
+ navigationItem.count_link = injectWildCardSearch(state, navigationItem.count_link);
+ }
+
+ return navigationItem;
+ });
+
+ const promises = items.map((navigationItem) =>
+ axios
+ .get(navigationItem.count_link)
+ .then(({ data: { count } }) => {
+ commit(types.RECEIVE_NAVIGATION_COUNT, { key: navigationItem.scope, count });
+ })
+ .catch((e) => logError(e)),
+ );
+
return Promise.all(promises);
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index a4afdee4d49..1aed3362c42 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -48,7 +48,7 @@ export default {
variables() {
return this.mergeRequestQueryVariables;
},
- update: (data) => data.project.mergeRequest,
+ update: (data) => data.project?.mergeRequest || {},
},
},
components: {
@@ -90,7 +90,7 @@ export default {
return this.state.rebaseInProgress;
},
canPushToSourceBranch() {
- return this.state.userPermissions.pushToSourceBranch;
+ return this.state.userPermissions?.pushToSourceBranch || false;
},
targetBranch() {
return this.state.targetBranch;
diff --git a/app/assets/stylesheets/page_bundles/login.scss b/app/assets/stylesheets/page_bundles/login.scss
index b63f199f7b9..4cc44b01e60 100644
--- a/app/assets/stylesheets/page_bundles/login.scss
+++ b/app/assets/stylesheets/page_bundles/login.scss
@@ -230,11 +230,8 @@
height: 100%;
body {
- padding-top: 48px; // Remove this line when the restyle_login_page feature flag is deleted. Instead, add self-align `center` to container, and maybe a top margin.
-
&.with-system-header {
padding-top: $system-header-height;
- padding-top: calc(#{$system-header-height} + 48px); // Remove this line when the restyle_login_page feature flag is deleted
}
&.with-system-footer {
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 8cdd6efa7c5..65cbe5a78ce 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -26,7 +26,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :cancel_auto_stop]
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? }
- before_action :set_kas_cookie, only: [:index, :edit, :new], if: -> { current_user && request.format.html? }
+ before_action :set_kas_cookie, only: [:index, :folder, :edit, :new], if: -> { current_user && request.format.html? }
after_action :expire_etag_cache, only: [:cancel_auto_stop]
track_event :index, :folder, :show, :new, :edit, :create, :update, :stop, :cancel_auto_stop, :terminal,
diff --git a/app/graphql/mutations/issues/set_assignees.rb b/app/graphql/mutations/issues/set_assignees.rb
index 8413c89b010..1e55cdee0a8 100644
--- a/app/graphql/mutations/issues/set_assignees.rb
+++ b/app/graphql/mutations/issues/set_assignees.rb
@@ -8,7 +8,7 @@ module Mutations
include Assignable
def assign!(issue, users, mode)
- permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue) }
+ permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue.resource_parent) }
super(issue, permitted, mode)
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index d7c3b313f84..3572cfd346b 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -248,6 +248,18 @@ module Types
'if `sast_reports_in_inline_diff` feature flag is disabled.',
resolver: ::Resolvers::CodequalityReportsComparerResolver
+ field :allows_multiple_assignees,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_assignees?,
+ description: 'Allows assigning multiple users to a merge request.',
+ null: false
+
+ field :allows_multiple_reviewers,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_reviewers?,
+ description: 'Allows assigning multiple reviewers to a merge request.',
+ null: false
+
markdown_field :title_html, null: true
markdown_field :description_html, null: true
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 68a55687419..7f49c717c78 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -669,6 +669,18 @@ module Types
description: 'Finds machine learning models',
resolver: Resolvers::Ml::FindModelsResolver
+ field :allows_multiple_merge_request_assignees,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_merge_request_assignees?,
+ description: 'Project allows assigning multiple users to a merge request.',
+ null: false
+
+ field :allows_multiple_merge_request_reviewers,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_merge_request_reviewers?,
+ description: 'Project allows assigning multiple reviewers to a merge request.',
+ null: false
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 6b1e3075968..ac34f429508 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -9,13 +9,14 @@ module EnvironmentsHelper
}
end
- def environments_folder_list_view_data
+ def environments_folder_list_view_data(project, folder)
{
- "endpoint" => folder_project_environments_path(@project, @folder, format: :json),
- "folder_name" => @folder,
- "project_path" => project_path(@project),
+ "endpoint" => folder_project_environments_path(project, folder, format: :json),
+ "folder_name" => folder,
+ "project_path" => project.full_path,
"help_page_path" => help_page_path("ci/environments/index"),
- "can_read_environment" => can?(current_user, :read_environment, @project).to_s
+ "can_read_environment" => can?(current_user, :read_environment, @project).to_s,
+ "kas_tunnel_url" => ::Gitlab::Kas.tunnel_url
}
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 2471c0bdb29..f863c1e5093 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -2098,8 +2098,12 @@ class MergeRequest < ApplicationRecord
true
end
+ def allows_multiple_assignees?
+ project.allows_multiple_merge_request_assignees?
+ end
+
def allows_multiple_reviewers?
- false
+ project.allows_multiple_merge_request_reviewers?
end
def supports_assignee?
diff --git a/app/models/project.rb b/app/models/project.rb
index 738b9b1ef72..3ebdc0a72fc 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3206,6 +3206,16 @@ class Project < ApplicationRecord
end
strong_memoize_attr :code_suggestions_enabled?
+ # Overridden in EE
+ def allows_multiple_merge_request_assignees?
+ false
+ end
+
+ # Overridden in EE
+ def allows_multiple_merge_request_reviewers?
+ false
+ end
+
private
# overridden in EE
@@ -3439,7 +3449,7 @@ class Project < ApplicationRecord
def check_project_export_limit!
return if Gitlab::CurrentSettings.current_application_settings.max_export_size == 0
- if self.statistics.storage_size > Gitlab::CurrentSettings.current_application_settings.max_export_size.megabytes
+ if self.statistics.export_size > Gitlab::CurrentSettings.current_application_settings.max_export_size.megabytes
raise ExportLimitExceeded, _('The project size exceeds the export limit.')
end
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 942f20f6e5e..f89894b77a8 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -145,6 +145,11 @@ class ProjectStatistics < ApplicationRecord
bulk_increment_counter(key, increments)
end
+ # Build artifacts & packages are not included in the project export
+ def export_size
+ storage_size - build_artifacts_size - packages_size
+ end
+
private
def incrementable_attribute?(key)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 27cfaef2db2..7683b0868ab 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -118,9 +118,8 @@ class IssuableBaseService < ::BaseContainerService
return false unless user
ability_name = :"read_#{issuable.to_ability_name}"
- resource = issuable.persisted? ? issuable : project
- can?(user, ability_name, resource)
+ can?(user, ability_name, issuable.resource_parent)
end
def filter_labels
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 03ffef8bc70..2905ba924ca 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -8,7 +8,7 @@
= render "layouts/init_client_detection_flags"
= yield :sessions_broadcast
.gl-h-full.borderless.gl-display-flex.gl-flex-wrap
- .container
+ .container.gl-align-self-center
.content
= render "layouts/flash"
- if custom_text.present?
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 6816a64ac8f..faf45ae78ef 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -7,7 +7,7 @@
= render "layouts/init_client_detection_flags"
= render "layouts/header/empty"
.gl-h-full.gl-display-flex.gl-flex-wrap
- .container
+ .container.gl-align-self-center
.content
= render "layouts/flash"
= yield
diff --git a/app/views/layouts/signup_onboarding.html.haml b/app/views/layouts/signup_onboarding.html.haml
index c8e15896b97..e3e071c2226 100644
--- a/app/views/layouts/signup_onboarding.html.haml
+++ b/app/views/layouts/signup_onboarding.html.haml
@@ -7,7 +7,7 @@
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/logo_with_title"
- .container
+ .container.gl-align-self-center
.content
= yield
diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml
index 2b4d19a0e1d..54855999431 100644
--- a/app/views/projects/environments/folder.html.haml
+++ b/app/views/projects/environments/folder.html.haml
@@ -3,4 +3,4 @@
- page_title _("Environments in %{name}") % { name: @folder }
- add_page_specific_style 'page_bundles/environments'
-#environments-folder-list-view{ data: { environments_data: environments_folder_list_view_data, project_path: @project.full_path } }
+#environments-folder-list-view{ data: environments_folder_list_view_data(@project, @folder) }