diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-24 21:09:57 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-24 21:09:57 +0300 |
commit | 80b22a4413679216b470c7a4e9fefd0eb928add5 (patch) | |
tree | 909b8eb16b9316d5260e609b11d0eb7d8dc03323 /app | |
parent | 92849dc177d5e0d11f89b4ca75f4e3e45ad6341b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
25 files changed, 154 insertions, 148 deletions
diff --git a/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue b/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue index 64229f54904..3a9ec341789 100644 --- a/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue +++ b/app/assets/javascripts/ci/catalog/components/list/catalog_header.vue @@ -2,6 +2,7 @@ import { GlBanner, GlLink } from '@gitlab/ui'; import { __, s__ } from '~/locale'; import { helpPagePath } from '~/helpers/help_page_helper'; +import BetaBadge from '~/vue_shared/components/badges/beta_badge.vue'; import { CATALOG_FEEDBACK_DISMISSED_KEY } from '../../constants'; const defaultTitle = __('CI/CD Catalog'); @@ -11,6 +12,7 @@ const defaultDescription = s__( export default { components: { + BetaBadge, GlBanner, GlLink, }, @@ -58,7 +60,10 @@ export default { {{ $options.i18n.banner.description }} </p> </gl-banner> - <h1 class="page-title gl-font-size-h-display">{{ pageTitle }}</h1> + <div class="gl-my-4 gl-display-flex gl-align-items-center"> + <h1 class="gl-m-0 gl-font-size-h-display">{{ pageTitle }}</h1> + <beta-badge class="gl-ml-3" /> + </div> <p> <span data-testid="page-description">{{ pageDescription }}</span> <gl-link :href="$options.learnMorePath" target="_blank">{{ diff --git a/app/assets/javascripts/clone_panel.js b/app/assets/javascripts/clone_panel.js index 79280c13f0f..d2a5d5a9db7 100644 --- a/app/assets/javascripts/clone_panel.js +++ b/app/assets/javascripts/clone_panel.js @@ -14,7 +14,7 @@ export default function initClonePanel() { $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active'); } - $('a', $cloneOptions).on('click', (e) => { + $('.js-clone-links a', $cloneOptions).on('click', (e) => { const $this = $(e.currentTarget); const url = $this.attr('href'); if ( diff --git a/app/assets/javascripts/environments/folder/environments_folder_app.vue b/app/assets/javascripts/environments/folder/environments_folder_app.vue new file mode 100644 index 00000000000..a963ca9b144 --- /dev/null +++ b/app/assets/javascripts/environments/folder/environments_folder_app.vue @@ -0,0 +1,21 @@ +<script> +import { s__ } from '~/locale'; + +export default { + props: { + folderName: { + type: String, + required: true, + }, + }, + i18n: { + pageTitle: s__('Environments|Environments'), + }, +}; +</script> +<template> + <h4 class="gl-font-weight-normal" data-testid="folder-name"> + {{ $options.i18n.pageTitle }} / + <b>{{ folderName }}</b> + </h4> +</template> diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js index 1a32de30de0..beaf8041c39 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js @@ -2,7 +2,8 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import Translate from '~/vue_shared/translate'; -import EnvironmentsFolderApp from './environments_folder_view.vue'; +import EnvironmentsFolderView from './environments_folder_view.vue'; +import EnvironmentsFolderApp from './environments_folder_app.vue'; Vue.use(Translate); Vue.use(VueApollo); @@ -13,19 +14,35 @@ const apolloProvider = new VueApollo({ export default () => { const el = document.getElementById('environments-folder-list-view'); + const environmentsData = el.dataset; + if (gon.features.environmentsFolderNewLook) { + const folderName = environmentsData.environmentsDataFolderName; + + return new Vue({ + el, + components: { + EnvironmentsFolderApp, + }, + render(createElement) { + return createElement('environments-folder-app', { + props: { + folderName, + }, + }); + }, + }); + } return new Vue({ el, components: { - EnvironmentsFolderApp, + EnvironmentsFolderView, }, apolloProvider, provide: { projectPath: el.dataset.projectPath, }, data() { - const environmentsData = el.dataset; - return { endpoint: environmentsData.environmentsDataEndpoint, folderName: environmentsData.environmentsDataFolderName, @@ -33,7 +50,7 @@ export default () => { }; }, render(createElement) { - return createElement('environments-folder-app', { + return createElement('environments-folder-view', { props: { endpoint: this.endpoint, folderName: this.folderName, diff --git a/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js b/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js index 9111dc9c86c..eab25298c36 100644 --- a/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js +++ b/app/assets/javascripts/environments/graphql/resolvers/kubernetes.js @@ -10,6 +10,7 @@ import produce from 'immer'; import { getK8sPods, handleClusterError, + buildWatchPath, } from '~/kubernetes_dashboard/graphql/helpers/resolver_helpers'; import { humanizeClusterErrors } from '../../helpers/k8s_integration_helper'; import k8sPodsQuery from '../queries/k8s_pods.query.graphql'; @@ -62,9 +63,7 @@ const mapWorkloadItems = (items, kind) => { const watchWorkloadItems = ({ kind, apiVersion, configuration, namespace, client }) => { const itemKind = kind.toLowerCase().replace('list', 's'); - const path = namespace - ? `/apis/${apiVersion}/namespaces/${namespace}/${itemKind}` - : `/apis/${apiVersion}/${itemKind}`; + const path = buildWatchPath({ resource: itemKind, api: `apis/${apiVersion}`, namespace }); const config = new Configuration(configuration); const watcherApi = new WatchApi(config); @@ -113,7 +112,7 @@ const mapServicesItems = (items) => { }; const watchServices = ({ configuration, namespace, client }) => { - const path = namespace ? `/api/v1/namespaces/${namespace}/services` : '/api/v1/services'; + const path = buildWatchPath({ resource: 'services', namespace }); const config = new Configuration(configuration); const watcherApi = new WatchApi(config); diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 39a8b1d0a9c..1babccff425 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -262,6 +262,38 @@ class GfmAutoComplete { }); } + // eslint-disable-next-line class-methods-use-this + setSubmitReviewStates($input) { + if (!window.gon.features?.mrRequestChanges) return; + + const REVIEW_STATES = { + reviewed: { + header: __('Comment'), + description: __('Submit general feedback without explicit approval.'), + }, + approve: { + header: __('Approve'), + description: __('Submit feedback and approve these changes.'), + }, + requested_changes: { + header: __('Request changes'), + description: __('Submit feedback that should be addressed before merging.'), + }, + }; + + $input.filter('[data-supports-quick-actions="true"]').atwho({ + // Always keep the trailing space otherwise the command won't display correctly + at: '/submit_review ', + alias: 'submit_review', + data: Object.keys(REVIEW_STATES), + displayTpl({ name }) { + const reviewState = REVIEW_STATES[name]; + + return `<li><span class="gl-font-weight-bold gl-display-block">${reviewState.header}</span><small class="description gl-display-block gl-w-full gl-float-left! gl-px-0!">${reviewState.description}</small></li>`; + }, + }); + } + setupEmoji($input) { const fetchData = this.fetchData.bind(this); @@ -851,6 +883,9 @@ class GfmAutoComplete { } else if (dataSource) { AjaxCache.retrieve(dataSource, true) .then((data) => { + if (data.some((c) => c.name === 'submit_review')) { + this.setSubmitReviewStates($input); + } this.loadData($input, at, data); }) .catch(() => { diff --git a/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js b/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js index 22ddd7c6a61..4eec31cfe37 100644 --- a/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js +++ b/app/assets/javascripts/kubernetes_dashboard/graphql/helpers/resolver_helpers.js @@ -9,8 +9,12 @@ export const handleClusterError = async (err) => { throw errorData; }; +export const buildWatchPath = ({ resource, api = 'api/v1', namespace = '' }) => { + return namespace ? `/${api}/namespaces/${namespace}/${resource}` : `/${api}/${resource}`; +}; + export const watchPods = ({ client, query, configuration, namespace }) => { - const path = namespace ? `/api/v1/namespaces/${namespace}/pods` : '/api/v1/pods'; + const path = buildWatchPath({ resource: 'pods', namespace }); const config = new Configuration(configuration); const watcherApi = new WatchApi(config); diff --git a/app/assets/javascripts/notes/components/notes_activity_header.vue b/app/assets/javascripts/notes/components/notes_activity_header.vue index 23b2ae74e41..be9c768ae60 100644 --- a/app/assets/javascripts/notes/components/notes_activity_header.vue +++ b/app/assets/javascripts/notes/components/notes_activity_header.vue @@ -38,9 +38,7 @@ export default { }, computed: { showAiActions() { - return ( - this.resourceGlobalId && this.glFeatures.aiGlobalSwitch && this.glFeatures.summarizeNotes - ); + return this.resourceGlobalId && this.glFeatures.summarizeNotes; }, }, }; diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index e0c49412d56..307be0b0aa0 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -67,10 +67,7 @@ export default { return this.currentScope === SCOPE_MILESTONES; }, showWikiBlobsFilters() { - return ( - this.currentScope === SCOPE_WIKI_BLOBS && - this.glFeatures?.searchProjectWikisHideArchivedProjects - ); + return this.currentScope === SCOPE_WIKI_BLOBS; }, }, methods: { diff --git a/app/assets/stylesheets/page_bundles/project.scss b/app/assets/stylesheets/page_bundles/project.scss index 8d8da10268a..504f1405148 100644 --- a/app/assets/stylesheets/page_bundles/project.scss +++ b/app/assets/stylesheets/page_bundles/project.scss @@ -53,7 +53,7 @@ } } - .project-clone-holder { + .project-code-holder { display: inline-block; margin: $gl-padding 0 0; diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 4b2749dc716..8cdd6efa7c5 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -14,6 +14,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController push_frontend_feature_flag(:k8s_watch_api, project) end + before_action only: [:folder] do + push_frontend_feature_flag(:environments_folder_new_look, project) + end + before_action :authorize_read_environment! before_action :authorize_create_environment!, only: [:new, :create] before_action :authorize_stop_environment!, only: [:stop] diff --git a/app/models/product_analytics_event.rb b/app/models/product_analytics_event.rb deleted file mode 100644 index 52baa3be6c4..00000000000 --- a/app/models/product_analytics_event.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -class ProductAnalyticsEvent < ApplicationRecord - self.table_name = 'product_analytics_events_experimental' - - # Ignore that the partition key :project_id is part of the formal primary key - self.primary_key = :id - - belongs_to :project - - validates :event_id, :project_id, :v_collector, :v_etl, presence: true - - # There is no default Rails timestamps in the table. - # collector_tstamp is a timestamp when a collector recorded an event. - scope :order_by_time, -> { order(collector_tstamp: :desc) } - - # If we decide to change this scope to use date_trunc('day', collector_tstamp), - # we should remember that a btree index on collector_tstamp will be no longer effective. - scope :timerange, ->(duration, today = Time.zone.today) { - where('collector_tstamp BETWEEN ? AND ? ', today - duration + 1, today + 1) - } - - def self.count_by_graph(graph, days) - group(graph).timerange(days).count - end - - def self.count_collector_tstamp_by_day(days) - group("DATE_TRUNC('day', collector_tstamp)") - .reorder('date_trunc_day_collector_tstamp') - .timerange(days) - .count - end - - def as_json_wo_empty - as_json.compact - end -end diff --git a/app/models/project.rb b/app/models/project.rb index 57c127965f7..5788885498c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -468,10 +468,6 @@ class Project < ApplicationRecord # rubocop:enable Cop/ActiveRecordDependent has_many :active_pages_deployments, -> { active }, class_name: 'PagesDeployment', inverse_of: :project - # Can be too many records. We need to implement delete_all in batches. - # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/228637 - has_many :product_analytics_events, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :operations_feature_flags, class_name: 'Operations::FeatureFlag' has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient' has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList' diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 04fbc8467c9..ccab3d9f02d 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -33,6 +33,7 @@ class UserPolicy < BasePolicy enable :read_saved_replies enable :read_user_email_address enable :admin_user_email_address + enable :make_profile_private end rule { default }.enable :read_user_profile diff --git a/app/services/product_analytics/build_activity_graph_service.rb b/app/services/product_analytics/build_activity_graph_service.rb deleted file mode 100644 index 63108d76afd..00000000000 --- a/app/services/product_analytics/build_activity_graph_service.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module ProductAnalytics - class BuildActivityGraphService < BuildGraphService - def execute - timerange = @params[:timerange].days - - results = product_analytics_events.count_collector_tstamp_by_day(timerange) - - format_results('collector_tstamp', results.transform_keys(&:to_date)) - end - end -end diff --git a/app/services/product_analytics/build_graph_service.rb b/app/services/product_analytics/build_graph_service.rb deleted file mode 100644 index da54ad4de0e..00000000000 --- a/app/services/product_analytics/build_graph_service.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module ProductAnalytics - class BuildGraphService - def initialize(project, params) - @project = project - @params = params - end - - def execute - graph = @params[:graph].to_sym - timerange = @params[:timerange].days - - results = product_analytics_events.count_by_graph(graph, timerange) - - format_results(graph, results) - end - - private - - def format_results(name, results) - { - id: name, - keys: results.keys, - values: results.values - } - end - - def product_analytics_events - @project.product_analytics_events - end - end -end diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 00da6c73081..68e73a017a7 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -5,8 +5,8 @@ .controls.gl-display-flex = link_button_to nil, project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'd-none d-sm-inline-flex has-tooltip', icon: 'rss' - if is_project_overview && can?(current_user, :download_code, @project) - .project-clone-holder.d-none.d-md-inline-flex.gl-ml-2 - = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' + .project-code-holder.d-none.d-md-inline-flex.gl-ml-2 + = render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref .content_list.project-activity{ :"data-href" => activity_project_path(@project) } .loading diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_code.html.haml index 0e645eda678..0b2a527025e 100644 --- a/app/views/projects/buttons/_clone.html.haml +++ b/app/views/projects/buttons/_code.html.haml @@ -1,15 +1,16 @@ - project = project || @project - dropdown_class = local_assigns.fetch(:dropdown_class, '') +- ref = local_assigns.fetch(:ref) - if can?(current_user, :download_code, @project) .git-clone-holder.js-git-clone-holder = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { id: 'clone-dropdown', class: 'clone-dropdown-btn', data: { toggle: 'dropdown', qa_selector: 'clone_dropdown' } }) do - %span.gl-mr-2.js-clone-dropdown-label - = _('Clone') + %span.js-clone-dropdown-label + = _('Code') = sprite_icon("chevron-down", css_class: "icon") - %ul.dropdown-menu.dropdown-menu-large.dropdown-menu-selectable.clone-options-dropdown{ class: dropdown_class, data: { qa_selector: 'clone_dropdown_content' } } + %ul.dropdown-menu.dropdown-menu-large.clone-options-dropdown{ class: dropdown_class, data: { qa_selector: 'clone_dropdown_content' } } - if ssh_enabled? - %li{ class: 'gl-px-4!' } + %li.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' } %label.label-bold = _('Clone with SSH') .input-group.btn-group @@ -18,7 +19,7 @@ = clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), category: :primary, size: :medium) = render_if_exists 'projects/buttons/geo' - if http_enabled? - %li.pt-2{ class: 'gl-px-4!' } + %li.pt-2.gl-dropdown-item.js-clone-links{ class: 'gl-px-4!' } %label.label-bold = _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase } .input-group.btn-group @@ -28,7 +29,7 @@ = render_if_exists 'projects/buttons/geo' = render_if_exists 'projects/buttons/kerberos_clone_field' %li.divider.mt-2 - %li.pt-2.gl-dropdown-item + %li.pt-2.gl-dropdown-item.js-clone-links %label.label-bold{ class: 'gl-px-4!' } = _('Open in your IDE') - if ssh_enabled? @@ -53,3 +54,6 @@ %a.dropdown-item.open-with-link{ href: xcode_uri_to_repo(@project) } .gl-dropdown-item-text-wrapper = _("Xcode") + - if !project.empty_repo? && can?(current_user, :download_code, project) + %li.divider.mt-2 + = render 'projects/buttons/download_menu_items', project: project, ref: ref diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index b3282742407..946057a2e2f 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,27 +1,13 @@ - project = local_assigns.fetch(:project) - ref = local_assigns.fetch(:ref) -- pipeline = local_assigns.fetch(:pipeline) { project.latest_successful_pipeline_for(ref) } +- pipeline = local_assigns.fetch(:pipeline, nil) - css_class = local_assigns.fetch(:css_class, '') - if !project.empty_repo? && can?(current_user, :download_code, project) - - archive_prefix = "#{project.path}-#{ref.tr('/', '-')}" .project-action-button.dropdown.gl-dropdown.inline{ class: css_class }> = render Pajamas::ButtonComponent.new(button_options: { class: 'dropdown-toggle gl-dropdown-toggle dropdown-icon-only has-tooltip', title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { testid: 'download-source-code-button' } }) do = sprite_icon('download', css_class: 'gl-icon dropdown-icon') %span.sr-only= _('Select Archive Format') = sprite_icon('chevron-down', css_class: 'gl-icon dropdown-chevron') - .dropdown-menu.dropdown-menu-right{ role: 'menu' } - %section - %h5.m-0.dropdown-bold-header= _('Download source code') - .dropdown-menu-content - = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil - .js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } } - - if pipeline && pipeline.latest_builds_with_artifacts.any? - %section.border-top.pt-1.mt-1 - %h5.m-0.dropdown-bold-header= _('Download artifacts') - - unless pipeline.latest? - %span.unclickable= ci_status_for_statuseable(project.latest_pipeline(ref)) - %h6.m-0.dropdown-header= _('Previous Artifacts') - %ul - - pipeline.latest_builds_with_artifacts.each do |job| - %li= link_to job.name, latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' + %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } + = render 'projects/buttons/download_menu_items', project: project, ref: ref, pipeline: pipeline diff --git a/app/views/projects/buttons/_download_links.html.haml b/app/views/projects/buttons/_download_links.html.haml index 31185fc1532..7035f3b3792 100644 --- a/app/views/projects/buttons/_download_links.html.haml +++ b/app/views/projects/buttons/_download_links.html.haml @@ -1,4 +1,5 @@ -.btn-group.ml-0.w-100 - - Gitlab::Workhorse::ARCHIVE_FORMATS.each_with_index do |fmt, index| - - archive_path = project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: fmt) - = link_button_to fmt, external_storage_url_or_path(archive_path), rel: 'nofollow', download: '', variant: index == 0 ? :confirm : :default, size: :small +- Gitlab::Workhorse::ARCHIVE_FORMATS.each_with_index do |fmt, index| + - archive_path = project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: fmt) + + %a.dropdown-item.open-with-link{ href: external_storage_url_or_path(archive_path), rel: 'nofollow', download: '' } + .gl-dropdown-item-text-wrapper= fmt diff --git a/app/views/projects/buttons/_download_menu_items.html.haml b/app/views/projects/buttons/_download_menu_items.html.haml new file mode 100644 index 00000000000..e4fcae1815c --- /dev/null +++ b/app/views/projects/buttons/_download_menu_items.html.haml @@ -0,0 +1,21 @@ +- project = local_assigns.fetch(:project) +- ref = local_assigns.fetch(:ref) +- pipeline = local_assigns.fetch(:pipeline) { project.latest_successful_pipeline_for(ref) } +- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}" + +%li.gl-dropdown-item + %h5.m-0.dropdown-bold-header= _('Download source code') + = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil +.js-directory-downloads{ data: { links: directory_download_links(project, ref, archive_prefix).to_json } } +- if pipeline && pipeline.latest_builds_with_artifacts.any? + %li.divider.mt-2 + %li.gl-dropdown-item + %h5.m-0.dropdown-bold-header= _('Download artifacts') + - unless pipeline.latest? + %span.gl-ml-3.unclickable= ci_status_for_statuseable(project.latest_pipeline(ref)) + %li.divider.mt-2 + %li.gl-dropdown-item + %h5.m-0.dropdown-bold-header= _('Previous Artifacts') + - pipeline.latest_builds_with_artifacts.each do |job| + = link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), class: 'dropdown-item open-with-link', rel: 'nofollow', download: '' do + .gl-dropdown-item-text-wrapper= job.name diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 60b5ab376ec..ca9900a646e 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -19,11 +19,11 @@ = _('You can get started by cloning the repository or start adding files to it with one of the following options.') .project-buttons{ data: { testid: 'quick-actions-container' } } - .project-clone-holder.d-block.d-md-none.gl-mt-3.gl-mr-3 + .project-code-holder.d-block.d-md-none.gl-mt-3.gl-mr-3 = render "shared/mobile_clone_panel" - .project-clone-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left - = render "projects/buttons/clone" + .project-code-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left + = render "projects/buttons/code", ref: @ref = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true - if can?(current_user, :push_code, @project) diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index bed37d9cb63..8eb0bb85e66 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -16,10 +16,10 @@ = render 'projects/find_file_link' = render 'shared/web_ide_button', blob: nil - = render 'projects/buttons/download', project: @project, ref: @ref - .project-clone-holder.d-none.d-md-inline-block> - = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' + .project-code-holder.d-none.d-md-inline-block> + = render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref - .project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-md-2> - = render "shared/mobile_clone_panel" + .project-code-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-md-2> + = render 'projects/buttons/download', project: @project, ref: @ref + = render "shared/mobile_clone_panel", ref: @ref diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 4b39ec52837..7dce8737eb4 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -10,9 +10,9 @@ = default_clone_protocol.upcase = sprite_icon('chevron-down', css_class: 'gl-icon') %ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown{ data: { testid: 'clone-dropdown-content' } } - %li + %li.js-clone-links = ssh_clone_button(container) - %li + %li.js-clone-links = http_clone_button(container) = render_if_exists 'shared/kerberos_clone_button', container: container diff --git a/app/views/shared/_mobile_clone_panel.html.haml b/app/views/shared/_mobile_clone_panel.html.haml index c594cee326e..2f7fb348c17 100644 --- a/app/views/shared/_mobile_clone_panel.html.haml +++ b/app/views/shared/_mobile_clone_panel.html.haml @@ -8,9 +8,9 @@ = sprite_icon("chevron-down", css_class: "dropdown-btn-icon icon") %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } } - if ssh_enabled? - %li + %li.js-clone-links = dropdown_item_with_description(ssh_copy_label, ssh_clone_url_to_repo(project), href: ssh_clone_url_to_repo(project), data: { clone_type: 'ssh' }, default: true) - if http_enabled? - %li + %li.js-clone-links = dropdown_item_with_description(http_copy_label, http_clone_url_to_repo(project), href: http_clone_url_to_repo(project), data: { clone_type: 'http' }) = render_if_exists 'shared/mobile_kerberos_clone' |