diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 03:09:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 03:09:44 +0300 |
commit | e512af1d82777b8bcf0bc678b8aeb3b96ef406a2 (patch) | |
tree | ebff9b5da0ca2e789502a91f7582985cf4ae091f /app | |
parent | e8c01bc6a16cc4aa934ac42cccb7b287527c93f0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
30 files changed, 325 insertions, 154 deletions
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js index 445602a8765..470c679b8ba 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js +++ b/app/assets/javascripts/blob/blob_file_dropzone.js @@ -3,7 +3,6 @@ import Dropzone from 'dropzone'; import $ from 'jquery'; import { sprintf, __ } from '~/locale'; -import { trackUploadFileFormSubmitted } from '~/projects/upload_file_experiment'; import { HIDDEN_CLASS } from '../lib/utils/constants'; import csrf from '../lib/utils/csrf'; import { visitUrl } from '../lib/utils/url_utility'; @@ -85,8 +84,6 @@ export default class BlobFileDropzone { e.preventDefault(); e.stopPropagation(); - trackUploadFileFormSubmitted(); - if (dropzone[0].dropzone.getQueuedFiles().length === 0) { // eslint-disable-next-line no-alert alert(__('Please select a file')); diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 6d9b56b4bb8..173c82ef9b0 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -4,7 +4,6 @@ import $ from 'jquery'; import initPopover from '~/blob/suggest_gitlab_ci_yml'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { disableButtonIfEmptyField, setCookie } from '~/lib/utils/common_utils'; -import { initUploadFileTrigger } from '~/projects/upload_file_experiment'; import Tracking from '~/tracking'; import BlobFileDropzone from '../blob/blob_file_dropzone'; import NewCommitForm from '../new_commit_form'; @@ -48,7 +47,6 @@ export const initUploadForm = () => { new NewCommitForm(uploadBlobForm); disableButtonIfEmptyField(uploadBlobForm.find('.js-commit-message'), '.btn-upload-file'); - initUploadFileTrigger(); } }; diff --git a/app/assets/javascripts/pages/admin/abuse_reports/index.js b/app/assets/javascripts/pages/admin/abuse_reports/index.js index 5649c47d7e8..0a4311ec73a 100644 --- a/app/assets/javascripts/pages/admin/abuse_reports/index.js +++ b/app/assets/javascripts/pages/admin/abuse_reports/index.js @@ -1,8 +1,5 @@ -/* eslint-disable no-new */ import UsersSelect from '~/users_select'; import AbuseReports from './abuse_reports'; -document.addEventListener('DOMContentLoaded', () => { - new AbuseReports(); - new UsersSelect(); -}); +new AbuseReports(); /* eslint-disable-line no-new */ +new UsersSelect(); /* eslint-disable-line no-new */ diff --git a/app/assets/javascripts/pages/admin/application_settings/general/index.js b/app/assets/javascripts/pages/admin/application_settings/general/index.js index e2a6f4208b9..eda1a9d3599 100644 --- a/app/assets/javascripts/pages/admin/application_settings/general/index.js +++ b/app/assets/javascripts/pages/admin/application_settings/general/index.js @@ -1,7 +1,3 @@ -// This is a true violation of @gitlab/no-runtime-template-compiler, as it -// relies on app/views/admin/application_settings/_gitpod.html.haml for its -// template. -/* eslint-disable @gitlab/no-runtime-template-compiler */ import Vue from 'vue'; import IntegrationHelpText from '~/vue_shared/components/integrations_help_text.vue'; import initUserInternalRegexPlaceholder from '../account_and_limits'; @@ -9,17 +5,23 @@ import initUserInternalRegexPlaceholder from '../account_and_limits'; (() => { initUserInternalRegexPlaceholder(); - const gitpodSettingEl = document.querySelector('#js-gitpod-settings-help-text'); - if (!gitpodSettingEl) { + const el = document.querySelector('#js-gitpod-settings-help-text'); + if (!el) { return; } + const { message, messageUrl } = el.dataset; + // eslint-disable-next-line no-new new Vue({ - el: gitpodSettingEl, - name: 'GitpodSettings', - components: { - IntegrationHelpText, + el, + render(createElement) { + return createElement(IntegrationHelpText, { + props: { + message, + messageUrl, + }, + }); }, }); })(); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index a0831c7df41..83e43d7ac48 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -5,6 +5,7 @@ import BlobViewer from '~/blob/viewer/index'; import { initUploadForm } from '~/blob_edit/blob_bundle'; import leaveByUrl from '~/namespaces/leave_by_url'; import initVueNotificationsDropdown from '~/notifications'; +import { initUploadFileTrigger } from '~/projects/upload_file_experiment'; import initReadMore from '~/read_more'; import UserCallout from '~/user_callout'; import Star from '../../../star'; @@ -41,3 +42,5 @@ leaveByUrl('project'); initVueNotificationsDropdown(); new ShortcutsNavigation(); // eslint-disable-line no-new + +initUploadFileTrigger(); diff --git a/app/assets/javascripts/projects/details/upload_button.vue b/app/assets/javascripts/projects/details/upload_button.vue index a89ea34c438..5b19f15c233 100644 --- a/app/assets/javascripts/projects/details/upload_button.vue +++ b/app/assets/javascripts/projects/details/upload_button.vue @@ -1,6 +1,7 @@ <script> import { GlButton, GlModalDirective } from '@gitlab/ui'; import UploadBlobModal from '~/repository/components/upload_blob_modal.vue'; +import { trackFileUploadEvent } from '../upload_file_experiment_tracking'; const UPLOAD_BLOB_MODAL_ID = 'details-modal-upload-blob'; @@ -16,7 +17,7 @@ export default { targetBranch: { default: '', }, - origionalBranch: { + originalBranch: { default: '', }, canPushCode: { @@ -29,19 +30,28 @@ export default { default: '', }, }, + methods: { + trackOpenModal() { + trackFileUploadEvent('click_upload_modal_trigger'); + }, + }, uploadBlobModalId: UPLOAD_BLOB_MODAL_ID, }; </script> <template> <span> - <gl-button v-gl-modal="$options.uploadBlobModalId" icon="upload">{{ - __('Upload File') - }}</gl-button> + <gl-button + v-gl-modal="$options.uploadBlobModalId" + icon="upload" + data-testid="upload-file-button" + @click="trackOpenModal" + >{{ __('Upload File') }}</gl-button + > <upload-blob-modal :modal-id="$options.uploadBlobModalId" :commit-message="__('Upload New File')" :target-branch="targetBranch" - :origional-branch="origionalBranch" + :original-branch="originalBranch" :can-push-code="canPushCode" :path="path" /> diff --git a/app/assets/javascripts/projects/upload_file_experiment.js b/app/assets/javascripts/projects/upload_file_experiment.js index 7d61df36a75..a7519f2bce8 100644 --- a/app/assets/javascripts/projects/upload_file_experiment.js +++ b/app/assets/javascripts/projects/upload_file_experiment.js @@ -1,24 +1,33 @@ -import ExperimentTracking from '~/experimentation/experiment_tracking'; +import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import createRouter from '~/repository/router'; +import UploadButton from './details/upload_button.vue'; -function trackEvent(eventName) { - const isEmpty = Boolean(document.querySelector('.project-home-panel.empty-project')); - const property = isEmpty ? 'empty' : 'nonempty'; - const label = 'blob-upload-modal'; - const Tracking = new ExperimentTracking('empty_repo_upload', { label, property }); - - Tracking.event(eventName); -} - -export function initUploadFileTrigger() { +export const initUploadFileTrigger = () => { const uploadFileTriggerEl = document.querySelector('.js-upload-file-experiment-trigger'); - if (uploadFileTriggerEl) { - uploadFileTriggerEl.addEventListener('click', () => { - trackEvent('click_upload_modal_trigger'); - }); - } -} + if (!uploadFileTriggerEl) return false; + + const { + targetBranch, + originalBranch, + canPushCode, + path, + projectPath, + } = uploadFileTriggerEl.dataset; -export function trackUploadFileFormSubmitted() { - trackEvent('click_upload_modal_form_submit'); -} + return new Vue({ + el: uploadFileTriggerEl, + router: createRouter(projectPath, originalBranch), + provide: { + targetBranch, + originalBranch, + canPushCode: parseBoolean(canPushCode), + path, + projectPath, + }, + render(h) { + return h(UploadButton); + }, + }); +}; diff --git a/app/assets/javascripts/projects/upload_file_experiment_tracking.js b/app/assets/javascripts/projects/upload_file_experiment_tracking.js new file mode 100644 index 00000000000..c5e93f19b32 --- /dev/null +++ b/app/assets/javascripts/projects/upload_file_experiment_tracking.js @@ -0,0 +1,9 @@ +import ExperimentTracking from '~/experimentation/experiment_tracking'; + +export const trackFileUploadEvent = (eventName) => { + const isEmpty = Boolean(document.querySelector('.project-home-panel.empty-project')); + const property = isEmpty ? 'empty' : 'nonempty'; + const label = 'blob-upload-modal'; + const FileUploadTracking = new ExperimentTracking('empty_repo_upload', { label, property }); + FileUploadTracking.event(eventName); +}; diff --git a/app/assets/javascripts/repository/components/upload_blob_modal.vue b/app/assets/javascripts/repository/components/upload_blob_modal.vue index 4cdfc5e947a..ec7ba469ca0 100644 --- a/app/assets/javascripts/repository/components/upload_blob_modal.vue +++ b/app/assets/javascripts/repository/components/upload_blob_modal.vue @@ -15,6 +15,7 @@ import { ContentTypeMultipartFormData } from '~/lib/utils/headers'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import { visitUrl, joinPaths } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; +import { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking'; import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue'; const PRIMARY_OPTIONS_TEXT = __('Upload file'); @@ -62,7 +63,7 @@ export default { type: String, required: true, }, - origionalBranch: { + originalBranch: { type: String, required: true, }, @@ -113,7 +114,7 @@ export default { return numberToHumanSize(this.file.size); }, showCreateNewMrToggle() { - return this.canPushCode && this.target !== this.origionalBranch; + return this.canPushCode && this.target !== this.originalBranch; }, formCompleted() { return this.file && this.commit && this.target; @@ -158,6 +159,7 @@ export default { }, }) .then((response) => { + trackFileUploadEvent('click_upload_modal_form_submit'); visitUrl(response.data.filePath); }) .catch(() => { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue deleted file mode 100644 index 3cd003461b3..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_merge_help.vue +++ /dev/null @@ -1,49 +0,0 @@ -<script> -import { GlButton, GlModalDirective } from '@gitlab/ui'; -import { sprintf, s__ } from '~/locale'; - -export default { - name: 'MRWidgetMergeHelp', - components: { - GlButton, - }, - directives: { - GlModalDirective, - }, - props: { - missingBranch: { - type: String, - required: false, - default: '', - }, - }, - computed: { - missingBranchInfo() { - return sprintf( - s__( - 'mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the', - ), - { branch: this.missingBranch }, - ); - }, - }, -}; -</script> -<template> - <section class="gl-py-3 gl-pr-3 gl-pl-5 gl-ml-7 mr-widget-help gl-font-style-italic"> - <template v-if="missingBranch"> - {{ missingBranchInfo }} - </template> - <template v-else> - {{ s__('mrWidget|You can merge this merge request manually using the') }} - </template> - - <gl-button - v-gl-modal-directive="'modal-merge-info'" - variant="link" - class="gl-mt-n2 js-open-modal-help" - > - {{ s__('mrWidget|command line') }} - </gl-button> - </section> -</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index e2a0689dffc..89b095fbfc1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -18,7 +18,6 @@ import GroupedTestReportsApp from '../reports/grouped_test_report/grouped_test_r import Loading from './components/loading.vue'; import MrWidgetAlertMessage from './components/mr_widget_alert_message.vue'; import WidgetHeader from './components/mr_widget_header.vue'; -import WidgetMergeHelp from './components/mr_widget_merge_help.vue'; import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue'; import WidgetRelatedLinks from './components/mr_widget_related_links.vue'; import WidgetSuggestPipeline from './components/mr_widget_suggest_pipeline.vue'; @@ -59,7 +58,6 @@ export default { // ExtensionsContainer, 'mr-widget-header': WidgetHeader, 'mr-widget-suggest-pipeline': WidgetSuggestPipeline, - 'mr-widget-merge-help': WidgetMergeHelp, MrWidgetPipelineContainer, 'mr-widget-related-links': WidgetRelatedLinks, MrWidgetAlertMessage, @@ -140,9 +138,6 @@ export default { componentName() { return stateMaps.stateToComponentMap[this.mr.state]; }, - shouldRenderMergeHelp() { - return stateMaps.statesToShowHelpWidget.indexOf(this.mr.state) > -1; - }, hasPipelineMustSucceedConflict() { return !this.mr.hasCI && this.mr.onlyAllowMergeIfPipelineSucceeds; }, @@ -530,9 +525,6 @@ export default { <source-branch-removal-status v-if="shouldRenderSourceBranchRemovalStatus" /> </div> </div> - <div v-if="shouldRenderMergeHelp" class="mr-widget-footer"> - <mr-widget-merge-help /> - </div> </div> <mr-widget-pipeline-container v-if="shouldRenderMergedPipeline" diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb index 01dc0b1ab00..5a885349467 100644 --- a/app/controllers/dashboard/snippets_controller.rb +++ b/app/controllers/dashboard/snippets_controller.rb @@ -19,6 +19,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController .page(params[:page]) .inc_author .inc_projects_namespace_route + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb index 91ab18f2f55..617cc2e7f3d 100644 --- a/app/controllers/explore/snippets_controller.rb +++ b/app/controllers/explore/snippets_controller.rb @@ -11,6 +11,7 @@ class Explore::SnippetsController < Explore::ApplicationController .page(params[:page]) .without_count .inc_author + .inc_statistics @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 779e149bb9c..ff28c3be298 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -22,6 +22,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController .execute .page(params[:page]) .inc_author + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 913b1e3bb6e..1c6168dbc2c 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -24,6 +24,7 @@ class SnippetsController < Snippets::ApplicationController .execute .page(params[:page]) .inc_author + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 1be7e240c1a..36f4fced147 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -68,4 +68,18 @@ module SnippetsHelper title: 'Download', rel: 'noopener noreferrer') end + + def snippet_file_count(snippet) + file_count = snippet.statistics&.file_count + + return unless file_count&.nonzero? + + tooltip = n_('%d file', '%d files', file_count) % file_count + + tag.span(class: 'file_count', title: tooltip, data: { toggle: 'tooltip', container: 'body' }) do + concat(sprite_icon('documents', css_class: 'gl-vertical-align-middle')) + concat(' ') + concat(file_count) + end + end end diff --git a/app/models/group.rb b/app/models/group.rb index ba0d70b9c13..9f8a9996f31 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -340,6 +340,13 @@ class Group < Namespace has_owner?(user) && members_with_parents.owners.size == 1 end + def last_blocked_owner?(user) + return false if members_with_parents.owners.any? + + blocked_owners = members.blocked.where(access_level: Gitlab::Access::OWNER) + blocked_owners.size == 1 && blocked_owners.exists?(user_id: user) + end + def ldap_synced? false end diff --git a/app/models/member.rb b/app/models/member.rb index 62fe757683f..38574d67cb6 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -75,7 +75,20 @@ class Member < ApplicationRecord left_join_users .where(user_ok) - .where(requested_at: nil) + .non_request + .non_minimal_access + .reorder(nil) + end + + scope :blocked, -> do + is_external_invite = arel_table[:user_id].eq(nil).and(arel_table[:invite_token].not_eq(nil)) + user_is_blocked = User.arel_table[:state].eq(:blocked) + + user_ok = Arel::Nodes::Grouping.new(is_external_invite).or(user_is_blocked) + + left_join_users + .where(user_ok) + .non_request .non_minimal_access .reorder(nil) end diff --git a/app/models/packages/maven/metadatum.rb b/app/models/packages/maven/metadatum.rb index b7f27fb9e06..7aed274216b 100644 --- a/app/models/packages/maven/metadatum.rb +++ b/app/models/packages/maven/metadatum.rb @@ -18,6 +18,13 @@ class Packages::Maven::Metadatum < ApplicationRecord validate :maven_package_type + scope :for_package_ids, -> (package_ids) { where(package_id: package_ids) } + scope :order_created, -> { reorder('created_at ASC') } + + def self.pluck_app_name + pluck(:app_name) + end + private def maven_package_type diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8edf31bd661..5fdd4551982 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -82,6 +82,7 @@ class Snippet < ApplicationRecord scope :fresh, -> { order("created_at DESC") } scope :inc_author, -> { includes(:author) } scope :inc_relations_for_view, -> { includes(author: :status) } + scope :inc_statistics, -> { includes(:statistics) } scope :with_statistics, -> { joins(:statistics) } scope :inc_projects_namespace_route, -> { includes(project: [:route, :namespace]) } diff --git a/app/policies/group_member_policy.rb b/app/policies/group_member_policy.rb index 09cac96e3a5..1dd650c8a90 100644 --- a/app/policies/group_member_policy.rb +++ b/app/policies/group_member_policy.rb @@ -4,7 +4,7 @@ class GroupMemberPolicy < BasePolicy delegate :group with_scope :subject - condition(:last_owner) { @subject.group.last_owner?(@subject.user) } + condition(:last_owner) { @subject.group.last_owner?(@subject.user) || @subject.group.last_blocked_owner?(@subject.user) } desc "Membership is users' own" with_score 0 diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index 59a9fb64a36..71cbe28de25 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -253,8 +253,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated nil, nil, { - 'toggle' => 'modal', - 'target' => '#modal-upload-blob' + 'target_branch' => default_branch_or_master, + 'original_branch' => default_branch_or_master, + 'can_push_code' => 'true', + 'path' => project_create_blob_path(project, default_branch_or_master), + 'project_path' => project.path } ) end diff --git a/app/services/packages/maven/metadata/base_create_xml_service.rb b/app/services/packages/maven/metadata/base_create_xml_service.rb new file mode 100644 index 00000000000..4d5cab4978e --- /dev/null +++ b/app/services/packages/maven/metadata/base_create_xml_service.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Packages + module Maven + module Metadata + class BaseCreateXmlService + include Gitlab::Utils::StrongMemoize + + INDENT_SPACE = 2 + + def initialize(metadata_content:, package:) + @metadata_content = metadata_content + @package = package + end + + private + + def xml_doc + strong_memoize(:xml_doc) do + Nokogiri::XML(@metadata_content) do |config| + config.default_xml.noblanks + end + end + end + + def xml_node(name, content) + xml_doc.create_element(name).tap { |e| e.content = content } + end + end + end + end +end diff --git a/app/services/packages/maven/metadata/create_plugins_xml_service.rb b/app/services/packages/maven/metadata/create_plugins_xml_service.rb new file mode 100644 index 00000000000..707a8c577ba --- /dev/null +++ b/app/services/packages/maven/metadata/create_plugins_xml_service.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Packages + module Maven + module Metadata + class CreatePluginsXmlService < BaseCreateXmlService + XPATH_PLUGIN_ARTIFACT_ID = '//plugin/artifactId' + XPATH_PLUGINS = '//metadata/plugins' + EMPTY_PLUGINS_PAYLOAD = { + changes_exist: true, + empty_plugins: true + }.freeze + + def execute + return ServiceResponse.error(message: 'package not set') unless @package + return ServiceResponse.error(message: 'metadata_content not set') unless @metadata_content + return ServiceResponse.error(message: 'metadata_content is invalid') unless plugins_xml_node.present? + return ServiceResponse.success(payload: EMPTY_PLUGINS_PAYLOAD) if plugin_artifact_ids_from_database.empty? + + changes_exist = update_plugins_list + + payload = { changes_exist: changes_exist, empty_versions: false } + payload[:metadata_content] = xml_doc.to_xml(indent: INDENT_SPACE) if changes_exist + + ServiceResponse.success(payload: payload) + end + + private + + def update_plugins_list + return false if plugin_artifact_ids_from_xml == plugin_artifact_ids_from_database + + plugins_xml_node.children.remove + + plugin_artifact_ids_from_database.each do |artifact_id| + plugins_xml_node.add_child(plugin_node_for(artifact_id)) + end + + true + end + + def plugins_xml_node + strong_memoize(:plugins_xml_node) do + xml_doc.xpath(XPATH_PLUGINS) + .first + end + end + + def plugin_artifact_ids_from_xml + strong_memoize(:plugin_artifact_ids_from_xml) do + plugins_xml_node.xpath(XPATH_PLUGIN_ARTIFACT_ID) + .map(&:content) + end + end + + def plugin_artifact_ids_from_database + strong_memoize(:plugin_artifact_ids_from_database) do + package_names = plugin_artifact_ids_from_xml.map do |artifact_id| + "#{@package.name}/#{artifact_id}" + end + + packages = @package.project.packages + .maven + .displayable + .with_name(package_names) + .has_version + + ::Packages::Maven::Metadatum.for_package_ids(packages.select(:id)) + .order_created + .pluck_app_name + .uniq + end + end + + def plugin_node_for(artifact_id) + xml_doc.create_element('plugin').tap do |plugin_node| + plugin_node.add_child(xml_node('name', artifact_id)) + plugin_node.add_child(xml_node('prefix', prefix_from(artifact_id))) + plugin_node.add_child(xml_node('artifactId', artifact_id)) + end + end + + # Maven plugin prefix generation from + # https://github.com/apache/maven/blob/c3dba0e5ba71ee7cbd62620f669a8c206e71b5e2/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/PluginDescriptor.java#L189 + def prefix_from(artifact_id) + artifact_id.gsub(/-?maven-?/, '') + .gsub(/-?plugin-?/, '') + end + end + end + end +end diff --git a/app/services/packages/maven/metadata/create_versions_xml_service.rb b/app/services/packages/maven/metadata/create_versions_xml_service.rb index 6aebc1560c6..13b6efa8650 100644 --- a/app/services/packages/maven/metadata/create_versions_xml_service.rb +++ b/app/services/packages/maven/metadata/create_versions_xml_service.rb @@ -3,9 +3,7 @@ module Packages module Maven module Metadata - class CreateVersionsXmlService - include Gitlab::Utils::StrongMemoize - + class CreateVersionsXmlService < BaseCreateXmlService XPATH_VERSIONING = '//metadata/versioning' XPATH_VERSIONS = '//versions' XPATH_VERSION = '//version' @@ -13,18 +11,11 @@ module Packages XPATH_RELEASE = '//release' XPATH_LAST_UPDATED = '//lastUpdated' - INDENT_SPACE = 2 - EMPTY_VERSIONS_PAYLOAD = { changes_exist: true, empty_versions: true }.freeze - def initialize(metadata_content:, package:) - @metadata_content = metadata_content - @package = package - end - def execute return ServiceResponse.error(message: 'package not set') unless @package return ServiceResponse.error(message: 'metadata_content not set') unless @metadata_content @@ -57,7 +48,7 @@ module Packages version_xml_nodes.remove versions_from_database.each do |version| - versions_xml_node.add_child(version_node_for(version)) + versions_xml_node.add_child(xml_node('version', version)) end true end @@ -131,10 +122,6 @@ module Packages end end - def version_node_for(version) - Nokogiri::XML::Node.new('version', xml_doc).tap { |node| node.content = version } - end - def versions_from_xml strong_memoize(:versions_from_xml) do versions_xml_node.xpath(XPATH_VERSION) @@ -172,14 +159,6 @@ module Packages non_snapshot_versions_from_database.last end end - - def xml_doc - strong_memoize(:xml_doc) do - Nokogiri::XML(@metadata_content) do |config| - config.default_xml.noblanks - end - end - end end end end diff --git a/app/services/packages/maven/metadata/sync_service.rb b/app/services/packages/maven/metadata/sync_service.rb index ab45e30c4f7..a6534aa706d 100644 --- a/app/services/packages/maven/metadata/sync_service.rb +++ b/app/services/packages/maven/metadata/sync_service.rb @@ -16,26 +16,50 @@ module Packages return error('Non existing versionless package') unless versionless_package_for_versions return error('Non existing metadata file for versions') unless metadata_package_file_for_versions + if metadata_package_file_for_plugins + result = update_plugins_xml + + return result if result.error? + end + update_versions_xml end private def update_versions_xml - return error('Metadata file for versions is too big') if metadata_package_file_for_versions.size > MAX_FILE_SIZE + update_xml( + kind: :versions, + package_file: metadata_package_file_for_versions, + service_class: CreateVersionsXmlService, + payload_empty_field: :empty_versions + ) + end - metadata_package_file_for_versions.file.use_open_file do |file| - result = CreateVersionsXmlService.new(metadata_content: file, package: versionless_package_for_versions) - .execute + def update_plugins_xml + update_xml( + kind: :plugins, + package_file: metadata_package_file_for_plugins, + service_class: CreatePluginsXmlService, + payload_empty_field: :empty_plugins + ) + end + + def update_xml(kind:, package_file:, service_class:, payload_empty_field:) + return error("Metadata file for #{kind} is too big") if package_file.size > MAX_FILE_SIZE + + package_file.file.use_open_file do |file| + result = service_class.new(metadata_content: file, package: package_file.package) + .execute next result unless result.success? - next success('No changes for versions xml') unless result.payload[:changes_exist] + next success("No changes for #{kind} xml") unless result.payload[:changes_exist] - if result.payload[:empty_versions] - versionless_package_for_versions.destroy! - success('Versionless package for versions destroyed') + if result.payload[payload_empty_field] + package_file.package.destroy! + success("Versionless package for #{kind} destroyed") else - AppendPackageFileService.new(metadata_content: result.payload[:metadata_content], package: versionless_package_for_versions) + AppendPackageFileService.new(metadata_content: result.payload[:metadata_content], package: package_file.package) .execute end end @@ -43,28 +67,49 @@ module Packages def metadata_package_file_for_versions strong_memoize(:metadata_file_for_versions) do - versionless_package_for_versions.package_files - .with_file_name(Metadata.filename) - .recent - .first + metadata_package_file_for(versionless_package_for_versions) end end def versionless_package_for_versions strong_memoize(:versionless_package_for_versions) do - project.packages - .maven - .displayable - .with_name(package_name) - .with_version(nil) - .first + versionless_package_named(package_name) end end + def metadata_package_file_for_plugins + strong_memoize(:metadata_package_file_for_plugins) do + metadata_package_file_for(versionless_package_named(package_name_for_plugins)) + end + end + + def metadata_package_file_for(package) + return unless package + + package.package_files + .with_file_name(Metadata.filename) + .recent + .first + end + + def versionless_package_named(name) + project.packages + .maven + .displayable + .with_name(name) + .with_version(nil) + .first + end + def package_name params[:package_name] end + def package_name_for_plugins + group = versionless_package_for_versions.maven_metadatum.app_group + group.tr('.', '/') + end + def error(message) ServiceResponse.error(message: message) end diff --git a/app/views/admin/application_settings/_gitpod.html.haml b/app/views/admin/application_settings/_gitpod.html.haml index 531ddcdb86f..48b0c6be0a8 100644 --- a/app/views/admin/application_settings/_gitpod.html.haml +++ b/app/views/admin/application_settings/_gitpod.html.haml @@ -7,7 +7,7 @@ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' } = expanded ? _('Collapse') : _('Expand') %p - %integration-help-text{ "id" => "js-gitpod-settings-help-text", "message" => gitpod_enable_description, "message-url" => "https://gitpod.io/" } + #js-gitpod-settings-help-text{ data: {"message" => gitpod_enable_description, "message-url" => "https://gitpod.io/" } } = link_to sprite_icon('question-o'), help_page_path('integration/gitpod.md'), target: '_blank', class: 'has-tooltip', title: _('More information') diff --git a/app/views/admin/labels/destroy.js.haml b/app/views/admin/labels/destroy.js.haml index b9b63829f25..5ee53088230 100644 --- a/app/views/admin/labels/destroy.js.haml +++ b/app/views/admin/labels/destroy.js.haml @@ -1,2 +1,3 @@ - if @labels.size == 0 - $('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000) + var emptyState = document.querySelector('.labels .nothing-here-block.hidden'); + if (emptyState) emptyState.classList.remove('hidden'); diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index 7b737c49a87..6861a802a63 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -13,5 +13,6 @@ = render @labels = paginate @labels, theme: 'gitlab' - - else - .nothing-here-block= _('There are no labels yet') + + .nothing-here-block{ class: ('hidden' if @labels.present?) } + = _('There are no labels yet') diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 5f0ecb2ee79..52cf0248f21 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -10,6 +10,8 @@ %ul.controls %li + = snippet_file_count(snippet) + %li = link_to gitlab_snippet_path(snippet, anchor: 'notes'), class: ('no-comments' if notes_count == 0) do = sprite_icon('comments', css_class: 'gl-vertical-align-text-bottom') = notes_count |