[CE] - Add milestones autocomplete for epics
CE backport of https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8632
This commit is contained in:
parent
420328d79e
commit
ffc3b98ceb
2 changed files with 89 additions and 26 deletions
|
@ -4,6 +4,8 @@ module Banzai
|
||||||
module Filter
|
module Filter
|
||||||
# HTML filter that replaces milestone references with links.
|
# HTML filter that replaces milestone references with links.
|
||||||
class MilestoneReferenceFilter < AbstractReferenceFilter
|
class MilestoneReferenceFilter < AbstractReferenceFilter
|
||||||
|
include Gitlab::Utils::StrongMemoize
|
||||||
|
|
||||||
self.reference_type = :milestone
|
self.reference_type = :milestone
|
||||||
|
|
||||||
def self.object_class
|
def self.object_class
|
||||||
|
@ -13,16 +15,34 @@ module Banzai
|
||||||
# Links to project milestones contain the IID, but when we're handling
|
# Links to project milestones contain the IID, but when we're handling
|
||||||
# 'regular' references, we need to use the global ID to disambiguate
|
# 'regular' references, we need to use the global ID to disambiguate
|
||||||
# between group and project milestones.
|
# between group and project milestones.
|
||||||
def find_object(project, id)
|
def find_object(parent, id)
|
||||||
return unless project.is_a?(Project)
|
return unless valid_context?(parent)
|
||||||
|
|
||||||
find_milestone_with_finder(project, id: id)
|
find_milestone_with_finder(parent, id: id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_object_from_link(project, iid)
|
def find_object_from_link(parent, iid)
|
||||||
return unless project.is_a?(Project)
|
return unless valid_context?(parent)
|
||||||
|
|
||||||
find_milestone_with_finder(project, iid: iid)
|
find_milestone_with_finder(parent, iid: iid)
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_context?(parent)
|
||||||
|
strong_memoize(:valid_context) do
|
||||||
|
group_context?(parent) || project_context?(parent)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_context?(parent)
|
||||||
|
strong_memoize(:group_context) do
|
||||||
|
parent.is_a?(Group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_context?(parent)
|
||||||
|
strong_memoize(:project_context) do
|
||||||
|
parent.is_a?(Project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def references_in(text, pattern = Milestone.reference_pattern)
|
def references_in(text, pattern = Milestone.reference_pattern)
|
||||||
|
@ -44,13 +64,15 @@ module Banzai
|
||||||
|
|
||||||
def find_milestone(project_ref, namespace_ref, milestone_id, milestone_name)
|
def find_milestone(project_ref, namespace_ref, milestone_id, milestone_name)
|
||||||
project_path = full_project_path(namespace_ref, project_ref)
|
project_path = full_project_path(namespace_ref, project_ref)
|
||||||
project = parent_from_ref(project_path)
|
|
||||||
|
|
||||||
return unless project && project.is_a?(Project)
|
# Returns group if project is not found by path
|
||||||
|
parent = parent_from_ref(project_path)
|
||||||
|
|
||||||
|
return unless parent
|
||||||
|
|
||||||
milestone_params = milestone_params(milestone_id, milestone_name)
|
milestone_params = milestone_params(milestone_id, milestone_name)
|
||||||
|
|
||||||
find_milestone_with_finder(project, milestone_params)
|
find_milestone_with_finder(parent, milestone_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def milestone_params(iid, name)
|
def milestone_params(iid, name)
|
||||||
|
@ -61,18 +83,30 @@ module Banzai
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_milestone_with_finder(project, params)
|
def find_milestone_with_finder(parent, params)
|
||||||
finder_params = { project_ids: [project.id], order: nil, state: 'all' }
|
finder_params = milestone_finder_params(parent, params[:iid].present?)
|
||||||
|
|
||||||
# We don't support IID lookups for group milestones, because IIDs can
|
|
||||||
# clash between group and project milestones.
|
|
||||||
if project.group && !params[:iid]
|
|
||||||
finder_params[:group_ids] = project.group.self_and_ancestors_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
MilestonesFinder.new(finder_params).find_by(params)
|
MilestonesFinder.new(finder_params).find_by(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def milestone_finder_params(parent, find_by_iid)
|
||||||
|
{ order: nil, state: 'all' }.tap do |params|
|
||||||
|
params[:project_ids] = parent.id if project_context?(parent)
|
||||||
|
|
||||||
|
# We don't support IID lookups because IIDs can clash between
|
||||||
|
# group/project milestones and group/subgroup milestones.
|
||||||
|
params[:group_ids] = self_and_ancestors_ids(parent) unless find_by_iid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self_and_ancestors_ids(parent)
|
||||||
|
if group_context?(parent)
|
||||||
|
parent.self_and_ancestors_ids
|
||||||
|
elsif project_context?(parent)
|
||||||
|
parent.group&.self_and_ancestors_ids
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def url_for_object(milestone, project)
|
def url_for_object(milestone, project)
|
||||||
Gitlab::Routing
|
Gitlab::Routing
|
||||||
.url_helpers
|
.url_helpers
|
||||||
|
|
|
@ -351,21 +351,50 @@ describe Banzai::Filter::MilestoneReferenceFilter do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'group context' do
|
context 'group context' do
|
||||||
let(:context) { { project: nil, group: create(:group) } }
|
let(:group) { create(:group) }
|
||||||
let(:milestone) { create(:milestone, project: project) }
|
let(:context) { { project: nil, group: group } }
|
||||||
|
|
||||||
it 'links to a valid reference' do
|
context 'when project milestone' do
|
||||||
reference = "#{project.full_path}%#{milestone.iid}"
|
let(:milestone) { create(:milestone, project: project) }
|
||||||
|
|
||||||
result = reference_filter("See #{reference}", context)
|
it 'links to a valid reference' do
|
||||||
|
reference = "#{project.full_path}%#{milestone.iid}"
|
||||||
|
|
||||||
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
|
result = reference_filter("See #{reference}", context)
|
||||||
|
|
||||||
|
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'ignores internal references' do
|
||||||
|
exp = act = "See %#{milestone.iid}"
|
||||||
|
|
||||||
|
expect(reference_filter(act, context).to_html).to eq exp
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores internal references' do
|
context 'when group milestone' do
|
||||||
exp = act = "See %#{milestone.iid}"
|
let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) }
|
||||||
|
|
||||||
expect(reference_filter(act, context).to_html).to eq exp
|
context 'for subgroups', :nested_groups do
|
||||||
|
let(:sub_group) { create(:group, parent: group) }
|
||||||
|
let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) }
|
||||||
|
|
||||||
|
it 'links to a valid reference of subgroup and group milestones' do
|
||||||
|
[group_milestone, sub_group_milestone].each do |milestone|
|
||||||
|
reference = "%#{milestone.title}"
|
||||||
|
|
||||||
|
result = reference_filter("See #{reference}", { project: nil, group: sub_group })
|
||||||
|
|
||||||
|
expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'ignores internal references' do
|
||||||
|
exp = act = "See %#{group_milestone.iid}"
|
||||||
|
|
||||||
|
expect(reference_filter(act, context).to_html).to eq exp
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue