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>2020-12-08 12:09:41 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-12-08 12:09:41 +0300
commit707742e59ca57d1f2ea00d65fa35a7b9a5ded398 (patch)
tree8dcb287cd941eab2acf6d62de519d1e915686175 /app
parenta0834ebcaa12d126a20e07b6502121e1dc58c9b9 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_row.vue4
-rw-r--r--app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue16
-rw-r--r--app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue66
-rw-r--r--app/assets/javascripts/members/constants.js53
-rw-r--r--app/assets/javascripts/members/utils.js52
-rw-r--r--app/controllers/projects/merge_requests_controller.rb1
-rw-r--r--app/helpers/members_helper.rb6
-rw-r--r--app/presenters/alert_management/alert_presenter.rb3
-rw-r--r--app/services/issues/base_service.rb16
-rw-r--r--app/services/issues/create_service.rb16
-rw-r--r--app/services/issues/update_service.rb1
-rw-r--r--app/services/members/invite_service.rb16
-rw-r--r--app/services/packages/create_event_service.rb4
13 files changed, 218 insertions, 36 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file_row.vue b/app/assets/javascripts/diffs/components/diff_file_row.vue
index 3888eb781fb..6c5d9170c9e 100644
--- a/app/assets/javascripts/diffs/components/diff_file_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_row.vue
@@ -41,10 +41,6 @@ export default {
return !this.hideFileStats && this.file.type === 'blob';
},
fileClasses() {
- if (!this.glFeatures.highlightCurrentDiffRow) {
- return '';
- }
-
return this.file.type === 'blob' && !this.viewedFiles[this.file.fileHash]
? 'gl-font-weight-bold'
: '';
diff --git a/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue b/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue
index f2acc3215cd..f869ecd392f 100644
--- a/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue
+++ b/app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue
@@ -1,18 +1,26 @@
<script>
import { mapState } from 'vuex';
import MembersFilteredSearchBar from './members_filtered_search_bar.vue';
+import SortDropdown from './sort_dropdown.vue';
export default {
name: 'FilterSortContainer',
- components: { MembersFilteredSearchBar },
+ components: { MembersFilteredSearchBar, SortDropdown },
computed: {
- ...mapState(['filteredSearchBar']),
+ ...mapState(['filteredSearchBar', 'tableSortableFields']),
+ showContainer() {
+ return this.filteredSearchBar.show || this.showSortDropdown;
+ },
+ showSortDropdown() {
+ return this.tableSortableFields.length;
+ },
},
};
</script>
<template>
- <div v-if="filteredSearchBar.show" class="gl-bg-gray-10 gl-p-5">
- <members-filtered-search-bar />
+ <div v-if="showContainer" class="gl-bg-gray-10 gl-p-3 gl-display-md-flex">
+ <members-filtered-search-bar v-if="filteredSearchBar.show" class="gl-p-3 gl-flex-grow-1" />
+ <sort-dropdown v-if="showSortDropdown" class="gl-p-3 gl-flex-shrink-0" />
</div>
</template>
diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
new file mode 100644
index 00000000000..e2fbb074fcd
--- /dev/null
+++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
@@ -0,0 +1,66 @@
+<script>
+import { mapState } from 'vuex';
+import { GlDropdown, GlDropdownItem, GlFormGroup } from '@gitlab/ui';
+import { parseSortParam, buildSortUrl } from '~/members/utils';
+import { FIELDS } from '~/members/constants';
+
+export default {
+ name: 'SortDropdown',
+ components: { GlDropdown, GlDropdownItem, GlFormGroup },
+ computed: {
+ ...mapState(['tableSortableFields', 'filteredSearchBar']),
+ sort() {
+ return parseSortParam(this.tableSortableFields);
+ },
+ filteredOptions() {
+ const buildOption = (field, sortDesc) => ({
+ ...(sortDesc ? field.sort.desc : field.sort.asc),
+ key: field.key,
+ sortDesc,
+ url: buildSortUrl({
+ sortBy: field.key,
+ sortDesc,
+ filteredSearchBarTokens: this.filteredSearchBar.tokens,
+ filteredSearchBarSearchParam: this.filteredSearchBar.searchParam,
+ }),
+ });
+
+ return FIELDS.filter(
+ field => this.tableSortableFields.includes(field.key) && field.sort,
+ ).flatMap(field => [buildOption(field, false), buildOption(field, true)]);
+ },
+ },
+ methods: {
+ isChecked(key, sortDesc) {
+ return this.sort?.sortBy === key && this.sort?.sortDesc === sortDesc;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group
+ :label="__('Sort by')"
+ class="gl-mb-0"
+ label-cols="auto"
+ label-class="gl-align-self-center gl-pb-0!"
+ >
+ <gl-dropdown
+ :text="sort.sortByLabel"
+ block
+ toggle-class="gl-mb-0"
+ data-testid="members-sort-dropdown"
+ right
+ >
+ <gl-dropdown-item
+ v-for="option in filteredOptions"
+ :key="option.param"
+ :href="option.url"
+ is-check-item
+ :is-checked="isChecked(option.key, option.sortDesc)"
+ >
+ {{ option.label }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index a23e9b942ef..874e934e5b0 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -1,9 +1,21 @@
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+
+const ACCOUNT_SORT_ASC_LABEL = s__('Members|Account, ascending');
export const FIELDS = [
{
key: 'account',
label: __('Account'),
+ sort: {
+ asc: {
+ param: 'name_asc',
+ label: ACCOUNT_SORT_ASC_LABEL,
+ },
+ desc: {
+ param: 'name_desc',
+ label: s__('Members|Account, descending'),
+ },
+ },
},
{
key: 'source',
@@ -16,6 +28,16 @@ export const FIELDS = [
label: __('Access granted'),
thClass: 'col-meta',
tdClass: 'col-meta',
+ sort: {
+ asc: {
+ param: 'last_joined',
+ label: s__('Members|Access granted, ascending'),
+ },
+ desc: {
+ param: 'oldest_joined',
+ label: s__('Members|Access granted, descending'),
+ },
+ },
},
{
key: 'invited',
@@ -40,6 +62,16 @@ export const FIELDS = [
label: __('Max role'),
thClass: 'col-max-role',
tdClass: 'col-max-role',
+ sort: {
+ asc: {
+ param: 'access_level_asc',
+ label: s__('Members|Max role, ascending'),
+ },
+ desc: {
+ param: 'access_level_desc',
+ label: s__('Members|Max role, descending'),
+ },
+ },
},
{
key: 'expiration',
@@ -48,6 +80,19 @@ export const FIELDS = [
tdClass: 'col-expiration',
},
{
+ key: 'lastSignIn',
+ sort: {
+ asc: {
+ param: 'recent_sign_in',
+ label: s__('Members|Last sign-in, ascending'),
+ },
+ desc: {
+ param: 'oldest_sign_in',
+ label: s__('Members|Last sign-in, descending'),
+ },
+ },
+ },
+ {
key: 'actions',
thClass: 'col-actions',
tdClass: 'col-actions',
@@ -55,6 +100,12 @@ export const FIELDS = [
},
];
+export const DEFAULT_SORT = {
+ sortBy: 'account',
+ sortDesc: false,
+ sortByLabel: ACCOUNT_SORT_ASC_LABEL,
+};
+
export const AVATAR_SIZE = 48;
export const MEMBER_TYPES = {
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
index 4229a62c0a7..5c58c4a9f6c 100644
--- a/app/assets/javascripts/members/utils.js
+++ b/app/assets/javascripts/members/utils.js
@@ -1,4 +1,7 @@
import { __ } from '~/locale';
+import { getParameterByName } from '~/lib/utils/common_utils';
+import { setUrlParams } from '~/lib/utils/url_utility';
+import { FIELDS, DEFAULT_SORT } from './constants';
export const generateBadges = (member, isCurrentUser) => [
{
@@ -44,5 +47,54 @@ export const canUpdate = (member, currentUserId, sourceId) => {
);
};
+export const parseSortParam = sortableFields => {
+ const sortParam = getParameterByName('sort');
+
+ const sortedField = FIELDS.filter(field => sortableFields.includes(field.key)).find(
+ field => field.sort?.asc?.param === sortParam || field.sort?.desc?.param === sortParam,
+ );
+
+ if (!sortedField) {
+ return DEFAULT_SORT;
+ }
+
+ const isDesc = sortedField?.sort?.desc?.param === sortParam;
+
+ return {
+ sortBy: sortedField.key,
+ sortDesc: isDesc,
+ sortByLabel: isDesc ? sortedField?.sort?.desc?.label : sortedField?.sort?.asc?.label,
+ };
+};
+
+export const buildSortUrl = ({
+ sortBy,
+ sortDesc,
+ filteredSearchBarTokens,
+ filteredSearchBarSearchParam,
+}) => {
+ const sortDefinition = FIELDS.find(field => field.key === sortBy)?.sort;
+
+ if (!sortDefinition) {
+ return '';
+ }
+
+ const sortParam = sortDesc ? sortDefinition.desc.param : sortDefinition.asc.param;
+
+ const filterParams =
+ filteredSearchBarTokens?.reduce((accumulator, token) => {
+ return {
+ ...accumulator,
+ [token]: getParameterByName(token),
+ };
+ }, {}) || {};
+
+ if (filteredSearchBarSearchParam) {
+ filterParams[filteredSearchBarSearchParam] = getParameterByName(filteredSearchBarSearchParam);
+ }
+
+ return setUrlParams({ ...filterParams, sort: sortParam }, window.location.href, true);
+};
+
// Defined in `ee/app/assets/javascripts/vue_shared/components/members/utils.js`
export const canOverride = () => false;
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 69aec044863..838b1925f34 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -36,7 +36,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:hide_jump_to_next_unresolved_in_threads, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project)
push_frontend_feature_flag(:unified_diff_components, @project)
- push_frontend_feature_flag(:highlight_current_diff_row, @project)
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
push_frontend_feature_flag(:core_security_mr_widget_counts, @project)
diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb
index d66f67fbb60..5dc636ad996 100644
--- a/app/helpers/members_helper.rb
+++ b/app/helpers/members_helper.rb
@@ -6,14 +6,14 @@ module MembersHelper
text = 'Are you sure you want to'
action =
- if member.request?
+ if member.invite?
+ "revoke the invitation for #{member.invite_email} to join"
+ elsif member.request?
if member.user == user
'withdraw your access request for'
else
"deny #{member.user.name}'s request to join"
end
- elsif member.invite?
- "revoke the invitation for #{member.invite_email} to join"
else
if member.user
"remove #{member.user.name} from"
diff --git a/app/presenters/alert_management/alert_presenter.rb b/app/presenters/alert_management/alert_presenter.rb
index 4bfa3dc9a13..1cebf5c561a 100644
--- a/app/presenters/alert_management/alert_presenter.rb
+++ b/app/presenters/alert_management/alert_presenter.rb
@@ -8,7 +8,6 @@ module AlertManagement
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"
- INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title]
delegate :metrics_dashboard_url, :runbook, to: :parsed_payload
@@ -48,7 +47,7 @@ module AlertManagement
end
def incident_issues_link
- project_issues_url(project, label_name: INCIDENT_LABEL_NAME)
+ project_incidents_url(project)
end
def performance_dashboard_link
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 978ea6fe9bc..25f319da03b 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -73,22 +73,6 @@ module Issues
Milestones::IssuesCountService.new(milestone).delete_cache
end
-
- # Applies label "incident" (creates it if missing) to incident issues.
- # Please use in "after" hooks only to ensure we are not appyling
- # labels prematurely.
- def add_incident_label(issue)
- return unless issue.incident?
-
- label = ::IncidentManagement::CreateIncidentLabelService
- .new(project, current_user)
- .execute
- .payload[:label]
-
- return if issue.label_ids.include?(label.id)
-
- issue.labels << label
- end
end
end
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index fb7683f940d..44de8eb6389 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -49,6 +49,22 @@ module Issues
def user_agent_detail_service
UserAgentDetailService.new(@issue, @request)
end
+
+ # Applies label "incident" (creates it if missing) to incident issues.
+ # For use in "after" hooks only to ensure we are not appyling
+ # labels prematurely.
+ def add_incident_label(issue)
+ return unless issue.incident?
+
+ label = ::IncidentManagement::CreateIncidentLabelService
+ .new(project, current_user)
+ .execute
+ .payload[:label]
+
+ return if issue.label_ids.include?(label.id)
+
+ issue.labels << label
+ end
end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 4f40ff5f535..127ed04cf51 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -34,7 +34,6 @@ module Issues
end
def after_update(issue)
- add_incident_label(issue)
IssuesChannel.broadcast_to(issue, event: 'updated') if Gitlab::ActionCable::Config.in_app? || Feature.enabled?(:broadcast_issue_updates, issue.project)
end
diff --git a/app/services/members/invite_service.rb b/app/services/members/invite_service.rb
index cfab5c3ef9d..60ebbaface2 100644
--- a/app/services/members/invite_service.rb
+++ b/app/services/members/invite_service.rb
@@ -20,8 +20,8 @@ module Members
emails.each do |email|
next if existing_member?(source, email)
-
next if existing_invite?(source, email)
+ next if existing_request?(source, email)
if existing_user?(email)
add_existing_user_as_member(current_user, source, params, email)
@@ -44,8 +44,7 @@ module Members
access_level: params[:access_level],
invite_email: email,
created_by_id: current_user.id,
- expires_at: params[:expires_at],
- requested_at: Time.current.utc)
+ expires_at: params[:expires_at])
unless new_member.valid? && new_member.persisted?
errors[params[:email]] = new_member.errors.full_messages.to_sentence
@@ -92,6 +91,17 @@ module Members
false
end
+ def existing_request?(source, email)
+ existing_request = source.requesters.with_user_by_email(email).exists?
+
+ if existing_request
+ errors[email] = "Member cannot be invited because they already requested to join #{source.name}"
+ return true
+ end
+
+ false
+ end
+
def existing_user(email)
User.find_by_email(email)
end
diff --git a/app/services/packages/create_event_service.rb b/app/services/packages/create_event_service.rb
index 74d57f2ad98..f0328ceb08a 100644
--- a/app/services/packages/create_event_service.rb
+++ b/app/services/packages/create_event_service.rb
@@ -4,7 +4,9 @@ module Packages
class CreateEventService < BaseService
def execute
if Feature.enabled?(:collect_package_events_redis) && redis_event_name
- unless guest?
+ if guest?
+ ::Gitlab::UsageDataCounters::GuestPackageEventCounter.count(redis_event_name)
+ else
::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(current_user.id, redis_event_name)
end
end