diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-21 03:11:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-01-21 03:11:07 +0300 |
commit | 9f021745a60369298705393103f621ff73a2b286 (patch) | |
tree | a6e9f85a8f35245e4ccef5f532c6ca6857f08d9e /app | |
parent | ef8c47e97e1c178291e4857314a3f53875d75062 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
25 files changed, 321 insertions, 59 deletions
diff --git a/app/assets/javascripts/design_management/components/list/item.vue b/app/assets/javascripts/design_management/components/list/item.vue index 5402fed0a9a..67110265b5f 100644 --- a/app/assets/javascripts/design_management/components/list/item.vue +++ b/app/assets/javascripts/design_management/components/list/item.vue @@ -133,7 +133,7 @@ export default { <div class="card-body gl-p-0 gl-display-flex gl-align-items-center gl-justify-content-center gl-overflow-hidden gl-relative" > - <div v-if="icon.name" data-testid="design-event" class="design-event gl-absolute"> + <div v-if="icon.name" data-testid="design-event" class="gl-top-5 gl-right-5 gl-absolute"> <span :title="icon.tooltip" :aria-label="icon.tooltip"> <gl-icon :name="icon.name" diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js index 2c51ce0d970..1a3c15bedb8 100644 --- a/app/assets/javascripts/notes/stores/mutations.js +++ b/app/assets/javascripts/notes/stores/mutations.js @@ -1,3 +1,4 @@ +import { isEqual } from 'lodash'; import * as utils from './utils'; import * as types from './mutation_types'; import * as constants from '../constants'; @@ -31,7 +32,8 @@ export default { } } - note.base_discussion = undefined; // No point keeping a reference to this + // note.base_discussion = undefined; // No point keeping a reference to this + delete note.base_discussion; discussion.notes = [note]; state.discussions.push(discussion); @@ -220,6 +222,11 @@ export default { [types.UPDATE_NOTE](state, note) { const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id); + // Disable eslint here so we can delete the property that we no longer need + // in the note object + // eslint-disable-next-line no-param-reassign + delete note.base_discussion; + if (noteObj.individual_note) { if (note.type === constants.DISCUSSION_NOTE) { noteObj.individual_note = false; @@ -228,7 +235,10 @@ export default { noteObj.notes.splice(0, 1, note); } else { const comment = utils.findNoteObjectById(noteObj.notes, note.id); - noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note); + + if (!isEqual(comment, note)) { + noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note); + } } }, diff --git a/app/assets/javascripts/search/topbar/components/project_filter.vue b/app/assets/javascripts/search/topbar/components/project_filter.vue index 3f1f3848ac7..effaafb72c1 100644 --- a/app/assets/javascripts/search/topbar/components/project_filter.vue +++ b/app/assets/javascripts/search/topbar/components/project_filter.vue @@ -27,7 +27,7 @@ export default { handleProjectChange(project) { // This determines if we need to update the group filter or not const queryParams = { - ...(project.namespace_id && { [GROUP_DATA.queryParam]: project.namespace_id }), + ...(project.namespace?.id && { [GROUP_DATA.queryParam]: project.namespace.id }), [PROJECT_DATA.queryParam]: project.id, }; diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index 5ee51764555..94a216596a8 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -64,6 +64,11 @@ export default { mounted() { this.renderSuggestions(); }, + beforeDestroy() { + if (this.suggestionsWatch) { + this.suggestionsWatch(); + } + }, methods: { renderSuggestions() { // swaps out suggestion(s) markdown with rich diff components @@ -108,6 +113,13 @@ export default { }, }); + // We're using `$watch` as `suggestionsCount` updates do not + // propagate to this component for some unknown reason while + // using a traditional prop watcher. + this.suggestionsWatch = this.$watch('suggestionsCount', () => { + suggestionDiff.suggestionsCount = this.suggestionsCount; + }); + suggestionDiff.$on('apply', ({ suggestionId, callback, message }) => { this.$emit('apply', { suggestionId, callback, flashContainer: this.$el, message }); }); diff --git a/app/assets/stylesheets/components/design_management/design_list_item.scss b/app/assets/stylesheets/components/design_management/design_list_item.scss index b7f6b2026fe..09af4da37e9 100644 --- a/app/assets/stylesheets/components/design_management/design_list_item.scss +++ b/app/assets/stylesheets/components/design_management/design_list_item.scss @@ -8,11 +8,6 @@ top: 10px; } - .design-event { - top: $gl-padding; - right: $gl-padding; - } - .card-body { height: 230px; } diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index baebedb8e5d..a3ea39d9c3d 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -34,10 +34,6 @@ module IntegrationsActions end end - def custom_integration_projects - Project.with_custom_integration_compared_to(integration).page(params[:page]).per(20) - end - def test render json: {}, status: :ok end diff --git a/app/finders/terraform/states_finder.rb b/app/finders/terraform/states_finder.rb new file mode 100644 index 00000000000..bbe90fead2b --- /dev/null +++ b/app/finders/terraform/states_finder.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Terraform + class StatesFinder + def initialize(project, current_user, params: {}) + @project = project + @current_user = current_user + @params = params + end + + def execute + return ::Terraform::State.none unless can_read_terraform_states? + + states = project.terraform_states + states = states.with_name(params[:name]) if params[:name].present? + + states.ordered_by_name + end + + private + + attr_reader :project, :current_user, :params + + def can_read_terraform_states? + current_user.can?(:read_terraform_state, project) + end + end +end diff --git a/app/graphql/mutations/security/ci_configuration/configure_sast.rb b/app/graphql/mutations/security/ci_configuration/configure_sast.rb new file mode 100644 index 00000000000..6cb3704a19b --- /dev/null +++ b/app/graphql/mutations/security/ci_configuration/configure_sast.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Mutations + module Security + module CiConfiguration + class ConfigureSast < BaseMutation + include ResolvesProject + + graphql_name 'ConfigureSast' + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: 'Full path of the project.' + + argument :configuration, ::Types::CiConfiguration::Sast::InputType, + required: true, + description: 'SAST CI configuration for the project.' + + field :status, GraphQL::STRING_TYPE, null: false, + description: 'Status of creating the commit for the supplied SAST CI configuration.' + + field :success_path, GraphQL::STRING_TYPE, null: true, + description: 'Redirect path to use when the response is successful.' + + authorize :push_code + + def resolve(project_path:, configuration:) + project = authorized_find!(full_path: project_path) + + result = ::Security::CiConfiguration::SastCreateService.new(project, current_user, configuration).execute + prepare_response(result) + end + + private + + def find_object(full_path:) + resolve_project(full_path: full_path) + end + + def prepare_response(result) + { + status: result[:status], + success_path: result[:success_path], + errors: Array(result[:errors]) + } + end + end + end + end +end diff --git a/app/graphql/resolvers/terraform/states_resolver.rb b/app/graphql/resolvers/terraform/states_resolver.rb index 38b26a948b1..f543eb182e8 100644 --- a/app/graphql/resolvers/terraform/states_resolver.rb +++ b/app/graphql/resolvers/terraform/states_resolver.rb @@ -3,20 +3,20 @@ module Resolvers module Terraform class StatesResolver < BaseResolver - type Types::Terraform::StateType, null: true + type Types::Terraform::StateType.connection_type, null: true alias_method :project, :object - def resolve(**args) - return ::Terraform::State.none unless can_read_terraform_states? - - project.terraform_states.ordered_by_name + when_single do + argument :name, GraphQL::STRING_TYPE, + required: true, + description: 'Name of the Terraform state.' end - private - - def can_read_terraform_states? - current_user.can?(:read_terraform_state, project) + def resolve(**args) + ::Terraform::StatesFinder + .new(project, current_user, params: args) + .execute end end end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index f9dd11cbe37..11980e009f4 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -15,6 +15,7 @@ module Types mount_mutation Mutations::AlertManagement::HttpIntegration::Update mount_mutation Mutations::AlertManagement::HttpIntegration::ResetToken mount_mutation Mutations::AlertManagement::HttpIntegration::Destroy + mount_mutation Mutations::Security::CiConfiguration::ConfigureSast mount_mutation Mutations::AlertManagement::PrometheusIntegration::Create mount_mutation Mutations::AlertManagement::PrometheusIntegration::Update mount_mutation Mutations::AlertManagement::PrometheusIntegration::ResetToken diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 41a4edd20a5..257c77e2ede 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -309,10 +309,16 @@ module Types description: 'Title of the label' end + field :terraform_state, + Types::Terraform::StateType, + null: true, + description: 'Find a single Terraform state by name.', + resolver: Resolvers::Terraform::StatesResolver.single + field :terraform_states, Types::Terraform::StateType.connection_type, null: true, - description: 'Terraform states associated with the project', + description: 'Terraform states associated with the project.', resolver: Resolvers::Terraform::StatesResolver field :pipeline_analytics, Types::Ci::AnalyticsType, null: true, diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 133d9d21a14..eeeffb7b3ae 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -62,6 +62,14 @@ module GroupsHelper can?(current_user, :set_emails_disabled, group) && !group.parent&.emails_disabled? end + def group_open_issues_count(group) + if Feature.enabled?(:cached_sidebar_open_issues_count, group) + cached_open_group_issues_count(group) + else + number_with_delimiter(group_issues_count(state: 'opened')) + end + end + def group_issues_count(state:) IssuesFinder .new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true) @@ -69,6 +77,21 @@ module GroupsHelper .count end + def cached_open_group_issues_count(group) + count_service = Groups::OpenIssuesCountService + issues_count = count_service.new(group, current_user).count + + if issues_count > count_service::CACHED_COUNT_THRESHOLD + ActiveSupport::NumberHelper + .number_to_human( + issues_count, + units: { thousand: 'k', million: 'm' }, precision: 1, significant: false, format: '%n%u' + ) + else + number_with_delimiter(issues_count) + end + end + def group_merge_requests_count(state:) MergeRequestsFinder .new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true) diff --git a/app/models/terraform/state.rb b/app/models/terraform/state.rb index efbbd86ae4a..a301c4a9689 100644 --- a/app/models/terraform/state.rb +++ b/app/models/terraform/state.rb @@ -22,6 +22,7 @@ module Terraform scope :versioning_not_enabled, -> { where(versioning_enabled: false) } scope :ordered_by_name, -> { order(:name) } + scope :with_name, -> (name) { where(name: name) } validates :project_id, presence: true validates :uuid, presence: true, uniqueness: true, length: { is: UUID_LENGTH }, diff --git a/app/services/groups/open_issues_count_service.rb b/app/services/groups/open_issues_count_service.rb new file mode 100644 index 00000000000..db1ca09212a --- /dev/null +++ b/app/services/groups/open_issues_count_service.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module Groups + # Service class for counting and caching the number of open issues of a group. + class OpenIssuesCountService < BaseCountService + include Gitlab::Utils::StrongMemoize + + VERSION = 1 + PUBLIC_COUNT_KEY = 'group_public_open_issues_count' + TOTAL_COUNT_KEY = 'group_total_open_issues_count' + CACHED_COUNT_THRESHOLD = 1000 + EXPIRATION_TIME = 24.hours + + attr_reader :group, :user + + def initialize(group, user = nil) + @group = group + @user = user + end + + # Reads count value from cache and return it if present. + # If empty or expired, #uncached_count will calculate the issues count for the group and + # compare it with the threshold. If it is greater, it will be written to the cache and returned. + # If below, it will be returned without being cached. + # This results in only caching large counts and calculating the rest with every call to maintain + # accuracy. + def count + cached_count = Rails.cache.read(cache_key) + return cached_count unless cached_count.blank? + + refreshed_count = uncached_count + update_cache_for_key(cache_key) { refreshed_count } if refreshed_count > CACHED_COUNT_THRESHOLD + refreshed_count + end + + def cache_key(key = nil) + ['groups', 'open_issues_count_service', VERSION, group.id, cache_key_name] + end + + private + + def cache_options + super.merge({ expires_in: EXPIRATION_TIME }) + end + + def cache_key_name + public_only? ? PUBLIC_COUNT_KEY : TOTAL_COUNT_KEY + end + + def public_only? + !user_is_at_least_reporter? + end + + def user_is_at_least_reporter? + strong_memoize(:user_is_at_least_reporter) do + group.member?(user, Gitlab::Access::REPORTER) + end + end + + def relation_for_count + IssuesFinder.new(user, group_id: group.id, state: 'opened', non_archived: true, include_subgroups: true, public_only: public_only?).execute + end + end +end diff --git a/app/services/security/ci_configuration/sast_create_service.rb b/app/services/security/ci_configuration/sast_create_service.rb new file mode 100644 index 00000000000..8fc3b8d078c --- /dev/null +++ b/app/services/security/ci_configuration/sast_create_service.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Security + module CiConfiguration + class SastCreateService < ::BaseService + def initialize(project, current_user, params) + @project = project + @current_user = current_user + @params = params + @branch_name = @project.repository.next_branch('set-sast-config') + end + + def execute + attributes_for_commit = attributes + result = ::Files::MultiService.new(@project, @current_user, attributes_for_commit).execute + + if result[:status] == :success + result[:success_path] = successful_change_path + track_event(attributes_for_commit) + else + result[:errors] = result[:message] + end + + result + + rescue Gitlab::Git::PreReceiveError => e + { status: :error, errors: e.message } + end + + private + + def attributes + actions = Security::CiConfiguration::SastBuildActions.new(@project.auto_devops_enabled?, @params, existing_gitlab_ci_content).generate + + @project.repository.add_branch(@current_user, @branch_name, @project.default_branch) + message = _('Set .gitlab-ci.yml to enable or configure SAST') + + { + commit_message: message, + branch_name: @branch_name, + start_branch: @branch_name, + actions: actions + } + end + + def existing_gitlab_ci_content + gitlab_ci_yml = @project.repository.gitlab_ci_yml_for(@project.repository.root_ref_sha) + YAML.safe_load(gitlab_ci_yml) if gitlab_ci_yml + end + + def successful_change_path + description = _('Set .gitlab-ci.yml to enable or configure SAST security scanning using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings) to customize SAST settings.') + merge_request_params = { source_branch: @branch_name, description: description } + Gitlab::Routing.url_helpers.project_new_merge_request_url(@project, merge_request: merge_request_params) + end + + def track_event(attributes_for_commit) + action = attributes_for_commit[:actions].first + + Gitlab::Tracking.event( + self.class.to_s, action[:action], label: action[:default_values_overwritten].to_s + ) + end + end + end +end diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml index c95e7c16161..83d2e13d345 100644 --- a/app/views/groups/_import_group_from_another_instance_panel.html.haml +++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml @@ -2,9 +2,18 @@ = form_errors(@group) .gl-border-l-solid.gl-border-r-solid.gl-border-gray-100.gl-border-1.gl-p-5 - %h4 + %h4.gl-display-flex = s_('GroupsNew|Import groups from another instance of GitLab') - %p + %span.badge.badge-info.badge-pill.gl-badge.md.gl-ml-3 + = _('Beta') + .gl-alert.gl-alert-warning{ role: 'alert' } + = sprite_icon('warning', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title') + .gl-alert-body + - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/group/import/index.md') } + - feedback_link_start = '<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/284495" target="_blank" rel="noopener noreferrer">'.html_safe + - link_end = '</a>'.html_safe + = s_('GroupsNew|Not all related objects are migrated, as %{docs_link_start}described here%{docs_link_end}. Please %{feedback_link_start}leave feedback%{feedback_link_end} on this feature.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end, feedback_link_start: feedback_link_start, feedback_link_end: link_end } + %p.gl-mt-3 = s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.') .form-group.gl-display-flex.gl-flex-direction-column = f.label :bulk_import_gitlab_url, s_('GroupsNew|GitLab source URL'), for: 'import_gitlab_url' diff --git a/app/views/groups/runners/_runner.html.haml b/app/views/groups/runners/_runner.html.haml index 3fc50cc86d2..80739395713 100644 --- a/app/views/groups/runners/_runner.html.haml +++ b/app/views/groups/runners/_runner.html.haml @@ -77,8 +77,9 @@ = link_to resume_group_runner_path(@group, runner), method: :post, class: 'btn btn-default has-tooltip', title: _('Resume'), ref: 'tooltip', aria: { label: _('Resume') }, data: { placement: 'top', container: 'body'} do = sprite_icon('play') - if runner.belongs_to_more_than_one_project? - .btn-group - .btn.btn-danger.has-tooltip{ 'aria-label' => 'Remove', 'data-container' => 'body', 'data-original-title' => _('Multi-project Runners cannot be removed'), 'data-placement' => 'top', disabled: 'disabled' } + - delete_runner_tooltip = _('Multi-project Runners cannot be removed') + .btn-group.has-tooltip{ data: { container: 'body', placement: 'top' }, title: delete_runner_tooltip } + .btn.btn-danger{ 'aria-label' => delete_runner_tooltip, disabled: 'disabled' } = sprite_icon('close') - else .btn-group diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 5569a69222f..52d3c8d133a 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -1,4 +1,4 @@ -- issues_count = group_issues_count(state: 'opened') +- issues_count = group_open_issues_count(@group) - merge_requests_count = group_merge_requests_count(state: 'opened') .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **tracking_attrs('groups_side_navigation', 'render', 'groups_side_navigation') } @@ -54,14 +54,14 @@ = sprite_icon('issues') %span.nav-item-name = _('Issues') - %span.badge.badge-pill.count= number_with_delimiter(issues_count) + %span.badge.badge-pill.count= issues_count %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_issues_sidebar_submenu'} } = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index', 'iterations#index'], html_options: { class: "fly-out-top-item" } ) do = link_to issues_group_path(@group) do %strong.fly-out-top-item-name = _('Issues') - %span.badge.badge-pill.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count) + %span.badge.badge-pill.count.issue_counter.fly-out-badge= issues_count %li.divider.fly-out-top-item = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index dc4172e2f09..9ea4d3df631 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -50,10 +50,10 @@ - if can?(current_user, :push_code, @project) - if branch.name == @project.repository.root_ref - %button{ class: "gl-button btn btn-danger remove-row has-tooltip disabled", - disabled: true, - title: s_('Branches|The default branch cannot be deleted') } - = sprite_icon("remove") + - delete_default_branch_tooltip = s_('Branches|The default branch cannot be deleted') + %span.has-tooltip{ title: delete_default_branch_tooltip } + %button{ class: "gl-button btn btn-danger remove-row disabled", disabled: true, 'aria-label' => delete_default_branch_tooltip } + = sprite_icon("remove") - elsif protected_branch?(@project, branch) - if can?(current_user, :push_to_delete_protected_branch, @project) %button{ class: "gl-button btn btn-danger remove-row has-tooltip", @@ -65,10 +65,10 @@ is_merged: ("true" if merged) } } = sprite_icon("remove") - else - %button{ class: "gl-button btn btn-danger remove-row has-tooltip disabled", - disabled: true, - title: s_('Branches|Only a project maintainer or owner can delete a protected branch') } - = sprite_icon("remove") + - delete_protected_branch_tooltip = s_('Branches|Only a project maintainer or owner can delete a protected branch') + %span.has-tooltip{ title: delete_protected_branch_tooltip } + %button{ class: "gl-button btn btn-danger remove-row disabled", disabled: true, 'aria-label' => delete_protected_branch_tooltip } + = sprite_icon("remove") - else = link_to project_branch_path(@project, branch.name), class: "gl-button btn btn-danger remove-row qa-remove-btn js-ajax-loading-spinner has-tooltip", diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index dbe0bf35b98..fad168da71e 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -7,11 +7,11 @@ %span= s_('ProjectOverview|Fork') - else - can_create_fork = current_user.can?(:create_fork) - = link_to new_project_fork_path(@project), - class: "btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center fork-btn #{'has-tooltip disabled' unless can_create_fork}", - title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do - = sprite_icon('fork', css_class: 'icon') - %span= s_('ProjectOverview|Fork') + - disabled_fork_tooltip = s_('ProjectOverview|You have reached your project limit') + %span.has-tooltip{ title: (disabled_fork_tooltip unless can_create_fork) } + = link_to new_project_fork_path(@project), class: "btn btn-default btn-xs count-badge-button d-flex align-items-center fork-btn #{' disabled' unless can_create_fork }", 'aria-label' => (disabled_fork_tooltip unless can_create_fork) do + = sprite_icon('fork', css_class: 'icon') + %span= s_('ProjectOverview|Fork') %span.fork-count.count-badge-count.d-flex.align-items-center = link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Fork'), s_('ProjectOverview|Forks'), @project.forks_count), class: 'count' do = @project.forks_count diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml index a785e36fad5..d11b61466e2 100644 --- a/app/views/projects/notes/_actions.html.haml +++ b/app/views/projects/notes/_actions.html.haml @@ -39,15 +39,15 @@ - if can?(current_user, :award_emoji, note) - if note.emoji_awardable? .note-actions-item - = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip btn btn-transparent", data: { position: 'right', container: 'body' } do - %span{ class: 'link-highlight award-control-icon-neutral' }= sprite_icon('slight-smile') - %span{ class: 'link-highlight award-control-icon-positive' }= sprite_icon('smiley') - %span{ class: 'link-highlight award-control-icon-super-positive' }= sprite_icon('smile') + = button_tag title: 'Add reaction', class: "note-action-button note-emoji-button js-add-award js-note-emoji has-tooltip btn gl-button btn-icon btn-default-tertiary btn-transparent", data: { position: 'right', container: 'body' } do + = sprite_icon('slight-smile', css_class: 'link-highlight award-control-icon-neutral gl-button-icon gl-icon gl-text-gray-400') + = sprite_icon('smiley', css_class: 'link-highlight award-control-icon-positive gl-button-icon gl-icon gl-left-3!') + = sprite_icon('smile', css_class: 'link-highlight award-control-icon-super-positive gl-button-icon gl-icon gl-left-3! ') - if note_editable - .note-actions-item - = button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn btn-transparent', data: { container: 'body', qa_selector: 'edit_comment_button' } do + .note-actions-item.gl-ml-0 + = button_tag title: 'Edit comment', class: 'note-action-button js-note-edit has-tooltip btn gl-button btn-default-tertiary btn-transparent gl-px-2!', data: { container: 'body', qa_selector: 'edit_comment_button' } do %span.link-highlight - = custom_icon('icon_pencil') + = sprite_icon('pencil', css_class: 'gl-button-icon gl-icon gl-text-gray-400 s16') = render 'projects/notes/more_actions_dropdown', note: note, note_editable: note_editable diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml index 8cf1b6b9294..c81a3683e90 100644 --- a/app/views/projects/notes/_more_actions_dropdown.html.haml +++ b/app/views/projects/notes/_more_actions_dropdown.html.haml @@ -1,10 +1,9 @@ - is_current_user = current_user == note.author - if note_editable || !is_current_user - .dropdown.more-actions.note-actions-item - = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body', qa_selector: 'more_actions_dropdown' } do - %span.icon - = custom_icon('ellipsis_v') + %div{ class: "dropdown more-actions note-actions-item gl-ml-0!" } + = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn gl-button btn-default-tertiary btn-transparent gl-pl-2! gl-pr-0!', data: { toggle: 'dropdown', container: 'body', qa_selector: 'more_actions_dropdown' } do + = sprite_icon('ellipsis_v', css_class: 'gl-button-icon gl-icon gl-text-gray-400') %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left %li = clipboard_button(text: noteable_note_url(note), title: _('Copy reference'), button_text: _('Copy link'), class: 'btn-clipboard', hide_tooltip: true, hide_button_icon: true) diff --git a/app/views/projects/pages/_use.html.haml b/app/views/projects/pages/_use.html.haml index e9ace8c72f1..ec3fc27dc20 100644 --- a/app/views/projects/pages/_use.html.haml +++ b/app/views/projects/pages/_use.html.haml @@ -4,7 +4,8 @@ = s_('GitLabPages|Configure pages') .card-body %p.gl-mb-0 - - link_start = "<a href='#{help_page_path('user/project/pages/index.md')}' target='_blank' rel='noopener noreferrer'>".html_safe + - docs_link_start = "<a href='#{help_page_path('user/project/pages/index.md')}' target='_blank' rel='noopener noreferrer'>".html_safe + - samples_link_start = "<a href='https://gitlab.com/pages' target='_blank' rel='noopener noreferrer'>".html_safe + - templates_link_start = "<a href='https://gitlab.com/gitlab-org/project-templates' target='_blank' rel='noopener noreferrer'>".html_safe - link_end = '</a>'.html_safe - = s_('GitLabPages|Learn how to upload your static site and have it served by GitLab by following the %{link_start}documentation on GitLab Pages%{link_end}.').html_safe % { link_start: link_start, - link_end: link_end } + = s_('GitLabPages|See the %{docs_link_start}GitLab Pages documentation%{link_end} to learn how to upload your static site and have GitLab serve it. You can also follow a %{samples_link_start}sample project%{link_end} or use a %{templates_link_start}GitLab CI template%{link_end}.').html_safe % { docs_link_start: docs_link_start, samples_link_start: samples_link_start, templates_link_start: templates_link_start, link_end: link_end } diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml index f206a2152c2..089643f4748 100644 --- a/app/views/shared/access_tokens/_form.html.haml +++ b/app/views/shared/access_tokens/_form.html.haml @@ -30,4 +30,4 @@ = render 'shared/tokens/scopes_form', prefix: prefix, token: token, scopes: scopes .gl-mt-3 - = f.submit _('Create %{type}') % { type: type }, class: 'btn btn-success', data: { qa_selector: 'create_token_button' } + = f.submit _('Create %{type}') % { type: type }, class: 'gl-button btn btn-success', data: { qa_selector: 'create_token_button' } diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml index 50daa400e6c..d7c74255578 100644 --- a/app/views/shared/access_tokens/_table.html.haml +++ b/app/views/shared/access_tokens/_table.html.haml @@ -43,7 +43,7 @@ - else %span.token-never-expires-label= _('Never') %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected') - %td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'btn btn-danger float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } } + %td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'gl-button btn btn-danger float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } } - else .settings-message.text-center = no_active_tokens_message |