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:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/content_editor/constants.js7
-rw-r--r--app/assets/javascripts/content_editor/extensions/code.js13
-rw-r--r--app/assets/javascripts/content_editor/extensions/frontmatter.js11
-rw-r--r--app/assets/javascripts/content_editor/services/markdown_serializer.js2
-rw-r--r--app/assets/javascripts/labels/index.js2
-rw-r--r--app/assets/javascripts/projects/project_visibility.js21
-rw-r--r--app/assets/stylesheets/pages/labels.scss5
-rw-r--r--app/models/ci/secure_file.rb33
-rw-r--r--app/models/project.rb1
-rw-r--r--app/uploaders/ci/secure_file_uploader.rb46
-rw-r--r--app/views/shared/_label.html.haml32
11 files changed, 147 insertions, 26 deletions
diff --git a/app/assets/javascripts/content_editor/constants.js b/app/assets/javascripts/content_editor/constants.js
index 4af9dc8e405..5e56078df01 100644
--- a/app/assets/javascripts/content_editor/constants.js
+++ b/app/assets/javascripts/content_editor/constants.js
@@ -49,3 +49,10 @@ export const LOADING_ERROR_EVENT = 'loadingError';
export const PARSE_HTML_PRIORITY_LOWEST = 1;
export const PARSE_HTML_PRIORITY_DEFAULT = 50;
export const PARSE_HTML_PRIORITY_HIGHEST = 100;
+
+export const EXTENSION_PRIORITY_LOWER = 75;
+/**
+ * 100 is the default priority in Tiptap
+ * https://tiptap.dev/guide/custom-extensions/#priority
+ */
+export const EXTENSION_PRIORITY_DEFAULT = 100;
diff --git a/app/assets/javascripts/content_editor/extensions/code.js b/app/assets/javascripts/content_editor/extensions/code.js
index f93c22ad10e..53f6d9b995c 100644
--- a/app/assets/javascripts/content_editor/extensions/code.js
+++ b/app/assets/javascripts/content_editor/extensions/code.js
@@ -1 +1,12 @@
-export { Code as default } from '@tiptap/extension-code';
+import Code from '@tiptap/extension-code';
+import { EXTENSION_PRIORITY_LOWER } from '../constants';
+
+export default Code.extend({
+ excludes: null,
+ /**
+ * Reduce the rendering priority of the code mark to
+ * ensure the bold, italic, and strikethrough marks
+ * are rendered first.
+ */
+ priority: EXTENSION_PRIORITY_LOWER,
+});
diff --git a/app/assets/javascripts/content_editor/extensions/frontmatter.js b/app/assets/javascripts/content_editor/extensions/frontmatter.js
index c09c10bc524..9842027e192 100644
--- a/app/assets/javascripts/content_editor/extensions/frontmatter.js
+++ b/app/assets/javascripts/content_editor/extensions/frontmatter.js
@@ -14,9 +14,20 @@ export default CodeBlockHighlight.extend({
},
];
},
+ addCommands() {
+ return {
+ setFrontmatter: (attributes) => ({ commands }) => {
+ return commands.setNode(this.name, attributes);
+ },
+ toggleFrontmatter: (attributes) => ({ commands }) => {
+ return commands.toggleNode(this.name, 'paragraph', attributes);
+ },
+ };
+ },
addNodeView() {
return new VueNodeViewRenderer(FrontmatterWrapper);
},
+
addInputRules() {
return [];
},
diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js
index 278ef326c7a..d54fb7cded2 100644
--- a/app/assets/javascripts/content_editor/services/markdown_serializer.js
+++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js
@@ -65,8 +65,8 @@ import {
const defaultSerializerConfig = {
marks: {
[Bold.name]: defaultMarkdownSerializer.marks.strong,
- [Code.name]: defaultMarkdownSerializer.marks.code,
[Italic.name]: { open: '_', close: '_', mixable: true, expelEnclosingWhitespace: true },
+ [Code.name]: defaultMarkdownSerializer.marks.code,
[Subscript.name]: { open: '<sub>', close: '</sub>', mixable: true },
[Superscript.name]: { open: '<sup>', close: '</sup>', mixable: true },
[InlineDiff.name]: {
diff --git a/app/assets/javascripts/labels/index.js b/app/assets/javascripts/labels/index.js
index 22a9c0a89c0..e87ad8d9a06 100644
--- a/app/assets/javascripts/labels/index.js
+++ b/app/assets/javascripts/labels/index.js
@@ -26,7 +26,7 @@ export function initLabels() {
if ($('.prioritized-labels').length) {
new LabelManager(); // eslint-disable-line no-new
}
- $('.label-subscription').each((i, el) => {
+ $('.js-label-subscription').each((i, el) => {
const $el = $(el);
if ($el.find('.dropdown-group-label').length) {
diff --git a/app/assets/javascripts/projects/project_visibility.js b/app/assets/javascripts/projects/project_visibility.js
index 1b57a69d464..c962554c9f4 100644
--- a/app/assets/javascripts/projects/project_visibility.js
+++ b/app/assets/javascripts/projects/project_visibility.js
@@ -1,4 +1,6 @@
import $ from 'jquery';
+import { escape } from 'lodash';
+import { __, sprintf } from '~/locale';
import eventHub from '~/projects/new/event_hub';
// Values are from lib/gitlab/visibility_level.rb
@@ -25,10 +27,21 @@ function setVisibilityOptions({ name, visibility, showPath, editPath }) {
if (reason) {
const optionTitle = option.querySelector('.option-title');
const optionName = optionTitle ? optionTitle.innerText.toLowerCase() : '';
- reason.innerHTML = `This project cannot be ${optionName} because the visibility of
- <a href="${showPath}">${name}</a> is ${visibility}. To make this project
- ${optionName}, you must first <a href="${editPath}">change the visibility</a>
- of the parent group.`;
+ reason.innerHTML = sprintf(
+ __(
+ 'This project cannot be %{visibilityLevel} because the visibility of %{openShowLink}%{name}%{closeShowLink} is %{visibility}. To make this project %{visibilityLevel}, you must first %{openEditLink}change the visibility%{closeEditLink} of the parent group.',
+ ),
+ {
+ visibilityLevel: optionName,
+ name: escape(name),
+ visibility,
+ openShowLink: `<a href="${showPath}">`,
+ closeShowLink: '</a>',
+ openEditLink: `<a href="${editPath}">`,
+ closeEditLink: '</a>',
+ },
+ false,
+ );
}
} else {
option.classList.remove('disabled');
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 08949578f46..82216b8d5c5 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -107,9 +107,8 @@
display: none;
}
-.label-subscribe-button {
- width: 105px;
- font-weight: 200;
+.label-subscription {
+ width: 109px;
}
.labels-container {
diff --git a/app/models/ci/secure_file.rb b/app/models/ci/secure_file.rb
new file mode 100644
index 00000000000..56f632b6232
--- /dev/null
+++ b/app/models/ci/secure_file.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Ci
+ class SecureFile < Ci::ApplicationRecord
+ include FileStoreMounter
+
+ FILE_SIZE_LIMIT = 5.megabytes.freeze
+ CHECKSUM_ALGORITHM = 'sha256'
+
+ belongs_to :project, optional: false
+
+ validates :file, presence: true, file_size: { maximum: FILE_SIZE_LIMIT }
+ validates :checksum, :file_store, :name, :permissions, :project_id, presence: true
+
+ before_validation :assign_checksum
+
+ enum permissions: { read_only: 0, read_write: 1, execute: 2 }
+
+ default_value_for(:file_store) { Ci::SecureFileUploader.default_store }
+
+ mount_file_store_uploader Ci::SecureFileUploader
+
+ def checksum_algorithm
+ CHECKSUM_ALGORITHM
+ end
+
+ private
+
+ def assign_checksum
+ self.checksum = file.checksum if file.present? && file_changed?
+ end
+ end
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 7e2c98c85b3..81ee1c1fe55 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -340,6 +340,7 @@ class Project < ApplicationRecord
has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
has_many :variables, class_name: 'Ci::Variable'
has_many :triggers, class_name: 'Ci::Trigger'
+ has_many :secure_files, class_name: 'Ci::SecureFile'
has_many :environments
has_many :environments_for_dashboard, -> { from(with_rank.unfoldered.available, :environments).where('rank <= 3') }, class_name: 'Environment'
has_many :deployments
diff --git a/app/uploaders/ci/secure_file_uploader.rb b/app/uploaders/ci/secure_file_uploader.rb
new file mode 100644
index 00000000000..514d88dd177
--- /dev/null
+++ b/app/uploaders/ci/secure_file_uploader.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Ci
+ class SecureFileUploader < GitlabUploader
+ include ObjectStorage::Concern
+
+ storage_options Gitlab.config.ci_secure_files
+
+ # Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks)
+ encrypt(key: :key)
+
+ def key
+ OpenSSL::HMAC.digest('SHA256', Gitlab::Application.secrets.db_key_base, model.project_id.to_s)
+ end
+
+ def checksum
+ @checksum ||= Digest::SHA256.hexdigest(model.file.read)
+ end
+
+ def store_dir
+ dynamic_segment
+ end
+
+ private
+
+ def dynamic_segment
+ Gitlab::HashedPath.new('secure_files', model.id, root_hash: model.project_id)
+ end
+
+ class << self
+ # direct upload is disabled since the file
+ # must always be encrypted
+ def direct_upload_enabled?
+ false
+ end
+
+ def background_upload_enabled?
+ false
+ end
+
+ def default_store
+ object_store_enabled? ? ObjectStorage::Store::REMOTE : ObjectStorage::Store::LOCAL
+ end
+ end
+ end
+end
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index e4ef0a52eba..9428813f6b0 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -10,18 +10,18 @@
= render "shared/label_row", label: label, force_priority: force_priority
%ul.label-actions-list
- if can?(current_user, :admin_label, @project)
- %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label),
+ %li.gl-display-inline-block.js-toggle-priority.gl-ml-3{ data: { url: remove_priority_project_label_path(@project, label),
dom_id: dom_id(label), type: label.type } }
- %button.add-priority.btn.gl-button.btn-default-tertiary.btn-sm.has-tooltip.gl-ml-3{ title: _('Prioritize'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Prioritize label') }
+ %button.add-priority.btn.gl-button.btn-default-tertiary.btn-sm.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Prioritize label') }
= sprite_icon('star-o')
- %button.remove-priority.btn.gl-button.btn-default-tertiary.btn-sm.has-tooltip.gl-ml-3{ title: _('Remove priority'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Deprioritize label') }
+ %button.remove-priority.btn.gl-button.btn-default-tertiary.btn-sm.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'bottom' }, aria_label: _('Deprioritize label') }
= sprite_icon('star')
- if can?(current_user, :admin_label, label)
- %li.inline
+ %li.gl-display-inline-block
= link_to label.edit_path, class: 'btn gl-button btn-default-tertiary btn-sm edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
- if can?(current_user, :admin_label, label)
- %li.inline
+ %li.gl-display-inline-block
.dropdown
%button{ type: 'button', class: 'btn gl-button btn-default-tertiary btn-sm js-label-options-dropdown', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') }
= sprite_icon('ellipsis_v')
@@ -41,23 +41,23 @@
%button.text-danger.js-delete-label-modal-button{ type: 'button', data: { label_name: label.name, subject_name: label.subject_name, destroy_path: label.destroy_path } }
= _('Delete')
- if current_user
- %li.inline.label-subscription
+ %li.gl-display-inline-block.label-subscription.js-label-subscription.gl-ml-3
- if label.can_subscribe_to_label_in_different_levels?
- %button.js-unsubscribe-button.gl-button.label-subscribe-button.btn.btn-default.gl-ml-3{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
- %span= _('Unsubscribe')
+ %button.js-unsubscribe-button.gl-button.btn.btn-default.gl-w-full{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
+ %span.gl-button-text= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
- %button.gl-button.label-subscribe-button.btn.btn-default.gl-ml-3{ data: { toggle: 'dropdown' } }
- %span
+ %button.gl-button.btn.btn-default.gl-w-full{ data: { toggle: 'dropdown' } }
+ %span.gl-button-text
= _('Subscribe')
= sprite_icon('chevron-down')
.dropdown-menu.dropdown-open-left
%ul
%li
- %button.js-subscribe-button.label-subscribe-button.gl-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } }
- %span= _('Subscribe at project level')
+ %button.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } }
+ %span.gl-button-text= _('Subscribe at project level')
%li
- %button.js-subscribe-button.js-group-level.label-subscribe-button.gl-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }
- %span= _('Subscribe at group level')
+ %button.js-subscribe-button.js-group-level{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }
+ %span.gl-button-text= _('Subscribe at group level')
- else
- %button.gl-button.js-subscribe-button.label-subscribe-button.btn.btn-default.gl-ml-3{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
- %span= label_subscription_toggle_button_text(label, @project)
+ %button.gl-button.js-subscribe-button.btn.btn-default.gl-w-full{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
+ %span.gl-button-text= label_subscription_toggle_button_text(label, @project)