diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-03 21:10:10 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-03 21:10:10 +0300 |
commit | c2a6cc86754adb3c5e064cebc58d206a52cb412e (patch) | |
tree | 3960c9ae2590e89e25193a0006e84d06f900e378 /app | |
parent | bbd9e2c915c46920ceb51376db19599cbf9ba836 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
36 files changed, 347 insertions, 168 deletions
diff --git a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue index 06f436adb8e..6fee40fb061 100644 --- a/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue +++ b/app/assets/javascripts/blob/suggest_gitlab_ci_yml/components/popover.vue @@ -107,7 +107,7 @@ export default { v-if="!popoverDismissed" show :target="target" - placement="rightbottom" + placement="right" trigger="manual" container="viewport" :css-classes="['suggest-gitlab-ci-yml', 'ml-4']" diff --git a/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql new file mode 100644 index 00000000000..b64ceb8e2c9 --- /dev/null +++ b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql @@ -0,0 +1,9 @@ +#import "../fragments/user.fragment.graphql" + +query usersSearch($search: String!) { + users(search: $search) { + nodes { + ...User + } + } +} diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 4d2955a8d3d..ab83f1ecc14 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import ContextualSidebar from './contextual_sidebar'; import initFlyOutNav from './fly_out_nav'; +import { setNotification } from './whats_new/utils/notification'; function hideEndFade($scrollingTabs) { $scrollingTabs.each(function scrollTabsLoop() { @@ -14,25 +15,17 @@ function hideEndFade($scrollingTabs) { function initDeferred() { $(document).trigger('init.scrolling-tabs'); - const whatsNewTriggerEl = document.querySelector('.js-whats-new-trigger'); - if (whatsNewTriggerEl) { - const storageKey = whatsNewTriggerEl.getAttribute('data-storage-key'); + const appEl = document.getElementById('whats-new-app'); + if (!appEl) return; - $('.header-help').on('show.bs.dropdown', () => { - const displayNotification = JSON.parse(localStorage.getItem(storageKey)); - if (displayNotification === false) { - $('.js-whats-new-notification-count').remove(); - } - }); - - whatsNewTriggerEl.addEventListener('click', () => { - import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new') - .then(({ default: initWhatsNew }) => { - initWhatsNew(); - }) - .catch(() => {}); - }); - } + setNotification(appEl); + document.querySelector('.js-whats-new-trigger').addEventListener('click', () => { + import(/* webpackChunkName: 'whatsNewApp' */ '~/whats_new') + .then(({ default: initWhatsNew }) => { + initWhatsNew(appEl); + }) + .catch(() => {}); + }); } export default function initLayoutNav() { diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index fe4e2cee69f..344f8dee5ea 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -102,14 +102,6 @@ MergeRequest.prototype.initMRBtnListeners = function() { return $('.btn-close, .btn-reopen').on('click', function(e) { const $this = $(this); const shouldSubmit = $this.hasClass('btn-comment'); - if ($this.hasClass('js-btn-issue-action')) { - const url = $this.data('endpoint'); - return axios - .put(url) - .then(() => window.location.reload()) - .catch(() => createFlash(__('Something went wrong.'))); - } - if (shouldSubmit && $this.data('submitted')) { return; } @@ -171,10 +163,6 @@ MergeRequest.decreaseCounter = function(by = 1) { MergeRequest.hideCloseButton = function() { const el = document.querySelector('.merge-request .js-issuable-actions'); - const closeDropdownItem = el.querySelector('li.close-item'); - if (closeDropdownItem) { - closeDropdownItem.classList.add('hidden'); - } // Dropdown for mobile screen el.querySelector('li.js-close-item').classList.add('hidden'); }; diff --git a/app/assets/javascripts/notes/components/multiline_comment_utils.js b/app/assets/javascripts/notes/components/multiline_comment_utils.js index dbae10c8f6c..2451400e980 100644 --- a/app/assets/javascripts/notes/components/multiline_comment_utils.js +++ b/app/assets/javascripts/notes/components/multiline_comment_utils.js @@ -103,9 +103,15 @@ export function getCommentedLines(selectedCommentPosition, diffLines) { }; } + const findLineCodeIndex = line => position => { + return [position.line_code, position.left?.line_code, position.right?.line_code].includes( + line.line_code, + ); + }; + const { start, end } = selectedCommentPosition; - const startLine = diffLines.findIndex(l => l.line_code === start.line_code); - const endLine = diffLines.findIndex(l => l.line_code === end.line_code); + const startLine = diffLines.findIndex(findLineCodeIndex(start)); + const endLine = diffLines.findIndex(findLineCodeIndex(end)); return { startLine, endLine }; } diff --git a/app/assets/javascripts/whats_new/index.js b/app/assets/javascripts/whats_new/index.js index a57c9718156..2b9e7a2815e 100644 --- a/app/assets/javascripts/whats_new/index.js +++ b/app/assets/javascripts/whats_new/index.js @@ -1,26 +1,34 @@ import Vue from 'vue'; +import { mapState } from 'vuex'; import App from './components/app.vue'; import store from './store'; +import { getStorageKey, setNotification } from './utils/notification'; let whatsNewApp; -export default () => { +export default el => { if (whatsNewApp) { store.dispatch('openDrawer'); } else { - const whatsNewElm = document.getElementById('whats-new-app'); + const storageKey = getStorageKey(el); whatsNewApp = new Vue({ - el: whatsNewElm, + el, store, components: { App, }, + computed: { + ...mapState(['open']), + }, + watch: { + open() { + setNotification(el); + }, + }, render(createElement) { return createElement('app', { - props: { - storageKey: whatsNewElm.getAttribute('data-storage-key'), - }, + props: { storageKey }, }); }, }); diff --git a/app/assets/javascripts/whats_new/utils/notification.js b/app/assets/javascripts/whats_new/utils/notification.js new file mode 100644 index 00000000000..f261a089554 --- /dev/null +++ b/app/assets/javascripts/whats_new/utils/notification.js @@ -0,0 +1,17 @@ +export const getStorageKey = appEl => appEl.getAttribute('data-storage-key'); + +export const setNotification = appEl => { + const storageKey = getStorageKey(appEl); + const notificationEl = document.querySelector('.header-help'); + let notificationCountEl = notificationEl.querySelector('.js-whats-new-notification-count'); + + if (JSON.parse(localStorage.getItem(storageKey)) === false) { + notificationEl.classList.remove('with-notifications'); + if (notificationCountEl) { + notificationCountEl.parentElement.removeChild(notificationCountEl); + notificationCountEl = null; + } + } else { + notificationEl.classList.add('with-notifications'); + } +}; diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 196fb3a7088..a93c70c75d3 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -103,7 +103,8 @@ @include transition(color); } -a { +a, +.notification-dot { @include transition(background-color, color, border); } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 52319d9658b..0286c2f517b 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -556,12 +556,17 @@ border: 1px solid $gray-normal; } -.header-user-notification-dot { +.notification-dot { background-color: $orange-300; height: 12px; width: 12px; - right: 8px; - top: -8px; + margin-top: -15px; + pointer-events: none; + visibility: hidden; +} + +.with-notifications .notification-dot { + visibility: visible; } .with-performance-bar .navbar-gitlab { diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index f357d508d5d..f237d57aa88 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -41,12 +41,6 @@ @include media-breakpoint-down(xs) { width: 100%; margin-top: 10px; - - > .issue-btn-group { - > .btn { - width: 100%; - } - } } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 57725aa3002..e5528c25e82 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -890,24 +890,6 @@ } } -.issuable-close-dropdown { - .dropdown-menu { - min-width: 270px; - left: auto; - right: 0; - } - - .description { - .text { - margin: 0; - } - } - - .dropdown-toggle > .icon { - margin: 0 3px; - } -} - /* * Following overrides are done to prevent * legacy dropdown styles from influencing diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss index 85115cfd5d9..417377b514e 100644 --- a/app/assets/stylesheets/themes/theme_helper.scss +++ b/app/assets/stylesheets/themes/theme_helper.scss @@ -64,14 +64,20 @@ color: $search-and-nav-links; > a { + .notification-dot { + border: 2px solid $nav-svg-color; + } + + &.header-help-dropdown-toggle { + .notification-dot { + background-color: $search-and-nav-links; + } + } + &.header-user-dropdown-toggle { .header-user-avatar { border-color: $search-and-nav-links; } - - .header-user-notification-dot { - border: 2px solid $nav-svg-color; - } } &:hover, @@ -84,9 +90,14 @@ fill: currentColor; } - &.header-user-dropdown-toggle .header-user-notification-dot { + .notification-dot { + will-change: border-color, background-color; border-color: $nav-svg-color + 33; } + + &.header-help-dropdown-toggle .notification-dot { + background-color: $white; + } } } @@ -101,9 +112,15 @@ } } - &.header-user-dropdown-toggle .header-user-notification-dot { + .notification-dot { border-color: $white; } + + &.header-help-dropdown-toggle { + .notification-dot { + background-color: $nav-svg-color; + } + } } .impersonated-user, diff --git a/app/controllers/concerns/dependency_proxy/auth.rb b/app/controllers/concerns/dependency_proxy/auth.rb new file mode 100644 index 00000000000..22618ca6366 --- /dev/null +++ b/app/controllers/concerns/dependency_proxy/auth.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module DependencyProxy + module Auth + extend ActiveSupport::Concern + + included do + # We disable `authenticate_user!` since the `DependencyProxy::Auth` performs auth using JWT token + skip_before_action :authenticate_user!, raise: false + prepend_before_action :authenticate_user_from_jwt_token! + end + + def authenticate_user_from_jwt_token! + return unless dependency_proxy_for_private_groups? + + authenticate_with_http_token do |token, _| + user = user_from_token(token) + sign_in(user) if user + end + + request_bearer_token! unless current_user + end + + private + + def dependency_proxy_for_private_groups? + Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: false) + end + + def request_bearer_token! + # unfortunately, we cannot use https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html#method-i-authentication_request + response.headers['WWW-Authenticate'] = ::DependencyProxy::Registry.authenticate_header + render plain: '', status: :unauthorized + end + + def user_from_token(token) + token_payload = DependencyProxy::AuthTokenService.decoded_token_payload(token) + User.find(token_payload['user_id']) + rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature + nil + end + end +end diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb new file mode 100644 index 00000000000..2a923d02752 --- /dev/null +++ b/app/controllers/concerns/dependency_proxy/group_access.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module DependencyProxy + module GroupAccess + extend ActiveSupport::Concern + + included do + before_action :verify_dependency_proxy_enabled! + before_action :authorize_read_dependency_proxy! + end + + private + + def verify_dependency_proxy_enabled! + render_404 unless group.dependency_proxy_feature_available? + end + + def authorize_read_dependency_proxy! + access_denied! unless can?(current_user, :read_dependency_proxy, group) + end + + def authorize_admin_dependency_proxy! + access_denied! unless can?(current_user, :admin_dependency_proxy, group) + end + end +end diff --git a/app/controllers/concerns/dependency_proxy_access.rb b/app/controllers/concerns/dependency_proxy_access.rb deleted file mode 100644 index 5036d0cfce4..00000000000 --- a/app/controllers/concerns/dependency_proxy_access.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module DependencyProxyAccess - extend ActiveSupport::Concern - - included do - before_action :verify_dependency_proxy_enabled! - before_action :authorize_read_dependency_proxy! - end - - private - - def verify_dependency_proxy_enabled! - render_404 unless group.dependency_proxy_feature_available? - end - - def authorize_read_dependency_proxy! - access_denied! unless can?(current_user, :read_dependency_proxy, group) - end - - def authorize_admin_dependency_proxy! - access_denied! unless can?(current_user, :admin_dependency_proxy, group) - end -end diff --git a/app/controllers/groups/dependency_proxies_controller.rb b/app/controllers/groups/dependency_proxies_controller.rb index 367dbafdd59..b896b240daf 100644 --- a/app/controllers/groups/dependency_proxies_controller.rb +++ b/app/controllers/groups/dependency_proxies_controller.rb @@ -2,7 +2,7 @@ module Groups class DependencyProxiesController < Groups::ApplicationController - include DependencyProxyAccess + include DependencyProxy::GroupAccess before_action :authorize_admin_dependency_proxy!, only: :update before_action :dependency_proxy diff --git a/app/controllers/groups/dependency_proxy_auth_controller.rb b/app/controllers/groups/dependency_proxy_auth_controller.rb new file mode 100644 index 00000000000..e3e9bd88e24 --- /dev/null +++ b/app/controllers/groups/dependency_proxy_auth_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Groups::DependencyProxyAuthController < ApplicationController + include DependencyProxy::Auth + + feature_category :dependency_proxy + + def authenticate + render plain: '', status: :ok + end +end diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb index f46902ef90f..22aea424998 100644 --- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb +++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class Groups::DependencyProxyForContainersController < Groups::ApplicationController - include DependencyProxyAccess + include DependencyProxy::Auth + include DependencyProxy::GroupAccess include SendFileUpload before_action :ensure_token_granted! @@ -9,7 +10,7 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro attr_reader :token - feature_category :package_registry + feature_category :dependency_proxy def manifest result = DependencyProxy::PullManifestService.new(image, tag, token).execute diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 5199bb25c8c..85ee2204324 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -11,7 +11,8 @@ class JwtController < ApplicationController feature_category :authentication_and_authorization SERVICES = { - Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService + ::Auth::ContainerRegistryAuthenticationService::AUDIENCE => ::Auth::ContainerRegistryAuthenticationService, + ::Auth::DependencyProxyAuthenticationService::AUDIENCE => ::Auth::DependencyProxyAuthenticationService }.freeze def auth diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb index 7f04927f517..f1d4f9b2cc0 100644 --- a/app/controllers/profiles/gpg_keys_controller.rb +++ b/app/controllers/profiles/gpg_keys_controller.rb @@ -2,6 +2,7 @@ class Profiles::GpgKeysController < Profiles::ApplicationController before_action :set_gpg_key, only: [:destroy, :revoke] + skip_before_action :authenticate_user!, only: [:get_keys] feature_category :users @@ -39,6 +40,24 @@ class Profiles::GpgKeysController < Profiles::ApplicationController end end + # Get all gpg keys of a user(params[:username]) in a text format + def get_keys + if params[:username].present? + begin + user = UserFinder.new(params[:username]).find_by_username + if user.present? + render plain: user.gpg_keys.select(&:verified?).map(&:key).join("\n") + else + render_404 + end + rescue => e + render html: e.message + end + else + render_404 + end + end + private def gpg_key_params diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 1f998e0083a..4610756192a 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -15,7 +15,7 @@ class Projects::JobsController < Projects::ApplicationController before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize before_action :verify_proxy_request!, only: :proxy_websocket_authorize before_action do - push_frontend_feature_flag(:ci_job_line_links, @project) + push_frontend_feature_flag(:ci_job_line_links, @project, default_enabled: true) end before_action only: :index do frontend_experimentation_tracking_data(:jobs_empty_state, 'click_button') diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb index 639e403d797..3cf0a23b7f6 100644 --- a/app/controllers/repositories/git_http_controller.rb +++ b/app/controllers/repositories/git_http_controller.rb @@ -80,6 +80,8 @@ module Repositories return if Gitlab::Database.read_only? return unless repo_type.project? + OnboardingProgressService.new(project.namespace).execute(action: :git_read) + if Feature.enabled?(:project_statistics_sync, project, default_enabled: true) Projects::FetchStatisticsIncrementService.new(project).execute else diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 1f289265916..547977c01a9 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -319,12 +319,6 @@ module IssuablesHelper issuable_path(issuable, close_reopen_params(issuable, :reopen)) end - def toggle_draft_issuable_path(issuable) - wip_event = issuable.work_in_progress? ? 'unwip' : 'wip' - - issuable_path(issuable, { merge_request: { wip_event: wip_event } }) - end - def issuable_path(issuable, *options) polymorphic_path(issuable, *options) end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 9cb7edbaeb6..35ceddada5f 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -39,19 +39,6 @@ module MergeRequestsHelper end end - def ci_build_details_path(merge_request) - build_url = merge_request.source_project.ci_service.build_page(merge_request.diff_head_sha, merge_request.source_branch) - return unless build_url - - parsed_url = URI.parse(build_url) - - unless parsed_url.userinfo.blank? - parsed_url.userinfo = '' - end - - parsed_url.to_s - end - def merge_path_description(merge_request, separator) if merge_request.for_fork? "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}" @@ -166,6 +153,12 @@ module MergeRequestsHelper current_user.fork_of(project) end end + + def toggle_draft_merge_request_path(issuable) + wip_event = issuable.work_in_progress? ? 'unwip' : 'wip' + + issuable_path(issuable, { merge_request: { wip_event: wip_event } }) + end end MergeRequestsHelper.prepend_if_ee('EE::MergeRequestsHelper') diff --git a/app/models/dependency_proxy/registry.rb b/app/models/dependency_proxy/registry.rb index 471d5be2600..6492acf325a 100644 --- a/app/models/dependency_proxy/registry.rb +++ b/app/models/dependency_proxy/registry.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true class DependencyProxy::Registry - AUTH_URL = 'https://auth.docker.io'.freeze - LIBRARY_URL = 'https://registry-1.docker.io/v2'.freeze + AUTH_URL = 'https://auth.docker.io' + LIBRARY_URL = 'https://registry-1.docker.io/v2' + PROXY_AUTH_URL = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, "jwt/auth") class << self def auth_url(image) @@ -17,6 +18,10 @@ class DependencyProxy::Registry "#{LIBRARY_URL}/#{image_path(image)}/blobs/#{blob_sha}" end + def authenticate_header + "Bearer realm=\"#{PROXY_AUTH_URL}\",service=\"#{::Auth::DependencyProxyAuthenticationService::AUDIENCE}\"" + end + private def image_path(image) diff --git a/app/models/namespace_onboarding_action.rb b/app/models/namespace_onboarding_action.rb index 7c3ab051d93..e1121279e2e 100644 --- a/app/models/namespace_onboarding_action.rb +++ b/app/models/namespace_onboarding_action.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true class NamespaceOnboardingAction < ApplicationRecord - belongs_to :namespace + belongs_to :namespace, optional: false + + validates :action, presence: true ACTIONS = { - subscription_created: 1 + subscription_created: 1, + git_write: 2, + git_read: 4 }.freeze enum action: ACTIONS diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb index 9674f9a41da..ef799b01452 100644 --- a/app/models/user_detail.rb +++ b/app/models/user_detail.rb @@ -31,3 +31,5 @@ class UserDetail < ApplicationRecord self.bio = '' if bio_changed? && bio.nil? end end + +UserDetail.prepend_if_ee('EE::UserDetail') diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb new file mode 100644 index 00000000000..1b8c16b7c79 --- /dev/null +++ b/app/services/auth/dependency_proxy_authentication_service.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Auth + class DependencyProxyAuthenticationService < BaseService + AUDIENCE = 'dependency_proxy' + HMAC_KEY = 'gitlab-dependency-proxy' + DEFAULT_EXPIRE_TIME = 1.minute + + def execute(authentication_abilities:) + return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled + return error('access forbidden', 403) unless current_user + + { token: authorized_token.encoded } + end + + class << self + include ::Gitlab::Utils::StrongMemoize + + def secret + strong_memoize(:secret) do + OpenSSL::HMAC.hexdigest( + 'sha256', + ::Settings.attr_encrypted_db_key_base, + HMAC_KEY + ) + end + end + + def token_expire_at + Time.current + Gitlab::CurrentSettings.container_registry_token_expire_delay.minutes + end + end + + private + + def authorized_token + JSONWebToken::HMACToken.new(self.class.secret).tap do |token| + token['user_id'] = current_user.id + token.expire_time = self.class.token_expire_at + end + end + end +end diff --git a/app/services/dependency_proxy/auth_token_service.rb b/app/services/dependency_proxy/auth_token_service.rb new file mode 100644 index 00000000000..16279ed12b0 --- /dev/null +++ b/app/services/dependency_proxy/auth_token_service.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module DependencyProxy + class AuthTokenService < DependencyProxy::BaseService + attr_reader :token + + def initialize(token) + @token = token + end + + def execute + JSONWebToken::HMACToken.decode(token, ::Auth::DependencyProxyAuthenticationService.secret).first + end + + class << self + def decoded_token_payload(token) + self.new(token).execute + end + end + end +end diff --git a/app/services/onboarding_progress_service.rb b/app/services/onboarding_progress_service.rb new file mode 100644 index 00000000000..c45edcaaf33 --- /dev/null +++ b/app/services/onboarding_progress_service.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class OnboardingProgressService + def initialize(namespace) + @namespace = namespace + end + + def execute(action:) + NamespaceOnboardingAction.create_action(@namespace, action) + end +end diff --git a/app/services/post_receive_service.rb b/app/services/post_receive_service.rb index 79b613f6a88..bd9588844ad 100644 --- a/app/services/post_receive_service.rb +++ b/app/services/post_receive_service.rb @@ -40,6 +40,8 @@ class PostReceiveService response.add_basic_message(redirect_message) response.add_basic_message(project_created_message) + + record_onboarding_progress end response @@ -90,6 +92,10 @@ class PostReceiveService banner&.message end + + def record_onboarding_progress + NamespaceOnboardingAction.create_action(project.namespace, :git_write) + end end PostReceiveService.prepend_if_ee('EE::PostReceiveService') diff --git a/app/views/groups/dependency_proxies/show.html.haml b/app/views/groups/dependency_proxies/show.html.haml index ff1312eb763..abbfb2d3b91 100644 --- a/app/views/groups/dependency_proxies/show.html.haml +++ b/app/views/groups/dependency_proxies/show.html.haml @@ -7,7 +7,7 @@ - link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('user/packages/dependency_proxy/index') } = _('Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } -- if @group.public? +- if Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: false) || @group.public? - if can?(current_user, :admin_dependency_proxy, @group) = form_for(@dependency_proxy, method: :put, url: group_dependency_proxy_path(@group)) do |f| .form-group diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 794d1589172..8aba9426ec0 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -74,6 +74,7 @@ %span.gl-sr-only = s_('Nav|Help') = sprite_icon('question') + %span.notification-dot.rounded-circle.gl-absolute = sprite_icon('chevron-down', css_class: 'caret-down') .dropdown-menu.dropdown-menu-right = render 'layouts/header/help_dropdown' diff --git a/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml new file mode 100644 index 00000000000..3a8629b3b6e --- /dev/null +++ b/app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml @@ -0,0 +1,37 @@ +- display_issuable_type = issuable_display_type(@merge_request) +- button_action_class = @merge_request.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary' +- button_class = "btn gl-button #{!@merge_request.closed? && 'js-draft-toggle-button'}" +- toggle_class = "btn gl-button dropdown-toggle" + +.float-left.btn-group.gl-ml-3.gl-display-none.gl-display-md-flex + = link_to @merge_request.closed? ? reopen_issuable_path(@merge_request) : toggle_draft_merge_request_path(@merge_request), method: :put, class: "#{button_class} #{button_action_class}" do + - if @merge_request.closed? + = _('Reopen') + = display_issuable_type + - else + = @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft') + + - if !@merge_request.closed? || !issuable_author_is_current_user(@merge_request) + = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do + %span.gl-sr-only= _('Toggle dropdown') + = sprite_icon "angle-down", size: 12 + + %ul.dropdown-menu.dropdown-menu-right + - if @merge_request.open? + %li + = link_to close_issuable_path(@merge_request), method: :put do + .description + %strong.title + = _('Close') + = display_issuable_type + + - unless issuable_author_is_current_user(@merge_request) + - unless @merge_request.closed? + %li.divider.droplab-item-ignore + + %li + %a{ href: new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) } + .description + %strong.title= _('Report abuse') + %p.text.gl-mb-0 + = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize } diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml index 5b30b6e3379..f7cc15cec5a 100644 --- a/app/views/projects/merge_requests/_mr_title.html.haml +++ b/app/views/projects/merge_requests/_mr_title.html.haml @@ -25,8 +25,8 @@ = sprite_icon('chevron-double-lg-left') .detail-page-header-actions.js-issuable-actions - .clearfix.issue-btn-group.dropdown - %button.gl-button.btn.btn-default.float-left.gl-display-md-none{ type: "button", data: { toggle: "dropdown" } } + .clearfix.dropdown + %button.gl-button.btn.btn-default.float-left.gl-display-md-none.gl-w-full{ type: "button", data: { toggle: "dropdown" } } Options = sprite_icon('chevron-down', css_class: 'gl-text-gray-500') .dropdown-menu.dropdown-menu-right @@ -35,12 +35,12 @@ %li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) - if @merge_request.opened? %li - = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_issuable_path(@merge_request), method: :put, class: "js-draft-toggle-button" + = link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_merge_request_path(@merge_request), method: :put, class: "js-draft-toggle-button" %li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] } = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request' - if can_reopen_merge_request %li{ class: merge_request_button_visibility(@merge_request, false) } - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request' + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, title: 'Reopen merge request' - unless @merge_request.merged? || current_user == @merge_request.author %li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)) @@ -48,6 +48,6 @@ = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-md-block btn gl-button btn-grouped js-issuable-edit qa-edit-button" - if can_update_merge_request && !are_close_and_open_buttons_hidden - = render 'shared/issuable/close_reopen_draft_report_toggle', issuable: @merge_request + = render 'projects/merge_requests/close_reopen_draft_report_toggle' - elsif !@merge_request.merged? = link_to _('Report abuse'), new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request)), class: 'gl-display-none gl-display-md-block gl-button btn btn-warning-secondary float-right gl-ml-3', title: _('Report abuse') diff --git a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml deleted file mode 100644 index 250e1516318..00000000000 --- a/app/views/shared/issuable/_close_reopen_draft_report_toggle.html.haml +++ /dev/null @@ -1,37 +0,0 @@ -- display_issuable_type = issuable_display_type(issuable) -- button_action_class = issuable.closed? ? 'btn-default' : 'btn-warning btn-warning-secondary' -- button_class = "btn gl-button #{!issuable.closed? && 'js-draft-toggle-button'}" -- toggle_class = "btn gl-button dropdown-toggle" - -.float-left.btn-group.gl-ml-3.issuable-close-dropdown.d-none.d-md-inline-flex.js-issuable-close-dropdown - = link_to issuable.closed? ? reopen_issuable_path(issuable) : toggle_draft_issuable_path(issuable), method: :put, class: "#{button_class} #{button_action_class}" do - - if issuable.closed? - = _('Reopen') - = display_issuable_type - - else - = issuable.work_in_progress? ? _('Mark as ready') : _('Mark as draft') - - - if !issuable.closed? || !issuable_author_is_current_user(issuable) - = button_tag type: 'button', class: "#{toggle_class} #{button_action_class}", data: { 'toggle' => 'dropdown' } do - %span.sr-only= _('Toggle dropdown') - = sprite_icon "angle-down", size: 12 - - %ul.js-issuable-close-menu.dropdown-menu.dropdown-menu-right - - if issuable.open? - %li - = link_to close_issuable_path(issuable), method: :put do - .description - %strong.title - = _('Close') - = display_issuable_type - - - unless issuable_author_is_current_user(issuable) - - unless issuable.closed? - %li.divider.droplab-item-ignore - - %li.report-item - %a.report-abuse-link{ href: new_abuse_report_path(user_id: issuable.author.id, ref_url: merge_request_url(issuable)) } - .description - %strong.title= _('Report abuse') - %p.text - = _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize } |