Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-16 12:09:36 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-16 12:09:36 +0300
commita57cec4bb89b61d210d4e413571b1d85d76179f6 (patch)
treec7868456df33c1849a7ff5037351bfd014a80584 /app
parent1de9854406851f7f1b599dd3311189f16db422f3 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js44
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_a.vue27
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue27
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js12
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/index/index.js25
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js83
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue8
-rw-r--r--app/controllers/concerns/boards_responses.rb6
-rw-r--r--app/controllers/projects/learn_gitlab_controller.rb19
-rw-r--r--app/controllers/projects/settings/repository_controller.rb1
-rw-r--r--app/helpers/learn_gitlab_helper.rb60
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/models/clusters/applications/cert_manager.rb2
-rw-r--r--app/models/clusters/applications/crossplane.rb2
-rw-r--r--app/models/clusters/applications/ingress.rb2
-rw-r--r--app/models/clusters/applications/jupyter.rb2
-rw-r--r--app/models/clusters/applications/knative.rb2
-rw-r--r--app/models/onboarding_progress.rb4
-rw-r--r--app/models/protected_branch/push_access_level.rb2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml7
-rw-r--r--app/views/projects/learn_gitlab/index.html.haml4
-rw-r--r--app/views/projects/protected_branches/_create_protected_branch.html.haml4
-rw-r--r--app/views/shared/projects/protected_branches/_update_protected_branch.html.haml4
23 files changed, 291 insertions, 58 deletions
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 0b920ba8e7a..cc2cf787a8f 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -16,6 +16,50 @@ function decodeUrlParameter(val) {
return decodeURIComponent(val.replace(/\+/g, '%20'));
}
+/**
+ * Safely encodes a string to be used as a path
+ *
+ * Note: This function DOES encode typical URL parts like ?, =, &, #, and +
+ * If you need to use search parameters or URL fragments, they should be
+ * added AFTER calling this function, not before.
+ *
+ * Usecase: An image filename is stored verbatim, and you need to load it in
+ * the browser.
+ *
+ * Example: /some_path/file #1.jpg ==> /some_path/file%20%231.jpg
+ * Example: /some-path/file! Final!.jpg ==> /some-path/file%21%20Final%21.jpg
+ *
+ * Essentially, if a character *could* present a problem in a URL, it's escaped
+ * to the hexadecimal representation instead. This means it's a bit more
+ * aggressive than encodeURIComponent: that built-in function doesn't
+ * encode some characters that *could* be problematic, so this function
+ * adds them (#, !, ~, *, ', (, and )).
+ * Additionally, encodeURIComponent *does* encode `/`, but we want safer
+ * URLs, not non-functional URLs, so this function DEcodes slashes ('%2F').
+ *
+ * @param {String} potentiallyUnsafePath
+ * @returns {String}
+ */
+export function encodeSaferUrl(potentiallyUnsafePath) {
+ const unencode = ['%2F'];
+ const encode = ['#', '!', '~', '\\*', "'", '\\(', '\\)'];
+ let saferPath = encodeURIComponent(potentiallyUnsafePath);
+
+ unencode.forEach((code) => {
+ saferPath = saferPath.replace(new RegExp(code, 'g'), decodeURIComponent(code));
+ });
+ encode.forEach((code) => {
+ const encodedValue = code
+ .codePointAt(code.length - 1)
+ .toString(16)
+ .toUpperCase();
+
+ saferPath = saferPath.replace(new RegExp(code, 'g'), `%${encodedValue}`);
+ });
+
+ return saferPath;
+}
+
export function cleanLeadingSeparator(path) {
return path.replace(PATH_SEPARATOR_LEADING_REGEX, '');
}
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_a.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_a.vue
new file mode 100644
index 00000000000..0393793bfe1
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_a.vue
@@ -0,0 +1,27 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import { ACTION_TEXT } from '../constants';
+
+export default {
+ components: { GlLink },
+ i18n: {
+ ACTION_TEXT,
+ },
+ props: {
+ actions: {
+ required: true,
+ type: Object,
+ },
+ },
+};
+</script>
+<template>
+ <ul>
+ <li v-for="(value, action) in actions" :key="action">
+ <span v-if="value.completed">{{ $options.i18n.ACTION_TEXT[action] }}</span>
+ <span v-else>
+ <gl-link :href="value.url">{{ $options.i18n.ACTION_TEXT[action] }}</gl-link>
+ </span>
+ </li>
+ </ul>
+</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue
new file mode 100644
index 00000000000..0393793bfe1
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_b.vue
@@ -0,0 +1,27 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import { ACTION_TEXT } from '../constants';
+
+export default {
+ components: { GlLink },
+ i18n: {
+ ACTION_TEXT,
+ },
+ props: {
+ actions: {
+ required: true,
+ type: Object,
+ },
+ },
+};
+</script>
+<template>
+ <ul>
+ <li v-for="(value, action) in actions" :key="action">
+ <span v-if="value.completed">{{ $options.i18n.ACTION_TEXT[action] }}</span>
+ <span v-else>
+ <gl-link :href="value.url">{{ $options.i18n.ACTION_TEXT[action] }}</gl-link>
+ </span>
+ </li>
+ </ul>
+</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
new file mode 100644
index 00000000000..8606af29785
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
@@ -0,0 +1,12 @@
+import { s__ } from '~/locale';
+
+export const ACTION_TEXT = {
+ gitWrite: s__('LearnGitLab|Create a repository'),
+ userAdded: s__('LearnGitLab|Invite your colleagues'),
+ pipelineCreated: s__('LearnGitLab|Set-up CI/CD'),
+ trialStarted: s__('LearnGitLab|Start a free trial of GitLab Gold'),
+ codeOwnersEnabled: s__('LearnGitLab|Add code owners'),
+ requiredMrApprovalsEnabled: s__('LearnGitLab|Enable require merge approvals'),
+ mergeRequestCreated: s__('LearnGitLab|Submit a merge request (MR)'),
+ securityScanEnabled: s__('LearnGitLab|Run a Security scan using CI/CD'),
+};
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
new file mode 100644
index 00000000000..c4dec89b984
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import LearnGitlabA from '../components/learn_gitlab_a.vue';
+import LearnGitlabB from '../components/learn_gitlab_b.vue';
+
+function initLearnGitlab() {
+ const el = document.getElementById('js-learn-gitlab-app');
+
+ if (!el) {
+ return false;
+ }
+
+ const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions));
+
+ const { learnGitlabA } = gon.experiments;
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(learnGitlabA ? LearnGitlabA : LearnGitlabB, { props: { actions } });
+ },
+ });
+}
+
+initLearnGitlab();
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index 209ae9927cf..a5e53ee3927 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -11,7 +11,6 @@ export default class AccessDropdown {
const { $dropdown, accessLevel, accessLevelsData, hasLicense = true } = options;
this.options = options;
this.hasLicense = hasLicense;
- this.deployKeysOnProtectedBranchesEnabled = gon.features.deployKeysOnProtectedBranches;
this.groups = [];
this.accessLevel = accessLevel;
this.accessLevelsData = accessLevelsData.roles;
@@ -330,11 +329,7 @@ export default class AccessDropdown {
);
})
.catch(() => {
- if (this.deployKeysOnProtectedBranchesEnabled) {
- createFlash({ message: __('Failed to load groups, users and deploy keys.') });
- } else {
- createFlash({ message: __('Failed to load groups & users.') });
- }
+ createFlash({ message: __('Failed to load groups, users and deploy keys.') });
});
} else {
this.getDeployKeys(query)
@@ -445,35 +440,33 @@ export default class AccessDropdown {
}
}
- if (this.deployKeysOnProtectedBranchesEnabled) {
- const deployKeys = deployKeysResponse.map((response) => {
- const {
- id,
- fingerprint,
- title,
- owner: { avatar_url, name, username },
- } = response;
-
- const shortFingerprint = `(${fingerprint.substring(0, 14)}...)`;
-
- return {
- id,
- title: title.concat(' ', shortFingerprint),
- avatar_url,
- fullname: name,
- username,
- type: LEVEL_TYPES.DEPLOY_KEY,
- };
- });
+ const deployKeys = deployKeysResponse.map((response) => {
+ const {
+ id,
+ fingerprint,
+ title,
+ owner: { avatar_url, name, username },
+ } = response;
+
+ const shortFingerprint = `(${fingerprint.substring(0, 14)}...)`;
+
+ return {
+ id,
+ title: title.concat(' ', shortFingerprint),
+ avatar_url,
+ fullname: name,
+ username,
+ type: LEVEL_TYPES.DEPLOY_KEY,
+ };
+ });
- if (this.accessLevel === ACCESS_LEVELS.PUSH) {
- if (deployKeys.length) {
- consolidatedData = consolidatedData.concat(
- [{ type: 'divider' }],
- [{ type: 'header', content: s__('AccessDropdown|Deploy Keys') }],
- deployKeys,
- );
- }
+ if (this.accessLevel === ACCESS_LEVELS.PUSH) {
+ if (deployKeys.length) {
+ consolidatedData = consolidatedData.concat(
+ [{ type: 'divider' }],
+ [{ type: 'header', content: s__('AccessDropdown|Deploy Keys') }],
+ deployKeys,
+ );
}
}
@@ -501,19 +494,15 @@ export default class AccessDropdown {
}
getDeployKeys(query) {
- if (this.deployKeysOnProtectedBranchesEnabled) {
- return axios.get(this.buildUrl(gon.relative_url_root, this.deployKeysPath), {
- params: {
- search: query,
- per_page: 20,
- active: true,
- project_id: gon.current_project_id,
- push_code: true,
- },
- });
- }
-
- return Promise.resolve({ data: [] });
+ return axios.get(this.buildUrl(gon.relative_url_root, this.deployKeysPath), {
+ params: {
+ search: query,
+ per_page: 20,
+ active: true,
+ project_id: gon.current_project_id,
+ push_code: true,
+ },
+ });
}
buildUrl(urlRoot, url) {
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index 9ece6a52805..a49eb7fd611 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -1,6 +1,7 @@
<script>
import { throttle } from 'lodash';
-import { numberToHumanSize } from '../../../../lib/utils/number_utils';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { encodeSaferUrl } from '~/lib/utils/url_utility';
export default {
props: {
@@ -43,6 +44,9 @@ export default {
hasDimensions() {
return this.width && this.height;
},
+ safePath() {
+ return encodeSaferUrl(this.path);
+ },
},
beforeDestroy() {
window.removeEventListener('resize', this.resizeThrottled, false);
@@ -84,7 +88,7 @@ export default {
<template>
<div data-testid="image-viewer" data-qa-selector="image_viewer_container">
<div :class="innerCssClasses" class="position-relative">
- <img ref="contentImg" :src="path" @load="onImgLoad" />
+ <img ref="contentImg" :src="safePath" @load="onImgLoad" />
<slot
name="image-overlay"
:rendered-width="renderedWidth"
diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb
index d8bc1320db4..6e6686f225c 100644
--- a/app/controllers/concerns/boards_responses.rb
+++ b/app/controllers/concerns/boards_responses.rb
@@ -66,7 +66,11 @@ module BoardsResponses
end
def respond_with_board
- respond_with(@board) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ return render_404 unless @board
+
+ respond_with(@board)
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
end
def serialize_as_json(resource)
diff --git a/app/controllers/projects/learn_gitlab_controller.rb b/app/controllers/projects/learn_gitlab_controller.rb
new file mode 100644
index 00000000000..162ba9bd5cb
--- /dev/null
+++ b/app/controllers/projects/learn_gitlab_controller.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class Projects::LearnGitlabController < Projects::ApplicationController
+ before_action :authenticate_user!
+ before_action :check_experiment_enabled?
+
+ feature_category :users
+
+ def index
+ push_frontend_experiment(:learn_gitlab_a, subject: current_user)
+ push_frontend_experiment(:learn_gitlab_b, subject: current_user)
+ end
+
+ private
+
+ def check_experiment_enabled?
+ return access_denied! unless helpers.learn_gitlab_experiment_enabled?(project)
+ end
+end
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index dd50ab1bc7a..821560e32ba 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -7,7 +7,6 @@ module Projects
before_action :define_variables, only: [:create_deploy_token]
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @project)
- push_frontend_feature_flag(:deploy_keys_on_protected_branches, @project)
end
feature_category :source_code_management, [:show, :cleanup]
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
new file mode 100644
index 00000000000..e72a9c83fc9
--- /dev/null
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module LearnGitlabHelper
+ def learn_gitlab_experiment_enabled?(project)
+ return false unless current_user
+ return false unless experiment_enabled_for_user?
+
+ learn_gitlab_onboarding_available?(project)
+ end
+
+ def onboarding_actions_data(project)
+ attributes = onboarding_progress(project).attributes.symbolize_keys
+
+ action_urls.map do |action, url|
+ [
+ action,
+ url: url,
+ completed: attributes[OnboardingProgress.column_name(action)].present?
+ ]
+ end.to_h
+ end
+
+ private
+
+ ACTION_ISSUE_IDS = {
+ git_write: 2,
+ pipeline_created: 4,
+ merge_request_created: 6,
+ user_added: 7,
+ trial_started: 13,
+ required_mr_approvals_enabled: 15,
+ code_owners_enabled: 16
+ }.freeze
+
+ ACTION_DOC_URLS = {
+ security_scan_enabled: 'https://docs.gitlab.com/ee/user/application_security/security_dashboard/#gitlab-security-dashboard-security-center-and-vulnerability-reports'
+ }.freeze
+
+ def action_urls
+ ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }.merge(ACTION_DOC_URLS)
+ end
+
+ def learn_gitlab_project
+ @learn_gitlab_project ||= LearnGitlab.new(current_user).project
+ end
+
+ def onboarding_progress(project)
+ OnboardingProgress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def experiment_enabled_for_user?
+ Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_a, subject: current_user) ||
+ Gitlab::Experimentation.in_experiment_group?(:learn_gitlab_b, subject: current_user)
+ end
+
+ def learn_gitlab_onboarding_available?(project)
+ OnboardingProgress.onboarding?(project.namespace) &&
+ LearnGitlab.new(current_user).available?
+ end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index a2e9952f350..f5cd89d96b4 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -433,6 +433,8 @@ module ProjectsHelper
nav_tabs += package_nav_tabs(project, current_user)
+ nav_tabs << :learn_gitlab if learn_gitlab_experiment_enabled?(project)
+
nav_tabs
end
# rubocop:enable Metrics/CyclomaticComplexity
diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb
index 8560826928a..2a051233de2 100644
--- a/app/models/clusters/applications/cert_manager.rb
+++ b/app/models/clusters/applications/cert_manager.rb
@@ -2,6 +2,8 @@
module Clusters
module Applications
+ # DEPRECATED for removal in %14.0
+ # See https://gitlab.com/groups/gitlab-org/-/epics/4280
class CertManager < ApplicationRecord
VERSION = 'v0.10.1'
CRD_VERSION = '0.10'
diff --git a/app/models/clusters/applications/crossplane.rb b/app/models/clusters/applications/crossplane.rb
index 2b1a86706a4..07378b4e8dc 100644
--- a/app/models/clusters/applications/crossplane.rb
+++ b/app/models/clusters/applications/crossplane.rb
@@ -2,6 +2,8 @@
module Clusters
module Applications
+ # DEPRECATED for removal in %14.0
+ # See https://gitlab.com/groups/gitlab-org/-/epics/4280
class Crossplane < ApplicationRecord
VERSION = '0.4.1'
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 36324e7f3e0..e7d4d737b8e 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -2,6 +2,8 @@
module Clusters
module Applications
+ # DEPRECATED for removal in %14.0
+ # See https://gitlab.com/groups/gitlab-org/-/epics/4280
class Ingress < ApplicationRecord
VERSION = '1.40.2'
INGRESS_CONTAINER_NAME = 'nginx-ingress-controller'
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index ff907c6847f..8d7d9c20bfa 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -4,6 +4,8 @@ require 'securerandom'
module Clusters
module Applications
+ # DEPRECATED for removal in %14.0
+ # See https://gitlab.com/groups/gitlab-org/-/epics/4280
class Jupyter < ApplicationRecord
VERSION = '0.9.0'
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index 7c131e031c1..6867d7b6934 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -2,6 +2,8 @@
module Clusters
module Applications
+ # DEPRECATED for removal in %14.0
+ # See https://gitlab.com/groups/gitlab-org/-/epics/4280
class Knative < ApplicationRecord
VERSION = '0.10.0'
REPOSITORY = 'https://charts.gitlab.io'
diff --git a/app/models/onboarding_progress.rb b/app/models/onboarding_progress.rb
index 38a9489a3ad..8a444f8934e 100644
--- a/app/models/onboarding_progress.rb
+++ b/app/models/onboarding_progress.rb
@@ -47,6 +47,10 @@ class OnboardingProgress < ApplicationRecord
safe_find_or_create_by(namespace: namespace)
end
+ def onboarding?(namespace)
+ where(namespace: namespace).any?
+ end
+
def register(namespace, action)
return unless root_namespace?(namespace) && ACTIONS.include?(action)
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index f28440f2444..ea51dca8a42 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -19,7 +19,7 @@ class ProtectedBranch::PushAccessLevel < ApplicationRecord
end
def check_access(user)
- if Feature.enabled?(:deploy_keys_on_protected_branches, project) && user && deploy_key.present?
+ if user && deploy_key.present?
return true if user.can?(:read_project, project) && enabled_deploy_key_for_user?(deploy_key, user)
end
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 2576caefdd4..8bb009bfd17 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -33,6 +33,13 @@
= link_to project_releases_path(@project), title: _('Releases'), class: 'shortcuts-project-releases' do
%span= _('Releases')
+ - if project_nav_tab? :learn_gitlab
+ = nav_link(controller: :learn_gitlab, html_options: { class: 'home' }) do
+ = link_to project_learn_gitlab_path(@project) do
+ .nav-icon-container
+ = sprite_icon('home')
+ %span.nav-item-name
+ = _('Learn GitLab')
- if project_nav_tab? :files
= nav_link(controller: sidebar_repository_paths, unless: -> { current_path?('projects/graphs#charts') }) do
diff --git a/app/views/projects/learn_gitlab/index.html.haml b/app/views/projects/learn_gitlab/index.html.haml
new file mode 100644
index 00000000000..d5fdbc10eb4
--- /dev/null
+++ b/app/views/projects/learn_gitlab/index.html.haml
@@ -0,0 +1,4 @@
+- breadcrumb_title _("Learn GitLab")
+- page_title _("Learn GitLab")
+
+#js-learn-gitlab-app{ data: { actions: onboarding_actions_data(@project).to_json } }
diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml
index 33be875d9a6..20cd45be6da 100644
--- a/app/views/projects/protected_branches/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_create_protected_branch.html.haml
@@ -1,5 +1,3 @@
-- select_mode_for_dropdown = Feature.enabled?(:deploy_keys_on_protected_branches, @project) ? 'js-multiselect' : ''
-
- content_for :merge_access_levels do
.merge_access_levels-container
= dropdown_tag('Select',
@@ -9,7 +7,7 @@
- content_for :push_access_levels do
.push_access_levels-container
= dropdown_tag('Select',
- options: { toggle_class: "js-allowed-to-push qa-allowed-to-push-select #{select_mode_for_dropdown} wide",
+ options: { toggle_class: "js-allowed-to-push qa-allowed-to-push-select js-multiselect wide",
dropdown_class: 'dropdown-menu-selectable qa-allowed-to-push-dropdown rspec-allowed-to-push-dropdown capitalize-header',
data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }})
diff --git a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
index cb954c20b48..d1b32df7139 100644
--- a/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
+++ b/app/views/shared/projects/protected_branches/_update_protected_branch.html.haml
@@ -1,5 +1,3 @@
-- select_mode_for_dropdown = Feature.enabled?(:deploy_keys_on_protected_branches, protected_branch.project) ? 'js-multiselect' : ''
-
- merge_access_levels = protected_branch.merge_access_levels.for_role
- push_access_levels = protected_branch.push_access_levels.for_role
@@ -25,7 +23,7 @@
%td.push_access_levels-container
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", push_access_levels.first&.access_level
= dropdown_tag( (push_access_levels.first&.humanize || 'Select') ,
- options: { toggle_class: "js-allowed-to-push #{select_mode_for_dropdown}", dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
+ options: { toggle_class: "js-allowed-to-push js-multiselect", dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", preselected_items: access_levels_data(push_access_levels) }})
- if user_push_access_levels.any?
%p.small