diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-08 12:09:41 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-08 12:09:41 +0300 |
commit | 707742e59ca57d1f2ea00d65fa35a7b9a5ded398 (patch) | |
tree | 8dcb287cd941eab2acf6d62de519d1e915686175 /app | |
parent | a0834ebcaa12d126a20e07b6502121e1dc58c9b9 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/diffs/components/diff_file_row.vue | 4 | ||||
-rw-r--r-- | app/assets/javascripts/members/components/filter_sort/filter_sort_container.vue | 16 | ||||
-rw-r--r-- | app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue | 66 | ||||
-rw-r--r-- | app/assets/javascripts/members/constants.js | 53 | ||||
-rw-r--r-- | app/assets/javascripts/members/utils.js | 52 | ||||
-rw-r--r-- | app/controllers/projects/merge_requests_controller.rb | 1 | ||||
-rw-r--r-- | app/helpers/members_helper.rb | 6 | ||||
-rw-r--r-- | app/presenters/alert_management/alert_presenter.rb | 3 | ||||
-rw-r--r-- | app/services/issues/base_service.rb | 16 | ||||
-rw-r--r-- | app/services/issues/create_service.rb | 16 | ||||
-rw-r--r-- | app/services/issues/update_service.rb | 1 | ||||
-rw-r--r-- | app/services/members/invite_service.rb | 16 | ||||
-rw-r--r-- | app/services/packages/create_event_service.rb | 4 |
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 |