Commit 87493945 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 86dcd3110c23
<script>
import eventHub from '../event_hub';
import { GlToggle } from '@gitlab/ui';
export default {
name: 'ActiveToggle',
components: {
GlToggle,
},
props: {
initialActivated: {
type: Boolean,
required: true,
},
disabled: {
type: Boolean,
required: true,
},
},
data() {
return {
activated: this.initialActivated,
};
},
mounted() {
// Initialize view
this.$nextTick(() => {
this.onToggle(this.activated);
});
},
methods: {
onToggle(e) {
eventHub.$emit('toggle', e);
},
},
};
</script>
<template>
<div>
<div class="form-group row" role="group">
<label for="service[active]" class="col-form-label col-sm-2">{{ __('Active') }}</label>
<div class="col-sm-10 pt-1">
<gl-toggle
v-model="activated"
:disabled="disabled"
name="service[active]"
@change="onToggle"
/>
</div>
</div>
</div>
</template>
import Vue from 'vue';
export default new Vue();
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import ActiveToggle from './components/active_toggle.vue';
export default el => {
if (!el) {
return null;
}
const { showActive: showActiveStr, activated: activatedStr, disabled: disabledStr } = el.dataset;
const showActive = parseBoolean(showActiveStr);
const activated = parseBoolean(activatedStr);
const disabled = parseBoolean(disabledStr);
if (!showActive) {
return null;
}
return new Vue({
el,
render(createElement) {
return createElement(ActiveToggle, {
props: {
initialActivated: activated,
disabled,
},
});
},
});
};
......@@ -2,28 +2,33 @@ import $ from 'jquery';
import axios from '../lib/utils/axios_utils';
import flash from '../flash';
import { __ } from '~/locale';
import initForm from './edit';
import eventHub from './edit/event_hub';
export default class IntegrationSettingsForm {
constructor(formSelector) {
this.$form = $(formSelector);
this.formActive = false;
// Form Metadata
this.canTestService = this.$form.data('canTest');
this.testEndPoint = this.$form.data('testUrl');
// Form Child Elements
this.$serviceToggle = this.$form.find('#service_active');
this.$submitBtn = this.$form.find('button[type="submit"]');
this.$submitBtnLoader = this.$submitBtn.find('.js-btn-spinner');
this.$submitBtnLabel = this.$submitBtn.find('.js-btn-label');
}
init() {
// Initialize View
this.toggleServiceState(this.$serviceToggle.is(':checked'));
// Init Vue component
initForm(document.querySelector('.js-vue-integration-settings'));
eventHub.$on('toggle', active => {
this.formActive = active;
this.handleServiceToggle();
});
// Bind Event Listeners
this.$serviceToggle.on('change', e => this.handleServiceToggle(e));
this.$submitBtn.on('click', e => this.handleSettingsSave(e));
}
......@@ -31,7 +36,7 @@ export default class IntegrationSettingsForm {
// Check if Service is marked active, as if not marked active,
// We can skip testing it and directly go ahead to allow form to
// be submitted
if (!this.$serviceToggle.is(':checked')) {
if (!this.formActive) {
return;
}
......@@ -47,16 +52,16 @@ export default class IntegrationSettingsForm {
}
}
handleServiceToggle(e) {
this.toggleServiceState($(e.currentTarget).is(':checked'));
handleServiceToggle() {
this.toggleServiceState();
}
/**
* Change Form's validation enforcement based on service status (active/inactive)
*/
toggleServiceState(serviceActive) {
this.toggleSubmitBtnLabel(serviceActive);
if (serviceActive) {
toggleServiceState() {
this.toggleSubmitBtnLabel();
if (this.formActive) {
this.$form.removeAttr('novalidate');
} else if (!this.$form.attr('novalidate')) {
this.$form.attr('novalidate', 'novalidate');
......@@ -66,10 +71,10 @@ export default class IntegrationSettingsForm {
/**
* Toggle Submit button label based on Integration status and ability to test service
*/
toggleSubmitBtnLabel(serviceActive) {
toggleSubmitBtnLabel() {
let btnLabel = __('Save changes');
if (serviceActive && this.canTestService) {
if (this.formActive && this.canTestService) {
btnLabel = __('Test settings and save changes');
}
......
......@@ -39,13 +39,18 @@ export default {
required: false,
default: true,
},
showSpinner: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
toggleChevronClass() {
return this.expanded ? 'fa-chevron-up' : 'fa-chevron-down';
},
noteTimestampLink() {
return `#note_${this.noteId}`;
return this.noteId ? `#note_${this.noteId}` : undefined;
},
hasAuthor() {
return this.author && Object.keys(this.author).length;
......@@ -60,7 +65,9 @@ export default {
this.$emit('toggleHandler');
},
updateTargetNoteHash() {
this.setTargetNoteHash(this.noteTimestampLink);
if (this.$store) {
this.setTargetNoteHash(this.noteTimestampLink);
}
},
},
};
......@@ -101,16 +108,20 @@ export default {
<template v-if="actionText">{{ actionText }}</template>
</span>
<a
ref="noteTimestamp"
v-if="noteTimestampLink"
ref="noteTimestampLink"
:href="noteTimestampLink"
class="note-timestamp system-note-separator"
@click="updateTargetNoteHash"
>
<time-ago-tooltip :time="createdAt" tooltip-placement="bottom" />
</a>
<time-ago-tooltip v-else ref="noteTimestamp" :time="createdAt" tooltip-placement="bottom" />
</template>
<slot name="extra-controls"></slot>
<i
v-if="showSpinner"
ref="spinner"
class="fa fa-spinner fa-spin editing-spinner"
:aria-label="__('Comment is being updated')"
aria-hidden="true"
......
......@@ -9,7 +9,7 @@ class Projects::ReleasesController < Projects::ApplicationController
push_frontend_feature_flag(:release_issue_summary, project, default_enabled: true)
push_frontend_feature_flag(:release_evidence_collection, project, default_enabled: true)
push_frontend_feature_flag(:release_show_page, project, default_enabled: true)
push_frontend_feature_flag(:release_asset_link_editing, project)
push_frontend_feature_flag(:release_asset_link_editing, project, default_enabled: true)
end
before_action :authorize_update_release!, only: %i[edit update]
......
......@@ -160,4 +160,14 @@ def embedded_snippet_download_button
title: 'Download',
rel: 'noopener noreferrer')
end
def snippet_file_name(snippet)
blob = if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
blob.name
end
end
......@@ -28,9 +28,20 @@ def position
end
def position=(position)
assign_attributes(self.class.position_to_attrs(position))
end
def self.create_or_update_for(note, params)
attrs = position_to_attrs(params[:position])
attrs.merge!(params.slice(:diff_type, :line_code))
attrs[:note_id] = note.id
upsert(attrs, unique_by: [:note_id, :diff_type])
end
def self.position_to_attrs(position)
position_attrs = position.to_h
position_attrs[:diff_content_type] = position_attrs.delete(:position_type)
assign_attributes(position_attrs)
position_attrs
end
end
......@@ -83,6 +83,7 @@ def value?(val)
has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :system_note_metadata
has_one :note_diff_file, inverse_of: :diff_note, foreign_key: :diff_note_id
has_many :diff_note_positions
delegate :gfm_reference, :local_reference, to: :noteable
delegate :name, to: :project, prefix: true
......
......@@ -4,7 +4,7 @@ module PerformanceMonitoring
class PrometheusPanel
include ActiveModel::Model
attr_accessor :type, :title, :y_label, :weight, :metrics, :y_axis
attr_accessor :type, :title, :y_label, :weight, :metrics, :y_axis, :max_value
validates :title, presence: true
validates :metrics, presence: true
......
# frozen_string_literal: true
module Discussions
class CaptureDiffNotePositionService
def initialize(merge_request, paths)
@project = merge_request.project
@tracer = build_tracer(merge_request, paths)
end
def execute(discussion)
# The service has been implemented for text only
# The impact of image notes on this service is being investigated in
# https://gitlab.com/gitlab-org/gitlab/-/issues/213989
return unless discussion.on_text?
result = tracer&.trace(discussion.position)
return unless result
position = result[:position]
# Currently position data is copied across all notes of a discussion
# It makes sense to store a position only for the first note instead
# Within the newly introduced table we can start doing just that
DiffNotePosition.create_or_update_for(discussion.notes.first,
diff_type: :head,
position: position,
line_code: position.line_code(project.repository))
end
private
attr_reader :tracer, :project
def build_tracer(merge_request, paths)
return if paths.blank?
old_diff_refs, new_diff_refs = build_diff_refs(merge_request)
return unless old_diff_refs && new_diff_refs
Gitlab::Diff::PositionTracer.new(
project: project,
old_diff_refs: old_diff_refs,
new_diff_refs: new_diff_refs,
paths: paths.uniq)
end
def build_diff_refs(merge_request)
merge_ref_head = merge_request.merge_ref_head
return unless merge_ref_head
start_sha, base_sha = merge_ref_head.parent_ids
new_diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: base_sha,
start_sha: start_sha,
head_sha: merge_ref_head.id)
old_diff_refs = merge_request.diff_refs
return if new_diff_refs == old_diff_refs
[old_diff_refs, new_diff_refs]
end
end
end
# frozen_string_literal: true
module Discussions
class CaptureDiffNotePositionsService
def initialize(merge_request)
@merge_request = merge_request
end
def execute
return unless merge_request.has_complete_diff_refs?
discussions, paths = build_discussions
service = Discussions::CaptureDiffNotePositionService.new(merge_request, paths)
discussions.each do |discussion|
service.execute(discussion)
end
end
private
attr_reader :merge_request
def build_discussions
active_diff_discussions = merge_request.notes.new_diff_notes.discussions.select do |discussion|
discussion.active?(merge_request.diff_refs)
end
paths = active_diff_discussions.flat_map { |n| n.diff_file.paths }
[active_diff_discussions, paths]
end
end
end
......@@ -118,11 +118,18 @@ def update_merge_status
if can_git_merge? && merge_to_ref
merge_request.mark_as_mergeable
update_diff_discussion_positions!
else
merge_request.mark_as_unmergeable
end
end
def update_diff_discussion_positions!
return if Feature.disabled?(:merge_ref_head_comments, merge_request.target_project)
Discussions::CaptureDiffNotePositionsService.new(merge_request).execute
end
def recheck!
if !merge_request.recheck_merge_status? && outdated_merge_ref?
merge_request.mark_as_unchecked
......
......@@ -65,6 +65,10 @@ def when_saved(note)
if Feature.enabled?(:notes_create_service_tracking, project)
Gitlab::Tracking.event('Notes::CreateService', 'execute', tracking_data_for(note))
end
if Feature.enabled?(:merge_ref_head_comments, project) && note.for_merge_request? && note.diff_note? && note.start_of_discussion?
Discussions::CaptureDiffNotePositionService.new(note.noteable, note.diff_file&.paths).execute(note.discussion)
end
end
def do_commands(note, update_params, message, only_commands)
......
......@@ -37,6 +37,7 @@
= render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f
= render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
= f.submit _('Save changes'), class: 'btn btn-success prepend-top-default js-dirty-submit', data: { qa_selector: 'save_permissions_changes_button' }
......@@ -8,11 +8,7 @@
= markdown @service.help
.service-settings
- if @service.show_active_box?
.form-group.row
= form.label :active, "Active", class: "col-form-label col-sm-2"
.col-sm-10
= form.check_box :active, checked: @service.active || @service.new_record?, disabled: disable_fields_service?(@service)
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, disabled: disable_fields_service?(@service).to_s } }
- if @service.configurable_events.present?
.form-group.row
......
- link_project = local_assigns.fetch(:link_project, false)
- notes_count = @noteable_meta_data[snippet.id].user_notes_count
- file_name = snippet_file_name(snippet)
%li.snippet-row.py-3
= image_tag avatar_icon_for_user(snippet.author), class: "avatar s40 d-none d-sm-block", alt: ''
......@@ -7,10 +8,10 @@
.title
= link_to gitlab_snippet_path(snippet) do
= snippet.title
- if snippet.file_name.present?
- if file_name.present?
%span.snippet-filename.d-none.d-sm-inline-block.ml-2
= sprite_icon('doc-code', size: 16, css_class: 'file-icon align-text-bottom')
= snippet.file_name
= file_name
%ul.controls
%li
......
......@@ -944,7 +944,7 @@
- :name: background_migration
:feature_category: :database
:has_external_dependencies:
:urgency: :low
:urgency: :throttled
:resource_boundary: :unknown
:weight: 1
:idempotent:
......
......@@ -4,6 +4,7 @@ class BackgroundMigrationWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
feature_category :database
urgency :throttled
# The minimum amount of time between processing two jobs of the same migration
# class.
......
---
title: Update Active checkbox component to use toggle
merge_request: 27778
author:
type: added
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment