Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
7ecc247a
Commit
7ecc247a
authored
Apr 13, 2021
by
Brett Walker
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adjust class names to new references dir
for reference filters
parent
dceae94f
Changes
49
Hide whitespace changes
Inline
Side-by-side
Showing
49 changed files
with
1634 additions
and
1586 deletions
+1634
-1586
ee/lib/ee/banzai/filter/references/abstract_reference_filter.rb
.../ee/banzai/filter/references/abstract_reference_filter.rb
+7
-5
ee/lib/ee/banzai/filter/references/epic_reference_filter.rb
ee/lib/ee/banzai/filter/references/epic_reference_filter.rb
+38
-36
ee/lib/ee/banzai/filter/references/iteration_reference_filter.rb
...ee/banzai/filter/references/iteration_reference_filter.rb
+82
-80
ee/lib/ee/banzai/filter/references/label_reference_filter.rb
ee/lib/ee/banzai/filter/references/label_reference_filter.rb
+16
-14
ee/lib/ee/banzai/filter/references/vulnerability_reference_filter.rb
...anzai/filter/references/vulnerability_reference_filter.rb
+44
-42
ee/lib/ee/banzai/pipeline/gfm_pipeline.rb
ee/lib/ee/banzai/pipeline/gfm_pipeline.rb
+3
-3
ee/lib/ee/banzai/pipeline/single_line_pipeline.rb
ee/lib/ee/banzai/pipeline/single_line_pipeline.rb
+3
-3
ee/spec/lib/banzai/filter/references/epic_reference_filter_spec.rb
...ib/banzai/filter/references/epic_reference_filter_spec.rb
+1
-1
ee/spec/lib/banzai/filter/references/iteration_reference_filter_spec.rb
...nzai/filter/references/iteration_reference_filter_spec.rb
+1
-1
ee/spec/lib/banzai/filter/references/label_reference_filter_spec.rb
...b/banzai/filter/references/label_reference_filter_spec.rb
+1
-1
ee/spec/lib/banzai/filter/references/vulnerability_reference_filters_spec.rb
...filter/references/vulnerability_reference_filters_spec.rb
+1
-1
lib/banzai/filter/references/abstract_reference_filter.rb
lib/banzai/filter/references/abstract_reference_filter.rb
+335
-333
lib/banzai/filter/references/alert_reference_filter.rb
lib/banzai/filter/references/alert_reference_filter.rb
+19
-17
lib/banzai/filter/references/commit_range_reference_filter.rb
...banzai/filter/references/commit_range_reference_filter.rb
+31
-29
lib/banzai/filter/references/commit_reference_filter.rb
lib/banzai/filter/references/commit_reference_filter.rb
+61
-59
lib/banzai/filter/references/design_reference_filter.rb
lib/banzai/filter/references/design_reference_filter.rb
+81
-79
lib/banzai/filter/references/epic_reference_filter.rb
lib/banzai/filter/references/epic_reference_filter.rb
+12
-10
lib/banzai/filter/references/external_issue_reference_filter.rb
...nzai/filter/references/external_issue_reference_filter.rb
+91
-89
lib/banzai/filter/references/feature_flag_reference_filter.rb
...banzai/filter/references/feature_flag_reference_filter.rb
+22
-20
lib/banzai/filter/references/issuable_reference_filter.rb
lib/banzai/filter/references/issuable_reference_filter.rb
+11
-9
lib/banzai/filter/references/issue_reference_filter.rb
lib/banzai/filter/references/issue_reference_filter.rb
+41
-39
lib/banzai/filter/references/iteration_reference_filter.rb
lib/banzai/filter/references/iteration_reference_filter.rb
+8
-6
lib/banzai/filter/references/label_reference_filter.rb
lib/banzai/filter/references/label_reference_filter.rb
+98
-96
lib/banzai/filter/references/merge_request_reference_filter.rb
...anzai/filter/references/merge_request_reference_filter.rb
+70
-68
lib/banzai/filter/references/milestone_reference_filter.rb
lib/banzai/filter/references/milestone_reference_filter.rb
+97
-95
lib/banzai/filter/references/project_reference_filter.rb
lib/banzai/filter/references/project_reference_filter.rb
+88
-86
lib/banzai/filter/references/reference_filter.rb
lib/banzai/filter/references/reference_filter.rb
+167
-165
lib/banzai/filter/references/snippet_reference_filter.rb
lib/banzai/filter/references/snippet_reference_filter.rb
+19
-17
lib/banzai/filter/references/user_reference_filter.rb
lib/banzai/filter/references/user_reference_filter.rb
+135
-133
lib/banzai/filter/references/vulnerability_reference_filter.rb
...anzai/filter/references/vulnerability_reference_filter.rb
+12
-10
lib/banzai/pipeline/gfm_pipeline.rb
lib/banzai/pipeline/gfm_pipeline.rb
+13
-13
lib/banzai/pipeline/label_pipeline.rb
lib/banzai/pipeline/label_pipeline.rb
+1
-1
lib/banzai/pipeline/single_line_pipeline.rb
lib/banzai/pipeline/single_line_pipeline.rb
+9
-9
spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb
...anzai/filter/references/abstract_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/alert_reference_filter_spec.rb
...b/banzai/filter/references/alert_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/commit_range_reference_filter_spec.rb
...i/filter/references/commit_range_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/commit_reference_filter_spec.rb
.../banzai/filter/references/commit_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/design_reference_filter_spec.rb
.../banzai/filter/references/design_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/external_issue_reference_filter_spec.rb
...filter/references/external_issue_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/feature_flag_reference_filter_spec.rb
...i/filter/references/feature_flag_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
...b/banzai/filter/references/issue_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/label_reference_filter_spec.rb
...b/banzai/filter/references/label_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/merge_request_reference_filter_spec.rb
.../filter/references/merge_request_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb
...nzai/filter/references/milestone_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/project_reference_filter_spec.rb
...banzai/filter/references/project_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/reference_filter_spec.rb
spec/lib/banzai/filter/references/reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/snippet_reference_filter_spec.rb
...banzai/filter/references/snippet_reference_filter_spec.rb
+1
-1
spec/lib/banzai/filter/references/user_reference_filter_spec.rb
...ib/banzai/filter/references/user_reference_filter_spec.rb
+1
-1
spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
+1
-1
No files found.
ee/lib/ee/banzai/filter/references/abstract_reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,12 +3,14 @@
module
EE
module
Banzai
module
Filter
module
AbstractReferenceFilter
extend
::
Gitlab
::
Utils
::
Override
module
References
module
AbstractReferenceFilter
extend
::
Gitlab
::
Utils
::
Override
override
:current_project_namespace_path
def
current_project_namespace_path
@current_project_namespace_path
||=
(
project
&
.
namespace
||
group
)
&
.
full_path
override
:current_project_namespace_path
def
current_project_namespace_path
@current_project_namespace_path
||=
(
project
&
.
namespace
||
group
)
&
.
full_path
end
end
end
end
...
...
ee/lib/ee/banzai/filter/references/epic_reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,51 +3,53 @@
module
EE
module
Banzai
module
Filter
# HTML filter that replaces epic references with links. References to
# epics that do not exist are ignored.
#
# This filter supports cross-project/group references.
module
EpicReferenceFilter
extend
ActiveSupport
::
Concern
module
References
# HTML filter that replaces epic references with links. References to
# epics that do not exist are ignored.
#
# This filter supports cross-project/group references.
module
EpicReferenceFilter
extend
ActiveSupport
::
Concern
class_methods
do
def
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
symbol
=
$~
[
object_sym
]
if
object_class
.
reference_valid?
(
symbol
)
yield
match
,
symbol
.
to_i
,
nil
,
$~
[
:group
],
$~
else
match
class_methods
do
def
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
symbol
=
$~
[
object_sym
]
if
object_class
.
reference_valid?
(
symbol
)
yield
match
,
symbol
.
to_i
,
nil
,
$~
[
:group
],
$~
else
match
end
end
end
end
end
def
url_for_object
(
epic
,
group
)
urls
=
::
Gitlab
::
Routing
.
url_helpers
urls
.
group_epic_url
(
group
,
epic
,
only_path:
context
[
:only_path
])
end
def
url_for_object
(
epic
,
group
)
urls
=
::
Gitlab
::
Routing
.
url_helpers
urls
.
group_epic_url
(
group
,
epic
,
only_path:
context
[
:only_path
])
end
def
data_attributes_for
(
text
,
group
,
object
,
link_content:
false
,
link_reference:
false
)
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
group:
group
.
id
,
object_sym
=>
object
.
id
}
end
def
data_attributes_for
(
text
,
group
,
object
,
link_content:
false
,
link_reference:
false
)
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
group:
group
.
id
,
object_sym
=>
object
.
id
}
end
# rubocop: disable CodeReuse/ActiveRecord
def
parent_records
(
parent
,
ids
)
parent
.
epics
.
where
(
iid:
ids
.
to_a
)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def
parent_records
(
parent
,
ids
)
parent
.
epics
.
where
(
iid:
ids
.
to_a
)
end
# rubocop: enable CodeReuse/ActiveRecord
private
private
def
parent_type
:group
def
parent_type
:group
end
end
end
end
...
...
ee/lib/ee/banzai/filter/references/iteration_reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,113 +3,115 @@
module
EE
module
Banzai
module
Filter
# HTML filter that replaces iteration references with links.
module
IterationReferenceFilter
include
::
Gitlab
::
Utils
::
StrongMemoize
module
References
# HTML filter that replaces iteration references with links.
module
IterationReferenceFilter
include
::
Gitlab
::
Utils
::
StrongMemoize
def
find_object
(
parent
,
id
)
return
unless
valid_context?
(
parent
)
def
find_object
(
parent
,
id
)
return
unless
valid_context?
(
parent
)
find_iteration
(
parent
,
id:
id
)
end
def
valid_context?
(
parent
)
group_context?
(
parent
)
||
project_context?
(
parent
)
end
def
group_context?
(
parent
)
strong_memoize
(
:group_context
)
do
parent
.
is_a?
(
Group
)
find_iteration
(
parent
,
id:
id
)
end
end
def
project_context?
(
parent
)
strong_memoize
(
:project_context
)
do
parent
.
is_a?
(
Project
)
def
valid_context?
(
parent
)
group_context?
(
parent
)
||
project_context?
(
parent
)
end
end
def
references_in
(
text
,
pattern
=
::
Iteration
.
reference_pattern
)
# We'll handle here the references that follow the `reference_pattern`.
# Other patterns (for example, the link pattern) are handled by the
# default implementation.
return
super
(
text
,
pattern
)
if
pattern
!=
::
Iteration
.
reference_pattern
iterations
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
iteration
=
parse_and_find_iteration
(
$~
[
:project
],
$~
[
:namespace
],
$~
[
:iteration_id
],
$~
[
:iteration_name
])
def
group_context?
(
parent
)
strong_memoize
(
:group_context
)
do
parent
.
is_a?
(
Group
)
end
end
if
iteration
iterations
[
iteration
.
id
]
=
yield
match
,
iteration
.
id
,
$~
[
:project
],
$~
[
:namespace
],
$~
"
#{
::
Banzai
::
Filter
::
AbstractReferenceFilter
::
REFERENCE_PLACEHOLDER
}#{
iteration
.
id
}
"
else
match
def
project_context?
(
parent
)
strong_memoize
(
:project_context
)
do
parent
.
is_a?
(
Project
)
end
end
return
text
if
iterations
.
empty?
def
references_in
(
text
,
pattern
=
::
Iteration
.
reference_pattern
)
# We'll handle here the references that follow the `reference_pattern`.
# Other patterns (for example, the link pattern) are handled by the
# default implementation.
return
super
(
text
,
pattern
)
if
pattern
!=
::
Iteration
.
reference_pattern
iterations
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
iteration
=
parse_and_find_iteration
(
$~
[
:project
],
$~
[
:namespace
],
$~
[
:iteration_id
],
$~
[
:iteration_name
])
if
iteration
iterations
[
iteration
.
id
]
=
yield
match
,
iteration
.
id
,
$~
[
:project
],
$~
[
:namespace
],
$~
"
#{
::
Banzai
::
Filter
::
References
::
AbstractReferenceFilter
::
REFERENCE_PLACEHOLDER
}#{
iteration
.
id
}
"
else
match
end
end
escape_with_placeholders
(
unescaped_html
,
iterations
)
end
return
text
if
iterations
.
empty?
def
parse_and_find_iteration
(
project_ref
,
namespace_ref
,
iteration_id
,
iteration_name
)
project_path
=
full_project_path
(
namespace_ref
,
project_ref
)
escape_with_placeholders
(
unescaped_html
,
iterations
)
end
# Returns group if project is not found by path
parent
=
parent_from_ref
(
project_path
)
def
parse_and_find_iteration
(
project_ref
,
namespace_ref
,
iteration_id
,
iteration_name
)
project_path
=
full_project_path
(
namespace_ref
,
project_ref
)
return
unless
parent
# Returns group if project is not found by path
parent
=
parent_from_ref
(
project_path
)
iteration_params
=
iteration_params
(
iteration_id
,
iteration_name
)
return
unless
parent
find_iteration
(
parent
,
iteration_params
)
end
iteration_params
=
iteration_params
(
iteration_id
,
iteration_name
)
def
iteration_params
(
id
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
id:
id
.
to_i
}
find_iteration
(
parent
,
iteration_params
)
end
end
# rubocop: disable CodeReuse/ActiveRecord
def
find_iteration
(
parent
,
params
)
::
Iteration
.
for_projects_and_groups
(
project_ids
(
parent
),
group_and_ancestors_ids
(
parent
)).
find_by
(
**
params
)
end
# rubocop: enable CodeReuse/ActiveRecord
def
iteration_params
(
id
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
id:
id
.
to_i
}
end
end
def
project_ids
(
parent
)
parent
.
id
if
project_context?
(
parent
)
end
# rubocop: disable CodeReuse/ActiveRecord
def
find_iteration
(
parent
,
params
)
::
Iteration
.
for_projects_and_groups
(
project_ids
(
parent
),
group_and_ancestors_ids
(
parent
)).
find_by
(
**
params
)
end
# rubocop: enable CodeReuse/ActiveRecord
def
group_and_ancestors_ids
(
parent
)
if
group_context?
(
parent
)
parent
.
self_and_ancestors
.
select
(
:id
)
elsif
project_context?
(
parent
)
parent
.
group
&
.
self_and_ancestors
&
.
select
(
:id
)
def
project_ids
(
parent
)
parent
.
id
if
project_context?
(
parent
)
end
end
def
url_for_object
(
iteration
,
_parent
)
::
Gitlab
::
Routing
.
url_helpers
.
iteration_url
(
iteration
,
only_path:
context
[
:only_path
])
end
def
group_and_ancestors_ids
(
parent
)
if
group_context?
(
parent
)
parent
.
self_and_ancestors
.
select
(
:id
)
elsif
project_context?
(
parent
)
parent
.
group
&
.
self_and_ancestors
&
.
select
(
:id
)
end
end
def
url_for_object
(
iteration
,
_parent
)
::
Gitlab
::
Routing
.
url_helpers
.
iteration_url
(
iteration
,
only_path:
context
[
:only_path
])
end
def
object_link_text
(
object
,
matches
)
iteration_link
=
escape_once
(
super
)
reference
=
object
.
project
&
.
to_reference_base
(
project
)
def
object_link_text
(
object
,
matches
)
iteration_link
=
escape_once
(
super
)
reference
=
object
.
project
&
.
to_reference_base
(
project
)
if
reference
.
present?
"
#{
iteration_link
}
<i>in
#{
reference
}
</i>"
.
html_safe
else
iteration_link
if
reference
.
present?
"
#{
iteration_link
}
<i>in
#{
reference
}
</i>"
.
html_safe
else
iteration_link
end
end
end
def
object_link_title
(
_object
,
_matches
)
'Iteration'
def
object_link_title
(
_object
,
_matches
)
'Iteration'
end
end
end
end
...
...
ee/lib/ee/banzai/filter/references/label_reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,24 +3,26 @@
module
EE
module
Banzai
module
Filter
module
LabelReferenceFilter
extend
::
Gitlab
::
Utils
::
Override
module
References
module
LabelReferenceFilter
extend
::
Gitlab
::
Utils
::
Override
override
:data_attributes_for
def
data_attributes_for
(
text
,
parent
,
object
,
link_content:
false
,
link_reference:
false
)
return
super
unless
object
.
scoped_label?
override
:data_attributes_for
def
data_attributes_for
(
text
,
parent
,
object
,
link_content:
false
,
link_reference:
false
)
return
super
unless
object
.
scoped_label?
# Enabling HTML tooltips for scoped labels here and additional escaping is done in `object_link_title`
super
.
merge!
(
html:
true
)
end
# Enabling HTML tooltips for scoped labels here and additional escaping is done in `object_link_title`
super
.
merge!
(
html:
true
)
end
override
:object_link_title
def
object_link_title
(
object
,
matches
)
return
super
unless
object
.
scoped_label?
override
:object_link_title
def
object_link_title
(
object
,
matches
)
return
super
unless
object
.
scoped_label?
ERB
::
Util
.
html_escape
(
super
)
ERB
::
Util
.
html_escape
(
super
)
end
end
end
end
...
...
ee/lib/ee/banzai/filter/references/vulnerability_reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,61 +3,63 @@
module
EE
module
Banzai
module
Filter
# HTML filter that replaces vulnerability references with links. References to
# vulnerabilities that do not exist are ignored.
#
# This filter supports cross-project/group references.
module
VulnerabilityReferenceFilter
extend
ActiveSupport
::
Concern
module
References
# HTML filter that replaces vulnerability references with links. References to
# vulnerabilities that do not exist are ignored.
#
# This filter supports cross-project/group references.
module
VulnerabilityReferenceFilter
extend
ActiveSupport
::
Concern
class_methods
do
def
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
symbol
=
$~
[
object_sym
]
if
object_class
.
reference_valid?
(
symbol
)
yield
match
,
symbol
.
to_i
,
$~
[
:project
],
$~
[
:namespace
],
$~
else
match
class_methods
do
def
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
symbol
=
$~
[
object_sym
]
if
object_class
.
reference_valid?
(
symbol
)
yield
match
,
symbol
.
to_i
,
$~
[
:project
],
$~
[
:namespace
],
$~
else
match
end
end
end
end
end
def
unescape_link
(
href
)
return
href
if
href
=~
object_class
.
reference_pattern
def
unescape_link
(
href
)
return
href
if
href
=~
object_class
.
reference_pattern
super
end
super
end
def
url_for_object
(
vulnerability
,
project
)
urls
=
::
Gitlab
::
Routing
.
url_helpers
urls
.
project_security_vulnerability_url
(
project
,
vulnerability
,
only_path:
context
[
:only_path
])
end
def
url_for_object
(
vulnerability
,
project
)
urls
=
::
Gitlab
::
Routing
.
url_helpers
urls
.
project_security_vulnerability_url
(
project
,
vulnerability
,
only_path:
context
[
:only_path
])
end
def
data_attributes_for
(
text
,
project
,
object
,
link_content:
false
,
link_reference:
false
)
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
project:
project
.
id
,
object_sym
=>
object
.
id
}
end
def
data_attributes_for
(
text
,
project
,
object
,
link_content:
false
,
link_reference:
false
)
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
project:
project
.
id
,
object_sym
=>
object
.
id
}
end
def
parent_records
(
parent
,
ids
)
return
::
Vulnerability
.
none
if
ids
.
blank?
||
parent
.
nil?
def
parent_records
(
parent
,
ids
)
return
::
Vulnerability
.
none
if
ids
.
blank?
||
parent
.
nil?
parent
.
vulnerabilities
.
id_in
(
ids
.
to_a
)
end
parent
.
vulnerabilities
.
id_in
(
ids
.
to_a
)
end
def
record_identifier
(
record
)
record
.
id
.
to_i
end
def
record_identifier
(
record
)
record
.
id
.
to_i
end
private
private
def
parent_type
:project
def
parent_type
:project
end
end
end
end
...
...
ee/lib/ee/banzai/pipeline/gfm_pipeline.rb
View file @
7ecc247a
...
...
@@ -16,9 +16,9 @@ module EE
def
reference_filters
[
::
Banzai
::
Filter
::
EpicReferenceFilter
,
::
Banzai
::
Filter
::
IterationReferenceFilter
,
::
Banzai
::
Filter
::
VulnerabilityReferenceFilter
,
::
Banzai
::
Filter
::
References
::
EpicReferenceFilter
,
::
Banzai
::
Filter
::
References
::
IterationReferenceFilter
,
::
Banzai
::
Filter
::
References
::
VulnerabilityReferenceFilter
,
*
super
]
end
...
...
ee/lib/ee/banzai/pipeline/single_line_pipeline.rb
View file @
7ecc247a
...
...
@@ -9,9 +9,9 @@ module EE
class_methods
do
def
reference_filters
[
::
Banzai
::
Filter
::
EpicReferenceFilter
,
::
Banzai
::
Filter
::
IterationReferenceFilter
,
::
Banzai
::
Filter
::
VulnerabilityReferenceFilter
,
::
Banzai
::
Filter
::
References
::
EpicReferenceFilter
,
::
Banzai
::
Filter
::
References
::
IterationReferenceFilter
,
::
Banzai
::
Filter
::
References
::
VulnerabilityReferenceFilter
,
*
super
]
end
...
...
ee/spec/lib/banzai/filter/references/epic_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
EpicReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
EpicReferenceFilter
do
include
FilterSpecHelper
let
(
:urls
)
{
Gitlab
::
Routing
.
url_helpers
}
...
...
ee/spec/lib/banzai/filter/references/iteration_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
IterationReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
IterationReferenceFilter
do
include
FilterSpecHelper
let
(
:parent_group
)
{
create
(
:group
,
:public
)
}
...
...
ee/spec/lib/banzai/filter/references/label_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
LabelReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
LabelReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
,
name:
'sample-project'
)
}
...
...
ee/spec/lib/banzai/filter/references/vulnerability_reference_filters_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
VulnerabilityReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
VulnerabilityReferenceFilter
do
include
FilterSpecHelper
let
(
:urls
)
{
Gitlab
::
Routing
.
url_helpers
}
...
...
lib/banzai/filter/references/abstract_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,445 +2,447 @@
module
Banzai
module
Filter
# Issues, merge requests, Snippets, Commits and Commit Ranges share
# similar functionality in reference filtering.
class
AbstractReferenceFilter
<
ReferenceFilter
include
CrossProjectReference
# REFERENCE_PLACEHOLDER is used for re-escaping HTML text except found
# reference (which we replace with placeholder during re-scaping). The
# random number helps ensure it's pretty close to unique. Since it's a
# transitory value (it never gets saved) we can initialize once, and it
# doesn't matter if it changes on a restart.
REFERENCE_PLACEHOLDER
=
"_reference_
#{
SecureRandom
.
hex
(
16
)
}
_"
REFERENCE_PLACEHOLDER_PATTERN
=
%r{
#{
REFERENCE_PLACEHOLDER
}
(
\d
+)}
.
freeze
def
self
.
object_class
# Implement in child class
# Example: MergeRequest
end
module
References
# Issues, merge requests, Snippets, Commits and Commit Ranges share
# similar functionality in reference filtering.
class
AbstractReferenceFilter
<
ReferenceFilter
include
CrossProjectReference
# REFERENCE_PLACEHOLDER is used for re-escaping HTML text except found
# reference (which we replace with placeholder during re-scaping). The
# random number helps ensure it's pretty close to unique. Since it's a
# transitory value (it never gets saved) we can initialize once, and it
# doesn't matter if it changes on a restart.
REFERENCE_PLACEHOLDER
=
"_reference_
#{
SecureRandom
.
hex
(
16
)
}
_"
REFERENCE_PLACEHOLDER_PATTERN
=
%r{
#{
REFERENCE_PLACEHOLDER
}
(
\d
+)}
.
freeze
def
self
.
object_class
# Implement in child class
# Example: MergeRequest
end
def
self
.
object_name
@object_name
||=
object_class
.
name
.
underscore
end
def
self
.
object_name
@object_name
||=
object_class
.
name
.
underscore
end
def
self
.
object_sym
@object_sym
||=
object_name
.
to_sym
end
def
self
.
object_sym
@object_sym
||=
object_name
.
to_sym
end
# Public: Find references in text (like `!123` for merge requests)
#
# AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches|
# object = find_object(project_ref, id)
# "<a href=...>#{object.to_reference}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, the Integer referenced object ID, an optional String
# of the external project reference, and all of the matchdata.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
if
ident
=
identifier
(
$~
)
yield
match
,
ident
,
$~
[
:project
],
$~
[
:namespace
],
$~
else
match
# Public: Find references in text (like `!123` for merge requests)
#
# AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches|
# object = find_object(project_ref, id)
# "<a href=...>#{object.to_reference}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, the Integer referenced object ID, an optional String
# of the external project reference, and all of the matchdata.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
,
pattern
=
object_class
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
if
ident
=
identifier
(
$~
)
yield
match
,
ident
,
$~
[
:project
],
$~
[
:namespace
],
$~
else
match
end
end
end
end
def
self
.
identifier
(
match_data
)
symbol
=
symbol_from_match
(
match_data
)
parse_symbol
(
symbol
,
match_data
)
if
object_class
.
reference_valid?
(
symbol
)
end
def
self
.
identifier
(
match_data
)
symbol
=
symbol_from_match
(
match_data
)
def
identifier
(
match_data
)
self
.
class
.
identifier
(
match_data
)
end
def
self
.
symbol_from_match
(
match
)
key
=
object_sym
match
[
key
]
if
match
.
names
.
include?
(
key
.
to_s
)
end
parse_symbol
(
symbol
,
match_data
)
if
object_class
.
reference_valid?
(
symbol
)
end
# Transform a symbol extracted from the text to a meaningful value
# In most cases these will be integers, so we call #to_i by default
#
# This method has the contract that if a string `ref` refers to a
# record `record`, then `parse_symbol(ref) == record_identifier(record)`.
def
self
.
parse_symbol
(
symbol
,
match_data
)
symbol
.
to_i
end
def
identifier
(
match_data
)
self
.
class
.
identifier
(
match_data
)
end
# We assume that most classes are identifying records by ID.
#
# This method has the contract that if a string `ref` refers to a
# record `record`, then `class.parse_symbol(ref) == record_identifier(record)`.
def
record_identifier
(
record
)
record
.
id
end
def
self
.
symbol_from_match
(
match
)
key
=
object_sym
match
[
key
]
if
match
.
names
.
include?
(
key
.
to_s
)
end
def
object_class
self
.
class
.
object_class
end
# Transform a symbol extracted from the text to a meaningful value
# In most cases these will be integers, so we call #to_i by default
#
# This method has the contract that if a string `ref` refers to a
# record `record`, then `parse_symbol(ref) == record_identifier(record)`.
def
self
.
parse_symbol
(
symbol
,
match_data
)
symbol
.
to_i
end
def
object_sym
self
.
class
.
object_sym
end
# We assume that most classes are identifying records by ID.
#
# This method has the contract that if a string `ref` refers to a
# record `record`, then `class.parse_symbol(ref) == record_identifier(record)`.
def
record_identifier
(
record
)
record
.
id
end
def
references_in
(
*
args
,
&
block
)
self
.
class
.
references_in
(
*
args
,
&
block
)
end
def
object_class
self
.
class
.
object_class
end
# Implement in child class
# Example: project.merge_requests.find
def
find_object
(
parent_object
,
id
)
end
def
object_sym
self
.
class
.
object_sym
end
# Override if the link reference pattern produces a different ID (global
# ID vs internal ID, for instance) to the regular reference pattern.
def
find_object_from_link
(
parent_object
,
id
)
find_object
(
parent_object
,
id
)
end
def
references_in
(
*
args
,
&
block
)
self
.
class
.
references_in
(
*
args
,
&
block
)
end
# Implement in child class
# Example: project_merge_request_url
def
url_for_object
(
object
,
parent_object
)
end
# Implement in child class
# Example: project.merge_requests.find
def
find_object
(
parent_object
,
id
)
end
def
find_object_cached
(
parent_object
,
id
)
cached_call
(
:banzai_find_object
,
id
,
path:
[
object_class
,
parent_object
.
id
])
do
# Override if the link reference pattern produces a different ID (global
# ID vs internal ID, for instance) to the regular reference pattern.
def
find_object_from_link
(
parent_object
,
id
)
find_object
(
parent_object
,
id
)
end
end
def
find_object_from_link_cached
(
parent_object
,
id
)
cached_call
(
:banzai_find_object_from_link
,
id
,
path:
[
object_class
,
parent_object
.
id
])
do
find_object_from_link
(
parent_object
,
id
)
# Implement in child class
# Example: project_merge_request_url
def
url_for_object
(
object
,
parent_object
)
end
end
def
from_ref_cached
(
ref
)
cached_call
(
"banzai_
#{
parent_type
}
_refs"
.
to_sym
,
ref
)
do
parent_from_ref
(
ref
)
def
find_object_cached
(
parent_object
,
id
)
cached_call
(
:banzai_find_object
,
id
,
path:
[
object_class
,
parent_object
.
id
])
do
find_object
(
parent_object
,
id
)
end
end
end
def
url_for_object_cached
(
object
,
parent_object
)
cached_call
(
:banzai_url_for_object
,
object
,
path:
[
object_class
,
parent_object
.
id
])
do
url_for_object
(
object
,
parent_object
)
def
find_object_from_link_cached
(
parent_object
,
id
)
cached_call
(
:banzai_find_object_from_link
,
id
,
path:
[
object_class
,
parent_object
.
id
])
do
find_object_from_link
(
parent_object
,
id
)
end
end
end
def
call
return
doc
unless
project
||
group
||
user
def
from_ref_cached
(
ref
)
cached_call
(
"banzai_
#{
parent_type
}
_refs"
.
to_sym
,
ref
)
do
parent_from_ref
(
ref
)
end
end
ref_pattern
=
object_class
.
reference_pattern
link_pattern
=
object_class
.
link_reference_pattern
def
url_for_object_cached
(
object
,
parent_object
)
cached_call
(
:banzai_url_for_object
,
object
,
path:
[
object_class
,
parent_object
.
id
])
do
url_for_object
(
object
,
parent_object
)
end
end
# Compile often used regexps only once outside of the loop
ref_pattern_anchor
=
/\A
#{
ref_pattern
}
\z/
link_pattern_start
=
/\A
#{
link_pattern
}
/
link_pattern_anchor
=
/\A
#{
link_pattern
}
\z/
def
call
return
doc
unless
project
||
group
||
user
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
&&
ref_pattern
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
object_link_filter
(
content
,
ref_pattern
)
end
ref_pattern
=
object_class
.
reference_pattern
link_pattern
=
object_class
.
link_reference_pattern
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
ref_pattern
&&
link
=~
ref_pattern_anchor
replace_link_node_with_href
(
node
,
index
,
link
)
do
object_link_filter
(
link
,
ref_pattern
,
link_content:
inner_html
)
end
# Compile often used regexps only once outside of the loop
ref_pattern_anchor
=
/\A
#{
ref_pattern
}
\z/
link_pattern_start
=
/\A
#{
link_pattern
}
/
link_pattern_anchor
=
/\A
#{
link_pattern
}
\z/
next
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
&&
ref_pattern
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
object_link_filter
(
content
,
ref_pattern
)
end
next
unless
link_pattern
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
ref_pattern
&&
link
=~
ref_pattern_anchor
replace_link_node_with_href
(
node
,
index
,
link
)
do
object_link_filter
(
link
,
ref_pattern
,
link_content:
inner_html
)
end
if
link
==
inner_html
&&
inner_html
=~
link_pattern_start
replace_link_node_with_text
(
node
,
index
)
do
object_link_filter
(
inner_html
,
link_pattern
,
link_reference:
true
)
next
end
next
end
next
unless
link_pattern
if
link
=~
link_pattern_anchor
replace_link_node_with_href
(
node
,
index
,
link
)
do
object_link_filter
(
link
,
link_pattern
,
link_content:
inner_html
,
link_reference:
true
)
if
link
==
inner_html
&&
inner_html
=~
link_pattern_start
replace_link_node_with_text
(
node
,
index
)
do
object_link_filter
(
inner_html
,
link_pattern
,
link_reference:
true
)
end
next
end
next
if
link
=~
link_pattern_anchor
replace_link_node_with_href
(
node
,
index
,
link
)
do
object_link_filter
(
link
,
link_pattern
,
link_content:
inner_html
,
link_reference:
true
)
end
next
end
end
end
end
end
doc
end
doc
end
# Replace references (like `!123` for merge requests) in text with links
# to the referenced object's details page.
#
# text - String text to replace references in.
# pattern - Reference pattern to match against.
# link_content - Original content of the link being replaced.
# link_reference - True if this was using the link reference pattern,
# false otherwise.
#
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def
object_link_filter
(
text
,
pattern
,
link_content:
nil
,
link_reference:
false
)
references_in
(
text
,
pattern
)
do
|
match
,
id
,
project_ref
,
namespace_ref
,
matches
|
parent_path
=
if
parent_type
==
:group
full_group_path
(
namespace_ref
)
else
full_project_path
(
namespace_ref
,
project_ref
)
end
parent
=
from_ref_cached
(
parent_path
)
if
parent
object
=
if
link_reference
find_object_from_link_cached
(
parent
,
id
)
else
find_object_cached
(
parent
,
id
)
end
end
# Replace references (like `!123` for merge requests) in text with links
# to the referenced object's details page.
#
# text - String text to replace references in.
# pattern - Reference pattern to match against.
# link_content - Original content of the link being replaced.
# link_reference - True if this was using the link reference pattern,
# false otherwise.
#
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def
object_link_filter
(
text
,
pattern
,
link_content:
nil
,
link_reference:
false
)
references_in
(
text
,
pattern
)
do
|
match
,
id
,
project_ref
,
namespace_ref
,
matches
|
parent_path
=
if
parent_type
==
:group
full_group_path
(
namespace_ref
)
else
full_project_path
(
namespace_ref
,
project_ref
)
end
parent
=
from_ref_cached
(
parent_path
)
if
parent
object
=
if
link_reference
find_object_from_link_cached
(
parent
,
id
)
else
find_object_cached
(
parent
,
id
)
end
end
if
object
title
=
object_link_title
(
object
,
matches
)
klass
=
reference_class
(
object_sym
)
if
object
title
=
object_link_title
(
object
,
matches
)
klass
=
reference_class
(
object_sym
)
data_attributes
=
data_attributes_for
(
link_content
||
match
,
parent
,
object
,
link_content:
!!
link_content
,
link_reference:
link_reference
)
data
=
data_attribute
(
data_attributes
)
data_attributes
=
data_attributes_for
(
link_content
||
match
,
parent
,
object
,
link_content:
!!
link_content
,
link_reference:
link_reference
)
data
=
data_attribute
(
data_attributes
)
url
=
if
matches
.
names
.
include?
(
"url"
)
&&
matches
[
:url
]
matches
[
:url
]
else
url_for_object_cached
(
object
,
parent
)
end
url
=
if
matches
.
names
.
include?
(
"url"
)
&&
matches
[
:url
]
matches
[
:url
]
else
url_for_object_cached
(
object
,
parent
)
end
content
=
link_content
||
object_link_text
(
object
,
matches
)
content
=
link_content
||
object_link_text
(
object
,
matches
)
link
=
%(<a href="#{url}" #{data}
title="#{escape_once(title)}"
class="#{klass}">#{content}</a>)
link
=
%(<a href="#{url}" #{data}
title="#{escape_once(title)}"
class="#{klass}">#{content}</a>)
wrap_link
(
link
,
object
)
else
match
wrap_link
(
link
,
object
)
else
match
end
end
end
end
def
wrap_link
(
link
,
object
)
link
end
def
wrap_link
(
link
,
object
)
link
end
def
data_attributes_for
(
text
,
parent
,
object
,
link_content:
false
,
link_reference:
false
)
object_parent_type
=
parent
.
is_a?
(
Group
)
?
:group
:
:project
def
data_attributes_for
(
text
,
parent
,
object
,
link_content:
false
,
link_reference:
false
)
object_parent_type
=
parent
.
is_a?
(
Group
)
?
:group
:
:project
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
object_parent_type
=>
parent
.
id
,
object_sym
=>
object
.
id
}
end
{
original:
escape_html_entities
(
text
),
link:
link_content
,
link_reference:
link_reference
,
object_parent_type
=>
parent
.
id
,
object_sym
=>
object
.
id
}
end
def
object_link_text_extras
(
object
,
matches
)
extras
=
[]
def
object_link_text_extras
(
object
,
matches
)
extras
=
[]
if
matches
.
names
.
include?
(
"anchor"
)
&&
matches
[
:anchor
]
&&
matches
[
:anchor
]
=~
/\A\#note_(\d+)\z/
extras
<<
"comment
#{
Regexp
.
last_match
(
1
)
}
"
end
if
matches
.
names
.
include?
(
"anchor"
)
&&
matches
[
:anchor
]
&&
matches
[
:anchor
]
=~
/\A\#note_(\d+)\z/
extras
<<
"comment
#{
Regexp
.
last_match
(
1
)
}
"
end
extension
=
matches
[
:extension
]
if
matches
.
names
.
include?
(
"extension"
)
extension
=
matches
[
:extension
]
if
matches
.
names
.
include?
(
"extension"
)
extras
<<
extension
if
extension
extras
<<
extension
if
extension
extras
end
extras
end
def
object_link_title
(
object
,
matches
)
object
.
title
end
def
object_link_title
(
object
,
matches
)
object
.
title
end
def
object_link_text
(
object
,
matches
)
parent
=
project
||
group
||
user
text
=
object
.
reference_link_text
(
parent
)
def
object_link_text
(
object
,
matches
)
parent
=
project
||
group
||
user
text
=
object
.
reference_link_text
(
parent
)
extras
=
object_link_text_extras
(
object
,
matches
)
text
+=
" (
#{
extras
.
join
(
", "
)
}
)"
if
extras
.
any?
extras
=
object_link_text_extras
(
object
,
matches
)
text
+=
" (
#{
extras
.
join
(
", "
)
}
)"
if
extras
.
any?
text
end
text
end
# Returns a Hash containing all object references (e.g. issue IDs) per the
# project they belong to.
def
references_per_parent
@references_per
||=
{}
@references_per
[
parent_type
]
||=
begin
refs
=
Hash
.
new
{
|
hash
,
key
|
hash
[
key
]
=
Set
.
new
}
regex
=
[
object_class
.
link_reference_pattern
,
object_class
.
reference_pattern
].
compact
.
reduce
{
|
a
,
b
|
Regexp
.
union
(
a
,
b
)
}
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
regex
)
do
path
=
if
parent_type
==
:project
full_project_path
(
$~
[
:namespace
],
$~
[
:project
])
else
full_group_path
(
$~
[
:group
])
end
if
ident
=
identifier
(
$~
)
refs
[
path
]
<<
ident
# Returns a Hash containing all object references (e.g. issue IDs) per the
# project they belong to.
def
references_per_parent
@references_per
||=
{}
@references_per
[
parent_type
]
||=
begin
refs
=
Hash
.
new
{
|
hash
,
key
|
hash
[
key
]
=
Set
.
new
}
regex
=
[
object_class
.
link_reference_pattern
,
object_class
.
reference_pattern
].
compact
.
reduce
{
|
a
,
b
|
Regexp
.
union
(
a
,
b
)
}
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
regex
)
do
path
=
if
parent_type
==
:project
full_project_path
(
$~
[
:namespace
],
$~
[
:project
])
else
full_group_path
(
$~
[
:group
])
end
if
ident
=
identifier
(
$~
)
refs
[
path
]
<<
ident
end
end
end
end
refs
refs
end
end
end
# Returns a Hash containing referenced projects grouped per their full
# path.
def
parent_per_reference
@per_reference
||=
{}
# Returns a Hash containing referenced projects grouped per their full
# path.
def
parent_per_reference
@per_reference
||=
{}
@per_reference
[
parent_type
]
||=
begin
refs
=
Set
.
new
@per_reference
[
parent_type
]
||=
begin
refs
=
Set
.
new
references_per_parent
.
each
do
|
ref
,
_
|
refs
<<
ref
end
references_per_parent
.
each
do
|
ref
,
_
|
refs
<<
ref
end
find_for_paths
(
refs
.
to_a
).
index_by
(
&
:full_path
)
find_for_paths
(
refs
.
to_a
).
index_by
(
&
:full_path
)
end
end
end
def
relation_for_paths
(
paths
)
klass
=
parent_type
.
to_s
.
camelize
.
constantize
result
=
klass
.
where_full_path_in
(
paths
)
return
result
if
parent_type
==
:group
def
relation_for_paths
(
paths
)
klass
=
parent_type
.
to_s
.
camelize
.
constantize
result
=
klass
.
where_full_path_in
(
paths
)
return
result
if
parent_type
==
:group
result
.
includes
(
:namespace
)
if
parent_type
==
:project
end
result
.
includes
(
:namespace
)
if
parent_type
==
:project
end
# Returns projects for the given paths.
def
find_for_paths
(
paths
)
if
Gitlab
::
SafeRequestStore
.
active?
cache
=
refs_cache
to_query
=
paths
-
cache
.
keys
# Returns projects for the given paths.
def
find_for_paths
(
paths
)
if
Gitlab
::
SafeRequestStore
.
active?
cache
=
refs_cache
to_query
=
paths
-
cache
.
keys
unless
to_query
.
empty?
records
=
relation_for_paths
(
to_query
)
unless
to_query
.
empty?
records
=
relation_for_paths
(
to_query
)
found
=
[]
records
.
each
do
|
record
|
ref
=
record
.
full_path
get_or_set_cache
(
cache
,
ref
)
{
record
}
found
<<
ref
end
found
=
[]
records
.
each
do
|
record
|
ref
=
record
.
full_path
get_or_set_cache
(
cache
,
ref
)
{
record
}
found
<<
ref
end
not_found
=
to_query
-
found
not_found
.
each
do
|
ref
|
get_or_set_cache
(
cache
,
ref
)
{
nil
}
not_found
=
to_query
-
found
not_found
.
each
do
|
ref
|
get_or_set_cache
(
cache
,
ref
)
{
nil
}
end
end
end
cache
.
slice
(
*
paths
).
values
.
compact
else
relation_for_paths
(
paths
)
cache
.
slice
(
*
paths
).
values
.
compact
else
relation_for_paths
(
paths
)
end
end
end
def
current_parent_path
@current_parent_path
||=
parent
&
.
full_path
end
def
current_parent_path
@current_parent_path
||=
parent
&
.
full_path
end
def
current_project_namespace_path
@current_project_namespace_path
||=
project
&
.
namespace
&
.
full_path
end
def
current_project_namespace_path
@current_project_namespace_path
||=
project
&
.
namespace
&
.
full_path
end
def
records_per_parent
@_records_per_project
||=
{}
def
records_per_parent
@_records_per_project
||=
{}
@_records_per_project
[
object_class
.
to_s
.
underscore
]
||=
begin
hash
=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
{}
}
@_records_per_project
[
object_class
.
to_s
.
underscore
]
||=
begin
hash
=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
{}
}
parent_per_reference
.
each
do
|
path
,
parent
|
record_ids
=
references_per_parent
[
path
]
parent_per_reference
.
each
do
|
path
,
parent
|
record_ids
=
references_per_parent
[
path
]
parent_records
(
parent
,
record_ids
).
each
do
|
record
|
hash
[
parent
][
record_identifier
(
record
)]
=
record
parent_records
(
parent
,
record_ids
).
each
do
|
record
|
hash
[
parent
][
record_identifier
(
record
)]
=
record
end
end
end
hash
hash
end
end
end
private
private
def
full_project_path
(
namespace
,
project_ref
)
return
current_parent_path
unless
project_ref
def
full_project_path
(
namespace
,
project_ref
)
return
current_parent_path
unless
project_ref
namespace_ref
=
namespace
||
current_project_namespace_path
"
#{
namespace_ref
}
/
#{
project_ref
}
"
end
namespace_ref
=
namespace
||
current_project_namespace_path
"
#{
namespace_ref
}
/
#{
project_ref
}
"
end
def
refs_cache
Gitlab
::
SafeRequestStore
[
"banzai_
#{
parent_type
}
_refs"
.
to_sym
]
||=
{}
end
def
refs_cache
Gitlab
::
SafeRequestStore
[
"banzai_
#{
parent_type
}
_refs"
.
to_sym
]
||=
{}
end
def
parent_type
:project
end
def
parent_type
:project
end
def
parent
parent_type
==
:project
?
project
:
group
end
def
parent
parent_type
==
:project
?
project
:
group
end
def
full_group_path
(
group_ref
)
return
current_parent_path
unless
group_ref
def
full_group_path
(
group_ref
)
return
current_parent_path
unless
group_ref
group_ref
end
group_ref
end
def
unescape_html_entities
(
text
)
CGI
.
unescapeHTML
(
text
.
to_s
)
end
def
unescape_html_entities
(
text
)
CGI
.
unescapeHTML
(
text
.
to_s
)
end
def
escape_html_entities
(
text
)
CGI
.
escapeHTML
(
text
.
to_s
)
end
def
escape_html_entities
(
text
)
CGI
.
escapeHTML
(
text
.
to_s
)
end
def
escape_with_placeholders
(
text
,
placeholder_data
)
escaped
=
escape_html_entities
(
text
)
def
escape_with_placeholders
(
text
,
placeholder_data
)
escaped
=
escape_html_entities
(
text
)
escaped
.
gsub
(
REFERENCE_PLACEHOLDER_PATTERN
)
do
|
match
|
placeholder_data
[
Regexp
.
last_match
(
1
).
to_i
]
escaped
.
gsub
(
REFERENCE_PLACEHOLDER_PATTERN
)
do
|
match
|
placeholder_data
[
Regexp
.
last_match
(
1
).
to_i
]
end
end
end
end
end
end
Banzai
::
Filter
::
AbstractReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter
::AbstractReferenceFilter'
)
Banzai
::
Filter
::
References
::
AbstractReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::References
::AbstractReferenceFilter'
)
lib/banzai/filter/references/alert_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,27 +2,29 @@
module
Banzai
module
Filter
class
AlertReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:alert
module
References
class
AlertReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:alert
def
self
.
object_class
AlertManagement
::
Alert
end
def
self
.
object_class
AlertManagement
::
Alert
end
def
self
.
object_sym
:alert
end
def
self
.
object_sym
:alert
end
def
parent_records
(
parent
,
ids
)
parent
.
alert_management_alerts
.
where
(
iid:
ids
.
to_a
)
end
def
parent_records
(
parent
,
ids
)
parent
.
alert_management_alerts
.
where
(
iid:
ids
.
to_a
)
end
def
url_for_object
(
alert
,
project
)
::
Gitlab
::
Routing
.
url_helpers
.
details_project_alert_management_url
(
project
,
alert
.
iid
,
only_path:
context
[
:only_path
]
)
def
url_for_object
(
alert
,
project
)
::
Gitlab
::
Routing
.
url_helpers
.
details_project_alert_management_url
(
project
,
alert
.
iid
,
only_path:
context
[
:only_path
]
)
end
end
end
end
...
...
lib/banzai/filter/references/commit_range_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,44 +2,46 @@
module
Banzai
module
Filter
# HTML filter that replaces commit range references with links.
#
# This filter supports cross-project references.
class
CommitRangeReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:commit_range
def
self
.
object_class
CommitRange
end
module
References
# HTML filter that replaces commit range references with links.
#
# This filter supports cross-project references.
class
CommitRangeReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:commit_range
def
self
.
object_class
CommitRange
end
def
self
.
references_in
(
text
,
pattern
=
CommitRange
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:commit_range
],
$~
[
:project
],
$~
[
:namespace
],
$~
def
self
.
references_in
(
text
,
pattern
=
CommitRange
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:commit_range
],
$~
[
:project
],
$~
[
:namespace
],
$~
end
end
end
def
initialize
(
*
args
)
super
def
initialize
(
*
args
)
super
@commit_map
=
{}
end
@commit_map
=
{}
end
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
range
=
CommitRange
.
new
(
id
,
project
)
range
=
CommitRange
.
new
(
id
,
project
)
range
.
valid_commits?
?
range
:
nil
end
range
.
valid_commits?
?
range
:
nil
end
def
url_for_object
(
range
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_compare_url
(
project
,
range
.
to_param
.
merge
(
only_path:
context
[
:only_path
]))
end
def
url_for_object
(
range
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_compare_url
(
project
,
range
.
to_param
.
merge
(
only_path:
context
[
:only_path
]))
end
def
object_link_title
(
range
,
matches
)
nil
def
object_link_title
(
range
,
matches
)
nil
end
end
end
end
...
...
lib/banzai/filter/references/commit_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,84 +2,86 @@
module
Banzai
module
Filter
# HTML filter that replaces commit references with links.
#
# This filter supports cross-project references.
class
CommitReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:commit
def
self
.
object_class
Commit
end
module
References
# HTML filter that replaces commit references with links.
#
# This filter supports cross-project references.
class
CommitReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:commit
def
self
.
object_class
Commit
end
def
self
.
references_in
(
text
,
pattern
=
Commit
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:commit
],
$~
[
:project
],
$~
[
:namespace
],
$~
def
self
.
references_in
(
text
,
pattern
=
Commit
.
reference_pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:commit
],
$~
[
:project
],
$~
[
:namespace
],
$~
end
end
end
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
&&
project
.
valid_repo?
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
&&
project
.
valid_repo?
_
,
record
=
records_per_parent
[
project
].
detect
{
|
k
,
_v
|
Gitlab
::
Git
.
shas_eql?
(
k
,
id
)
}
_
,
record
=
records_per_parent
[
project
].
detect
{
|
k
,
_v
|
Gitlab
::
Git
.
shas_eql?
(
k
,
id
)
}
record
end
record
end
def
referenced_merge_request_commit_shas
return
[]
unless
noteable
.
is_a?
(
MergeRequest
)
def
referenced_merge_request_commit_shas
return
[]
unless
noteable
.
is_a?
(
MergeRequest
)
@referenced_merge_request_commit_shas
||=
begin
referenced_shas
=
references_per_parent
.
values
.
reduce
(
:|
).
to_a
noteable
.
all_commit_shas
.
select
do
|
sha
|
referenced_shas
.
any?
{
|
ref
|
Gitlab
::
Git
.
shas_eql?
(
sha
,
ref
)
}
@referenced_merge_request_commit_shas
||=
begin
referenced_shas
=
references_per_parent
.
values
.
reduce
(
:|
).
to_a
noteable
.
all_commit_shas
.
select
do
|
sha
|
referenced_shas
.
any?
{
|
ref
|
Gitlab
::
Git
.
shas_eql?
(
sha
,
ref
)
}
end
end
end
end
# The default behaviour is `#to_i` - we just pass the hash through.
def
self
.
parse_symbol
(
sha_hash
,
_match
)
sha_hash
end
# The default behaviour is `#to_i` - we just pass the hash through.
def
self
.
parse_symbol
(
sha_hash
,
_match
)
sha_hash
end
def
url_for_object
(
commit
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
if
referenced_merge_request_commit_shas
.
include?
(
commit
.
id
)
h
.
diffs_project_merge_request_url
(
project
,
noteable
,
commit_id:
commit
.
id
,
only_path:
only_path?
)
else
h
.
project_commit_url
(
project
,
commit
,
only_path:
only_path?
)
def
url_for_object
(
commit
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
if
referenced_merge_request_commit_shas
.
include?
(
commit
.
id
)
h
.
diffs_project_merge_request_url
(
project
,
noteable
,
commit_id:
commit
.
id
,
only_path:
only_path?
)
else
h
.
project_commit_url
(
project
,
commit
,
only_path:
only_path?
)
end
end
end
def
object_link_text_extras
(
object
,
matches
)
extras
=
super
def
object_link_text_extras
(
object
,
matches
)
extras
=
super
path
=
matches
[
:path
]
if
matches
.
names
.
include?
(
"path"
)
if
path
==
'/builds'
extras
.
unshift
"builds"
end
path
=
matches
[
:path
]
if
matches
.
names
.
include?
(
"path"
)
if
path
==
'/builds'
extras
.
unshift
"builds"
end
extras
end
extras
end
private
private
def
parent_records
(
parent
,
ids
)
parent
.
commits_by
(
oids:
ids
.
to_a
)
end
def
parent_records
(
parent
,
ids
)
parent
.
commits_by
(
oids:
ids
.
to_a
)
end
def
noteable
context
[
:noteable
]
end
def
noteable
context
[
:noteable
]
end
def
only_path?
context
[
:only_path
]
def
only_path?
context
[
:only_path
]
end
end
end
end
...
...
lib/banzai/filter/references/design_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,105 +2,107 @@
module
Banzai
module
Filter
class
DesignReferenceFilter
<
AbstractReferenceFilter
class
Identifier
include
Comparable
attr_reader
:issue_iid
,
:filename
def
initialize
(
issue_iid
:,
filename
:)
@issue_iid
=
issue_iid
@filename
=
filename
module
References
class
DesignReferenceFilter
<
AbstractReferenceFilter
class
Identifier
include
Comparable
attr_reader
:issue_iid
,
:filename
def
initialize
(
issue_iid
:,
filename
:)
@issue_iid
=
issue_iid
@filename
=
filename
end
def
as_composite_id
(
id_for_iid
)
id
=
id_for_iid
[
issue_iid
]
return
unless
id
{
issue_id:
id
,
filename:
filename
}
end
def
<
=>
(
other
)
return
unless
other
.
is_a?
(
Identifier
)
[
issue_iid
,
filename
]
<=>
[
other
.
issue_iid
,
other
.
filename
]
end
alias_method
:eql?
,
:==
def
hash
[
issue_iid
,
filename
].
hash
end
end
def
as_composite_id
(
id_for_iid
)
id
=
id_for_iid
[
issue_iid
]
return
unless
id
self
.
reference_type
=
:design
{
issue_id:
id
,
filename:
filename
}
def
find_object
(
project
,
identifier
)
records_per_parent
[
project
][
identifier
]
end
def
<
=>
(
other
)
return
unless
other
.
is_a?
(
Identifier
)
def
parent_records
(
project
,
identifiers
)
return
[]
unless
project
.
design_management_enabled?
[
issue_iid
,
filename
]
<=>
[
other
.
issue_iid
,
other
.
filename
]
end
alias_method
:eql?
,
:==
iids
=
identifiers
.
map
(
&
:issue_iid
).
to_set
issues
=
project
.
issues
.
where
(
iid:
iids
)
id_for_iid
=
issues
.
index_by
(
&
:iid
).
transform_values
(
&
:id
)
issue_by_id
=
issues
.
index_by
(
&
:id
)
def
hash
[
issue_iid
,
filename
].
hash
designs
(
identifiers
,
id_for_iid
).
each
do
|
d
|
issue
=
issue_by_id
[
d
.
issue_id
]
# optimisation: assign values we have already fetched
d
.
project
=
project
d
.
issue
=
issue
end
end
end
self
.
reference_type
=
:design
def
find_object
(
project
,
identifier
)
records_per_parent
[
project
][
identifier
]
end
def
parent_records
(
project
,
identifiers
)
return
[]
unless
project
.
design_management_enabled?
iids
=
identifiers
.
map
(
&
:issue_iid
).
to_set
issues
=
project
.
issues
.
where
(
iid:
iids
)
id_for_iid
=
issues
.
index_by
(
&
:iid
).
transform_values
(
&
:id
)
issue_by_id
=
issues
.
index_by
(
&
:id
)
designs
(
identifiers
,
id_for_iid
).
each
do
|
d
|
issue
=
issue_by_id
[
d
.
issue_id
]
# optimisation: assign values we have already fetched
d
.
project
=
project
d
.
issue
=
issue
def
relation_for_paths
(
paths
)
super
.
includes
(
:route
,
:namespace
,
:group
)
end
end
def
relation_for_paths
(
paths
)
super
.
includes
(
:route
,
:namespace
,
:group
)
end
def
parent_type
:project
end
def
parent_type
:project
end
# optimisation to reuse the parent_per_reference query information
def
parent_from_ref
(
ref
)
parent_per_reference
[
ref
||
current_parent_path
]
end
# optimisation to reuse the parent_per_reference query information
def
parent_from_ref
(
ref
)
parent_per_reference
[
ref
||
current_parent_path
]
end
def
url_for_object
(
design
,
project
)
path_options
=
{
vueroute:
design
.
filename
}
Gitlab
::
Routing
.
url_helpers
.
designs_project_issue_path
(
project
,
design
.
issue
,
path_options
)
end
def
url_for_object
(
design
,
project
)
path_options
=
{
vueroute:
design
.
filename
}
Gitlab
::
Routing
.
url_helpers
.
designs_project_issue_path
(
project
,
design
.
issue
,
path_options
)
end
def
data_attributes_for
(
_text
,
_project
,
design
,
**
_kwargs
)
super
.
merge
(
issue:
design
.
issue_id
)
end
def
data_attributes_for
(
_text
,
_project
,
design
,
**
_kwargs
)
super
.
merge
(
issue:
design
.
issue_id
)
end
def
self
.
object_class
::
DesignManagement
::
Design
end
def
self
.
object_class
::
DesignManagement
::
Design
end
def
self
.
object_sym
:design
end
def
self
.
object_sym
:design
end
def
self
.
parse_symbol
(
raw
,
match_data
)
filename
=
match_data
[
:url_filename
]
iid
=
match_data
[
:issue
].
to_i
Identifier
.
new
(
filename:
CGI
.
unescape
(
filename
),
issue_iid:
iid
)
end
def
self
.
parse_symbol
(
raw
,
match_data
)
filename
=
match_data
[
:url_filename
]
iid
=
match_data
[
:issue
].
to_i
Identifier
.
new
(
filename:
CGI
.
unescape
(
filename
),
issue_iid:
iid
)
end
def
record_identifier
(
design
)
Identifier
.
new
(
filename:
design
.
filename
,
issue_iid:
design
.
issue
.
iid
)
end
def
record_identifier
(
design
)
Identifier
.
new
(
filename:
design
.
filename
,
issue_iid:
design
.
issue
.
iid
)
end
private
private
def
designs
(
identifiers
,
id_for_iid
)
identifiers
.
map
{
|
identifier
|
identifier
.
as_composite_id
(
id_for_iid
)
}
.
compact
.
in_groups_of
(
100
,
false
)
# limitation of by_issue_id_and_filename, so we batch
.
flat_map
{
|
ids
|
DesignManagement
::
Design
.
by_issue_id_and_filename
(
ids
)
}
def
designs
(
identifiers
,
id_for_iid
)
identifiers
.
map
{
|
identifier
|
identifier
.
as_composite_id
(
id_for_iid
)
}
.
compact
.
in_groups_of
(
100
,
false
)
# limitation of by_issue_id_and_filename, so we batch
.
flat_map
{
|
ids
|
DesignManagement
::
Design
.
by_issue_id_and_filename
(
ids
)
}
end
end
end
end
...
...
lib/banzai/filter/references/epic_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,21 +2,23 @@
module
Banzai
module
Filter
# The actual filter is implemented in the EE mixin
class
EpicReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:epic
module
References
# The actual filter is implemented in the EE mixin
class
EpicReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:epic
def
self
.
object_class
Epic
end
def
self
.
object_class
Epic
end
private
private
def
group
context
[
:group
]
||
context
[
:project
]
&
.
group
def
group
context
[
:group
]
||
context
[
:project
]
&
.
group
end
end
end
end
end
Banzai
::
Filter
::
EpicReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter
::EpicReferenceFilter'
)
Banzai
::
Filter
::
References
::
EpicReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::References
::EpicReferenceFilter'
)
lib/banzai/filter/references/external_issue_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,116 +2,118 @@
module
Banzai
module
Filter
# HTML filter that replaces external issue tracker references with links.
# References are ignored if the project doesn't use an external issue
# tracker.
#
# This filter does not support cross-project references.
class
ExternalIssueReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:external_issue
# Public: Find `JIRA-123` issue references in text
module
References
# HTML filter that replaces external issue tracker references with links.
# References are ignored if the project doesn't use an external issue
# tracker.
#
# ExternalIssueReferenceFilter.references_in(text, pattern) do |match, issue|
# "<a href=...>##{issue}</a>"
# end
#
# text - String text to search.
#
# Yields the String match and the String issue reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
,
pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:issue
]
# This filter does not support cross-project references.
class
ExternalIssueReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:external_issue
# Public: Find `JIRA-123` issue references in text
#
# ExternalIssueReferenceFilter.references_in(text, pattern) do |match, issue|
# "<a href=...>##{issue}</a>"
# end
#
# text - String text to search.
#
# Yields the String match and the String issue reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
,
pattern
)
text
.
gsub
(
pattern
)
do
|
match
|
yield
match
,
$~
[
:issue
]
end
end
end
def
call
# Early return if the project isn't using an external tracker
return
doc
if
project
.
nil?
||
default_issues_tracker?
def
call
# Early return if the project isn't using an external tracker
return
doc
if
project
.
nil?
||
default_issues_tracker?
ref_pattern
=
issue_reference_pattern
ref_start_pattern
=
/\A
#{
ref_pattern
}
\z/
ref_pattern
=
issue_reference_pattern
ref_start_pattern
=
/\A
#{
ref_pattern
}
\z/
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
issue_link_filter
(
content
)
end
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
issue_link_filter
(
content
)
end
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_start_pattern
replace_link_node_with_href
(
node
,
index
,
link
)
do
issue_link_filter
(
link
,
link_content:
inner_html
)
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_start_pattern
replace_link_node_with_href
(
node
,
index
,
link
)
do
issue_link_filter
(
link
,
link_content:
inner_html
)
end
end
end
end
end
end
doc
end
private
doc
end
# Replace `JIRA-123` issue references in text with links to the referenced
# issue's details page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `JIRA-123` references replaced with links. All
# links have `gfm` and `gfm-issue` class names attached for styling.
def
issue_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
,
issue_reference_pattern
)
do
|
match
,
id
|
url
=
url_for_issue
(
id
)
klass
=
reference_class
(
:issue
)
data
=
data_attribute
(
project:
project
.
id
,
external_issue:
id
)
content
=
link_content
||
match
%(<a href="#{url}" #{data}
title="#{escape_once(issue_title)}"
class="#{klass}">#{content}</a>)
private
# Replace `JIRA-123` issue references in text with links to the referenced
# issue's details page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `JIRA-123` references replaced with links. All
# links have `gfm` and `gfm-issue` class names attached for styling.
def
issue_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
,
issue_reference_pattern
)
do
|
match
,
id
|
url
=
url_for_issue
(
id
)
klass
=
reference_class
(
:issue
)
data
=
data_attribute
(
project:
project
.
id
,
external_issue:
id
)
content
=
link_content
||
match
%(<a href="#{url}" #{data}
title="#{escape_once(issue_title)}"
class="#{klass}">#{content}</a>)
end
end
end
def
url_for_issue
(
issue_id
)
return
''
if
project
.
nil?
def
url_for_issue
(
issue_id
)
return
''
if
project
.
nil?
url
=
if
only_path?
project
.
external_issue_tracker
.
issue_path
(
issue_id
)
else
project
.
external_issue_tracker
.
issue_url
(
issue_id
)
end
url
=
if
only_path?
project
.
external_issue_tracker
.
issue_path
(
issue_id
)
else
project
.
external_issue_tracker
.
issue_url
(
issue_id
)
end
# Ensure we return a valid URL to prevent possible XSS.
URI
.
parse
(
url
).
to_s
rescue
URI
::
InvalidURIError
''
end
# Ensure we return a valid URL to prevent possible XSS.
URI
.
parse
(
url
).
to_s
rescue
URI
::
InvalidURIError
''
end
def
default_issues_tracker?
external_issues_cached
(
:default_issues_tracker?
)
end
def
default_issues_tracker?
external_issues_cached
(
:default_issues_tracker?
)
end
def
issue_reference_pattern
external_issues_cached
(
:external_issue_reference_pattern
)
end
def
issue_reference_pattern
external_issues_cached
(
:external_issue_reference_pattern
)
end
def
project
context
[
:project
]
end
def
project
context
[
:project
]
end
def
issue_title
"Issue in
#{
project
.
external_issue_tracker
.
title
}
"
end
def
issue_title
"Issue in
#{
project
.
external_issue_tracker
.
title
}
"
end
def
external_issues_cached
(
attribute
)
cached_attributes
=
Gitlab
::
SafeRequestStore
[
:banzai_external_issues_tracker_attributes
]
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
{}
}
cached_attributes
[
project
.
id
][
attribute
]
=
project
.
public_send
(
attribute
)
if
cached_attributes
[
project
.
id
][
attribute
].
nil?
# rubocop:disable GitlabSecurity/PublicSend
cached_attributes
[
project
.
id
][
attribute
]
def
external_issues_cached
(
attribute
)
cached_attributes
=
Gitlab
::
SafeRequestStore
[
:banzai_external_issues_tracker_attributes
]
||=
Hash
.
new
{
|
h
,
k
|
h
[
k
]
=
{}
}
cached_attributes
[
project
.
id
][
attribute
]
=
project
.
public_send
(
attribute
)
if
cached_attributes
[
project
.
id
][
attribute
].
nil?
# rubocop:disable GitlabSecurity/PublicSend
cached_attributes
[
project
.
id
][
attribute
]
end
end
end
end
...
...
lib/banzai/filter/references/feature_flag_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,31 +2,33 @@
module
Banzai
module
Filter
class
FeatureFlagReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:feature_flag
module
References
class
FeatureFlagReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:feature_flag
def
self
.
object_class
Operations
::
FeatureFlag
end
def
self
.
object_class
Operations
::
FeatureFlag
end
def
self
.
object_sym
:feature_flag
end
def
self
.
object_sym
:feature_flag
end
def
parent_records
(
parent
,
ids
)
parent
.
operations_feature_flags
.
where
(
iid:
ids
.
to_a
)
end
def
parent_records
(
parent
,
ids
)
parent
.
operations_feature_flags
.
where
(
iid:
ids
.
to_a
)
end
def
url_for_object
(
feature_flag
,
project
)
::
Gitlab
::
Routing
.
url_helpers
.
edit_project_feature_flag_url
(
project
,
feature_flag
.
iid
,
only_path:
context
[
:only_path
]
)
end
def
url_for_object
(
feature_flag
,
project
)
::
Gitlab
::
Routing
.
url_helpers
.
edit_project_feature_flag_url
(
project
,
feature_flag
.
iid
,
only_path:
context
[
:only_path
]
)
end
def
object_link_title
(
object
,
matches
)
object
.
name
def
object_link_title
(
object
,
matches
)
object
.
name
end
end
end
end
...
...
lib/banzai/filter/references/issuable_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,17 +2,19 @@
module
Banzai
module
Filter
class
IssuableReferenceFilter
<
AbstractReferenceFilter
def
record_identifier
(
record
)
record
.
iid
.
to_i
end
module
References
class
IssuableReferenceFilter
<
AbstractReferenceFilter
def
record_identifier
(
record
)
record
.
iid
.
to_i
end
def
find_object
(
parent
,
iid
)
records_per_parent
[
parent
][
iid
]
end
def
find_object
(
parent
,
iid
)
records_per_parent
[
parent
][
iid
]
end
def
parent_from_ref
(
ref
)
parent_per_reference
[
ref
||
current_parent_path
]
def
parent_from_ref
(
ref
)
parent_per_reference
[
ref
||
current_parent_path
]
end
end
end
end
...
...
lib/banzai/filter/references/issue_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,55 +2,57 @@
module
Banzai
module
Filter
# HTML filter that replaces issue references with links. References to
# issues that do not exist are ignored.
#
# This filter supports cross-project references.
#
# When external issues tracker like Jira is activated we should not
# use issue reference pattern, but we should still be able
# to reference issues from other GitLab projects.
class
IssueReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:issue
def
self
.
object_class
Issue
end
module
References
# HTML filter that replaces issue references with links. References to
# issues that do not exist are ignored.
#
# This filter supports cross-project references.
#
# When external issues tracker like Jira is activated we should not
# use issue reference pattern, but we should still be able
# to reference issues from other GitLab projects.
class
IssueReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:issue
def
self
.
object_class
Issue
end
def
url_for_object
(
issue
,
project
)
return
issue_path
(
issue
,
project
)
if
only_path?
def
url_for_object
(
issue
,
project
)
return
issue_path
(
issue
,
project
)
if
only_path?
issue_url
(
issue
,
project
)
end
issue_url
(
issue
,
project
)
end
def
parent_records
(
parent
,
ids
)
parent
.
issues
.
where
(
iid:
ids
.
to_a
)
end
def
parent_records
(
parent
,
ids
)
parent
.
issues
.
where
(
iid:
ids
.
to_a
)
end
def
object_link_text_extras
(
issue
,
matches
)
super
+
design_link_extras
(
issue
,
matches
.
named_captures
[
'path'
])
end
def
object_link_text_extras
(
issue
,
matches
)
super
+
design_link_extras
(
issue
,
matches
.
named_captures
[
'path'
])
end
private
private
def
issue_path
(
issue
,
project
)
Gitlab
::
Routing
.
url_helpers
.
namespace_project_issue_path
(
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
issue
.
iid
)
end
def
issue_path
(
issue
,
project
)
Gitlab
::
Routing
.
url_helpers
.
namespace_project_issue_path
(
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
issue
.
iid
)
end
def
issue_url
(
issue
,
project
)
Gitlab
::
Routing
.
url_helpers
.
namespace_project_issue_url
(
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
issue
.
iid
)
end
def
issue_url
(
issue
,
project
)
Gitlab
::
Routing
.
url_helpers
.
namespace_project_issue_url
(
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
issue
.
iid
)
end
def
design_link_extras
(
issue
,
path
)
if
path
==
'/designs'
&&
read_designs?
(
issue
)
[
'designs'
]
else
[]
def
design_link_extras
(
issue
,
path
)
if
path
==
'/designs'
&&
read_designs?
(
issue
)
[
'designs'
]
else
[]
end
end
end
def
read_designs?
(
issue
)
issue
.
project
.
design_management_enabled?
def
read_designs?
(
issue
)
issue
.
project
.
design_management_enabled?
end
end
end
end
...
...
lib/banzai/filter/references/iteration_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,15 +2,17 @@
module
Banzai
module
Filter
# The actual filter is implemented in the EE mixin
class
IterationReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:iteration
module
References
# The actual filter is implemented in the EE mixin
class
IterationReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:iteration
def
self
.
object_class
Iteration
def
self
.
object_class
Iteration
end
end
end
end
end
Banzai
::
Filter
::
IterationReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter
::IterationReferenceFilter'
)
Banzai
::
Filter
::
References
::
IterationReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::References
::IterationReferenceFilter'
)
lib/banzai/filter/references/label_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,128 +2,130 @@
module
Banzai
module
Filter
# HTML filter that replaces label references with links.
class
LabelReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:label
module
References
# HTML filter that replaces label references with links.
class
LabelReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:label
def
self
.
object_class
Label
end
def
find_object
(
parent_object
,
id
)
find_labels
(
parent_object
).
find
(
id
)
end
def
references_in
(
text
,
pattern
=
Label
.
reference_pattern
)
labels
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
namespace
,
project
=
$~
[
:namespace
],
$~
[
:project
]
project_path
=
full_project_path
(
namespace
,
project
)
label
=
find_label_cached
(
project_path
,
$~
[
:label_id
],
$~
[
:label_name
])
def
self
.
object_class
Label
end
if
label
labels
[
label
.
id
]
=
yield
match
,
label
.
id
,
project
,
namespace
,
$~
"
#{
REFERENCE_PLACEHOLDER
}#{
label
.
id
}
"
else
match
end
def
find_object
(
parent_object
,
id
)
find_labels
(
parent_object
).
find
(
id
)
end
return
text
if
labels
.
empty?
def
references_in
(
text
,
pattern
=
Label
.
reference_pattern
)
labels
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
namespace
,
project
=
$~
[
:namespace
],
$~
[
:project
]
project_path
=
full_project_path
(
namespace
,
project
)
label
=
find_label_cached
(
project_path
,
$~
[
:label_id
],
$~
[
:label_name
])
if
label
labels
[
label
.
id
]
=
yield
match
,
label
.
id
,
project
,
namespace
,
$~
"
#{
REFERENCE_PLACEHOLDER
}#{
label
.
id
}
"
else
match
end
end
escape_with_placeholders
(
unescaped_html
,
labels
)
end
return
text
if
labels
.
empty?
def
find_label_cached
(
parent_ref
,
label_id
,
label_name
)
cached_call
(
:banzai_find_label_cached
,
label_name
&
.
tr
(
'"'
,
''
)
||
label_id
,
path:
[
object_class
,
parent_ref
])
do
find_label
(
parent_ref
,
label_id
,
label_name
)
escape_with_placeholders
(
unescaped_html
,
labels
)
end
end
def
find_label
(
parent_ref
,
label_id
,
label_name
)
parent
=
parent_from_ref
(
parent_ref
)
return
unless
parent
def
find_label_cached
(
parent_ref
,
label_id
,
label_name
)
cached_call
(
:banzai_find_label_cached
,
label_name
&
.
tr
(
'"'
,
''
)
||
label_id
,
path:
[
object_class
,
parent_ref
])
do
find_label
(
parent_ref
,
label_id
,
label_name
)
end
end
label_params
=
label_params
(
label_id
,
label_name
)
find_labels
(
parent
).
find_by
(
label_params
)
end
def
find_label
(
parent_ref
,
label_id
,
label_name
)
parent
=
parent_from_ref
(
parent_ref
)
return
unless
parent
def
find_labels
(
parent
)
params
=
if
parent
.
is_a?
(
Group
)
{
group_id:
parent
.
id
,
include_ancestor_groups:
true
,
only_group_labels:
true
}
else
{
project:
parent
,
include_ancestor_groups:
true
}
end
LabelsFinder
.
new
(
nil
,
params
).
execute
(
skip_authorization:
true
)
end
label_params
=
label_params
(
label_id
,
label_name
)
find_labels
(
parent
).
find_by
(
label_params
)
end
# Parameters to pass to `Label.find_by` based on the given arguments
#
# id - Integer ID to pass. If present, returns {id: id}
# name - String name to pass. If `id` is absent, finds by name without
# surrounding quotes.
#
# Returns a Hash.
def
label_params
(
id
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
id:
id
.
to_i
}
def
find_labels
(
parent
)
params
=
if
parent
.
is_a?
(
Group
)
{
group_id:
parent
.
id
,
include_ancestor_groups:
true
,
only_group_labels:
true
}
else
{
project:
parent
,
include_ancestor_groups:
true
}
end
LabelsFinder
.
new
(
nil
,
params
).
execute
(
skip_authorization:
true
)
end
end
def
url_for_object
(
label
,
parent
)
label_url_method
=
if
context
[
:label_url_method
]
context
[
:label_url_method
]
elsif
parent
.
is_a?
(
Project
)
:project_issues_url
# Parameters to pass to `Label.find_by` based on the given arguments
#
# id - Integer ID to pass. If present, returns {id: id}
# name - String name to pass. If `id` is absent, finds by name without
# surrounding quotes.
#
# Returns a Hash.
def
label_params
(
id
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
id:
id
.
to_i
}
end
end
return
unless
label_url_method
def
url_for_object
(
label
,
parent
)
label_url_method
=
if
context
[
:label_url_method
]
context
[
:label_url_method
]
elsif
parent
.
is_a?
(
Project
)
:project_issues_url
end
Gitlab
::
Routing
.
url_helpers
.
public_send
(
label_url_method
,
parent
,
label_name:
label
.
name
,
only_path:
context
[
:only_path
])
# rubocop:disable GitlabSecurity/PublicSend
end
return
unless
label_url_method
def
object_link_text
(
object
,
matches
)
label_suffix
=
''
parent
=
project
||
group
Gitlab
::
Routing
.
url_helpers
.
public_send
(
label_url_method
,
parent
,
label_name:
label
.
name
,
only_path:
context
[
:only_path
])
# rubocop:disable GitlabSecurity/PublicSend
end
if
project
||
full_path_ref?
(
matches
)
project_path
=
full_project_path
(
matches
[
:namespace
],
matches
[
:project
])
parent_from_ref
=
from_ref_cached
(
project_path
)
reference
=
parent_from_ref
.
to_human_reference
(
parent
)
def
object_link_text
(
object
,
matches
)
label_suffix
=
''
parent
=
project
||
group
label_suffix
=
" <i>in
#{
ERB
::
Util
.
html_escape
(
reference
)
}
</i>"
if
reference
.
present?
end
if
project
||
full_path_ref?
(
matches
)
project_path
=
full_project_path
(
matches
[
:namespace
],
matches
[
:project
])
parent_from_ref
=
from_ref_cached
(
project_path
)
reference
=
parent_from_ref
.
to_human_reference
(
parent
)
presenter
=
object
.
present
(
issuable_subject:
parent
)
LabelsHelper
.
render_colored_label
(
presenter
,
suffix:
label_suffix
)
end
label_suffix
=
" <i>in
#{
ERB
::
Util
.
html_escape
(
reference
)
}
</i>"
if
reference
.
present?
end
def
wrap_link
(
link
,
label
)
presenter
=
label
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
wrap_label_html
(
link
,
small:
true
,
label:
presenter
)
end
presenter
=
object
.
present
(
issuable_subject:
parent
)
LabelsHelper
.
render_colored_label
(
presenter
,
suffix:
label_suffix
)
end
def
full_path_ref?
(
matches
)
matches
[
:namespace
]
&&
matches
[
:project
]
end
def
wrap_link
(
link
,
label
)
presenter
=
label
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
wrap_label_html
(
link
,
small:
true
,
label:
presenter
)
end
def
reference_class
(
type
,
tooltip:
true
)
super
+
' gl-link gl-label-link'
end
def
full_path_ref?
(
matches
)
matches
[
:namespace
]
&&
matches
[
:project
]
end
def
reference_class
(
type
,
tooltip:
true
)
super
+
' gl-link gl-label-link'
end
def
object_link_title
(
object
,
matches
)
presenter
=
object
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
label_tooltip_title
(
presenter
)
def
object_link_title
(
object
,
matches
)
presenter
=
object
.
present
(
issuable_subject:
project
||
group
)
LabelsHelper
.
label_tooltip_title
(
presenter
)
end
end
end
end
end
Banzai
::
Filter
::
LabelReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter
::LabelReferenceFilter'
)
Banzai
::
Filter
::
References
::
LabelReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::References
::LabelReferenceFilter'
)
lib/banzai/filter/references/merge_request_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,95 +2,97 @@
module
Banzai
module
Filter
# HTML filter that replaces merge request references with links. References
# to merge requests that do not exist are ignored.
#
# This filter supports cross-project references.
class
MergeRequestReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:merge_request
def
self
.
object_class
MergeRequest
end
module
References
# HTML filter that replaces merge request references with links. References
# to merge requests that do not exist are ignored.
#
# This filter supports cross-project references.
class
MergeRequestReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:merge_request
def
self
.
object_class
MergeRequest
end
def
url_for_object
(
mr
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_merge_request_url
(
project
,
mr
,
only_path:
context
[
:only_path
])
end
def
url_for_object
(
mr
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_merge_request_url
(
project
,
mr
,
only_path:
context
[
:only_path
])
end
def
object_link_title
(
object
,
matches
)
# The method will return `nil` if object is not a commit
# allowing for properly handling the extended MR Tooltip
object_link_commit_title
(
object
,
matches
)
end
def
object_link_title
(
object
,
matches
)
# The method will return `nil` if object is not a commit
# allowing for properly handling the extended MR Tooltip
object_link_commit_title
(
object
,
matches
)
end
def
object_link_text_extras
(
object
,
matches
)
extras
=
super
def
object_link_text_extras
(
object
,
matches
)
extras
=
super
if
commit_ref
=
object_link_commit_ref
(
object
,
matches
)
klass
=
reference_class
(
:commit
,
tooltip:
false
)
commit_ref_tag
=
%(<span class="#{klass}">#{commit_ref}</span>)
if
commit_ref
=
object_link_commit_ref
(
object
,
matches
)
klass
=
reference_class
(
:commit
,
tooltip:
false
)
commit_ref_tag
=
%(<span class="#{klass}">#{commit_ref}</span>)
return
extras
.
unshift
(
commit_ref_tag
)
end
return
extras
.
unshift
(
commit_ref_tag
)
end
path
=
matches
[
:path
]
if
matches
.
names
.
include?
(
"path"
)
path
=
matches
[
:path
]
if
matches
.
names
.
include?
(
"path"
)
case
path
when
'/diffs'
extras
.
unshift
"diffs"
when
'/commits'
extras
.
unshift
"commits"
when
'/builds'
extras
.
unshift
"builds"
end
case
path
when
'/diffs'
extras
.
unshift
"diffs"
when
'/commits'
extras
.
unshift
"commits"
when
'/builds'
extras
.
unshift
"builds"
end
extras
end
extras
end
def
parent_records
(
parent
,
ids
)
parent
.
merge_requests
.
where
(
iid:
ids
.
to_a
)
.
includes
(
target_project: :namespace
)
end
def
parent_records
(
parent
,
ids
)
parent
.
merge_requests
.
where
(
iid:
ids
.
to_a
)
.
includes
(
target_project: :namespace
)
end
def
reference_class
(
object_sym
,
options
=
{})
super
(
object_sym
,
tooltip:
false
)
end
def
reference_class
(
object_sym
,
options
=
{})
super
(
object_sym
,
tooltip:
false
)
end
def
data_attributes_for
(
text
,
parent
,
object
,
**
data
)
super
.
merge
(
project_path:
parent
.
full_path
,
iid:
object
.
iid
,
mr_title:
object
.
title
)
end
def
data_attributes_for
(
text
,
parent
,
object
,
**
data
)
super
.
merge
(
project_path:
parent
.
full_path
,
iid:
object
.
iid
,
mr_title:
object
.
title
)
end
private
private
def
object_link_commit_title
(
object
,
matches
)
object_link_commit
(
object
,
matches
)
&
.
title
end
def
object_link_commit_title
(
object
,
matches
)
object_link_commit
(
object
,
matches
)
&
.
title
end
def
object_link_commit_ref
(
object
,
matches
)
object_link_commit
(
object
,
matches
)
&
.
short_id
end
def
object_link_commit_ref
(
object
,
matches
)
object_link_commit
(
object
,
matches
)
&
.
short_id
end
def
object_link_commit
(
object
,
matches
)
return
unless
matches
.
names
.
include?
(
'query'
)
&&
query
=
matches
[
:query
]
def
object_link_commit
(
object
,
matches
)
return
unless
matches
.
names
.
include?
(
'query'
)
&&
query
=
matches
[
:query
]
# Removes leading "?". CGI.parse expects "arg1&arg2&arg3"
params
=
CGI
.
parse
(
query
.
sub
(
/^\?/
,
''
))
# Removes leading "?". CGI.parse expects "arg1&arg2&arg3"
params
=
CGI
.
parse
(
query
.
sub
(
/^\?/
,
''
))
return
unless
commit_sha
=
params
[
'commit_id'
]
&
.
first
return
unless
commit_sha
=
params
[
'commit_id'
]
&
.
first
if
commit
=
find_commit_by_sha
(
object
,
commit_sha
)
Commit
.
from_hash
(
commit
.
to_hash
,
object
.
project
)
if
commit
=
find_commit_by_sha
(
object
,
commit_sha
)
Commit
.
from_hash
(
commit
.
to_hash
,
object
.
project
)
end
end
end
def
find_commit_by_sha
(
object
,
commit_sha
)
@all_commits
||=
{}
@all_commits
[
object
.
id
]
||=
object
.
all_commits
def
find_commit_by_sha
(
object
,
commit_sha
)
@all_commits
||=
{}
@all_commits
[
object
.
id
]
||=
object
.
all_commits
@all_commits
[
object
.
id
].
find
{
|
commit
|
commit
.
sha
==
commit_sha
}
@all_commits
[
object
.
id
].
find
{
|
commit
|
commit
.
sha
==
commit_sha
}
end
end
end
end
...
...
lib/banzai/filter/references/milestone_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,136 +2,138 @@
module
Banzai
module
Filter
# HTML filter that replaces milestone references with links.
class
MilestoneReferenceFilter
<
AbstractReferenceFilter
include
Gitlab
::
Utils
::
StrongMemoize
module
References
# HTML filter that replaces milestone references with links.
class
MilestoneReferenceFilter
<
AbstractReferenceFilter
include
Gitlab
::
Utils
::
StrongMemoize
self
.
reference_type
=
:milestone
self
.
reference_type
=
:milestone
def
self
.
object_class
Milestone
end
def
self
.
object_class
Milestone
end
# Links to project milestones contain the IID, but when we're handling
# 'regular' references, we need to use the global ID to disambiguate
# between group and project milestones.
def
find_object
(
parent
,
id
)
return
unless
valid_context?
(
parent
)
# Links to project milestones contain the IID, but when we're handling
# 'regular' references, we need to use the global ID to disambiguate
# between group and project milestones.
def
find_object
(
parent
,
id
)
return
unless
valid_context?
(
parent
)
find_milestone_with_finder
(
parent
,
id:
id
)
end
find_milestone_with_finder
(
parent
,
id:
id
)
end
def
find_object_from_link
(
parent
,
iid
)
return
unless
valid_context?
(
parent
)
def
find_object_from_link
(
parent
,
iid
)
return
unless
valid_context?
(
parent
)
find_milestone_with_finder
(
parent
,
iid:
iid
)
end
def
valid_context?
(
parent
)
strong_memoize
(
:valid_context
)
do
group_context?
(
parent
)
||
project_context?
(
parent
)
find_milestone_with_finder
(
parent
,
iid:
iid
)
end
end
def
group_context?
(
parent
)
strong_memoize
(
:group_context
)
do
parent
.
is_a?
(
Group
)
def
valid_context?
(
parent
)
strong_memoize
(
:valid_context
)
do
group_context?
(
parent
)
||
project_context?
(
parent
)
end
end
end
def
project_context?
(
parent
)
strong_memoize
(
:project_context
)
do
parent
.
is_a?
(
Project
)
def
group_context?
(
parent
)
strong_memoize
(
:group_context
)
do
parent
.
is_a?
(
Group
)
end
end
end
def
references_in
(
text
,
pattern
=
Milestone
.
reference_pattern
)
# We'll handle here the references that follow the `reference_pattern`.
# Other patterns (for example, the link pattern) are handled by the
# default implementation.
return
super
(
text
,
pattern
)
if
pattern
!=
Milestone
.
reference_pattern
milestones
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
milestone
=
find_milestone
(
$~
[
:project
],
$~
[
:namespace
],
$~
[
:milestone_iid
],
$~
[
:milestone_name
])
if
milestone
milestones
[
milestone
.
id
]
=
yield
match
,
milestone
.
id
,
$~
[
:project
],
$~
[
:namespace
],
$~
"
#{
REFERENCE_PLACEHOLDER
}#{
milestone
.
id
}
"
else
match
def
project_context?
(
parent
)
strong_memoize
(
:project_context
)
do
parent
.
is_a?
(
Project
)
end
end
return
text
if
milestones
.
empty?
def
references_in
(
text
,
pattern
=
Milestone
.
reference_pattern
)
# We'll handle here the references that follow the `reference_pattern`.
# Other patterns (for example, the link pattern) are handled by the
# default implementation.
return
super
(
text
,
pattern
)
if
pattern
!=
Milestone
.
reference_pattern
milestones
=
{}
unescaped_html
=
unescape_html_entities
(
text
).
gsub
(
pattern
)
do
|
match
|
milestone
=
find_milestone
(
$~
[
:project
],
$~
[
:namespace
],
$~
[
:milestone_iid
],
$~
[
:milestone_name
])
if
milestone
milestones
[
milestone
.
id
]
=
yield
match
,
milestone
.
id
,
$~
[
:project
],
$~
[
:namespace
],
$~
"
#{
REFERENCE_PLACEHOLDER
}#{
milestone
.
id
}
"
else
match
end
end
escape_with_placeholders
(
unescaped_html
,
milestones
)
end
return
text
if
milestones
.
empty?
def
find_milestone
(
project_ref
,
namespace_ref
,
milestone_id
,
milestone_name
)
project_path
=
full_project_path
(
namespace_ref
,
project_ref
)
escape_with_placeholders
(
unescaped_html
,
milestones
)
end
# Returns group if project is not found by path
parent
=
parent_from_ref
(
project_path
)
def
find_milestone
(
project_ref
,
namespace_ref
,
milestone_id
,
milestone_name
)
project_path
=
full_project_path
(
namespace_ref
,
project_ref
)
return
unless
parent
# Returns group if project is not found by path
parent
=
parent_from_ref
(
project_path
)
milestone_params
=
milestone_params
(
milestone_id
,
milestone_name
)
return
unless
parent
find_milestone_with_finder
(
parent
,
milestone_params
)
end
milestone_params
=
milestone_params
(
milestone_id
,
milestone_name
)
def
milestone_params
(
iid
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
iid:
iid
.
to_i
}
find_milestone_with_finder
(
parent
,
milestone_params
)
end
end
def
find_milestone_with_finder
(
parent
,
params
)
finder_params
=
milestone_finder_params
(
parent
,
params
[
:iid
].
present?
)
def
milestone_params
(
iid
,
name
)
if
name
{
name:
name
.
tr
(
'"'
,
''
)
}
else
{
iid:
iid
.
to_i
}
end
end
MilestonesFinder
.
new
(
finder_params
).
find_by
(
params
)
end
def
find_milestone_with_finder
(
parent
,
params
)
finder_params
=
milestone_finder_params
(
parent
,
params
[
:iid
].
present?
)
def
milestone_finder_params
(
parent
,
find_by_iid
)
{
order:
nil
,
state:
'all'
}.
tap
do
|
params
|
params
[
:project_ids
]
=
parent
.
id
if
project_context?
(
parent
)
MilestonesFinder
.
new
(
finder_params
).
find_by
(
params
)
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
# 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
end
def
self_and_ancestors_ids
(
parent
)
if
group_context?
(
parent
)
parent
.
self_and_ancestors
.
select
(
:id
)
elsif
project_context?
(
parent
)
parent
.
group
&
.
self_and_ancestors
&
.
select
(
:id
)
def
self_and_ancestors_ids
(
parent
)
if
group_context?
(
parent
)
parent
.
self_and_ancestors
.
select
(
:id
)
elsif
project_context?
(
parent
)
parent
.
group
&
.
self_and_ancestors
&
.
select
(
:id
)
end
end
end
def
url_for_object
(
milestone
,
project
)
Gitlab
::
Routing
.
url_helpers
.
milestone_url
(
milestone
,
only_path:
context
[
:only_path
])
end
def
url_for_object
(
milestone
,
project
)
Gitlab
::
Routing
.
url_helpers
.
milestone_url
(
milestone
,
only_path:
context
[
:only_path
])
end
def
object_link_text
(
object
,
matches
)
milestone_link
=
escape_once
(
super
)
reference
=
object
.
project
&
.
to_reference_base
(
project
)
def
object_link_text
(
object
,
matches
)
milestone_link
=
escape_once
(
super
)
reference
=
object
.
project
&
.
to_reference_base
(
project
)
if
reference
.
present?
"
#{
milestone_link
}
<i>in
#{
reference
}
</i>"
.
html_safe
else
milestone_link
if
reference
.
present?
"
#{
milestone_link
}
<i>in
#{
reference
}
</i>"
.
html_safe
else
milestone_link
end
end
end
def
object_link_title
(
object
,
matches
)
nil
def
object_link_title
(
object
,
matches
)
nil
end
end
end
end
...
...
lib/banzai/filter/references/project_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,115 +2,117 @@
module
Banzai
module
Filter
# HTML filter that replaces project references with links.
class
ProjectReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:project
# Public: Find `namespace/project>` project references in text
#
# ProjectReferenceFilter.references_in(text) do |match, project|
# "<a href=...>#{project}></a>"
# end
#
# text - String text to search.
#
# Yields the String match, and the String project name.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
Project
.
markdown_reference_pattern
)
do
|
match
|
yield
match
,
"
#{
$~
[
:namespace
]
}
/
#{
$~
[
:project
]
}
"
module
References
# HTML filter that replaces project references with links.
class
ProjectReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:project
# Public: Find `namespace/project>` project references in text
#
# ProjectReferenceFilter.references_in(text) do |match, project|
# "<a href=...>#{project}></a>"
# end
#
# text - String text to search.
#
# Yields the String match, and the String project name.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
Project
.
markdown_reference_pattern
)
do
|
match
|
yield
match
,
"
#{
$~
[
:namespace
]
}
/
#{
$~
[
:project
]
}
"
end
end
end
def
call
ref_pattern
=
Project
.
markdown_reference_pattern
ref_pattern_start
=
/\A
#{
ref_pattern
}
\z/
def
call
ref_pattern
=
Project
.
markdown_reference_pattern
ref_pattern_start
=
/\A
#{
ref_pattern
}
\z/
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
project_link_filter
(
content
)
end
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_pattern_start
replace_link_node_with_href
(
node
,
index
,
link
)
do
project_link_filter
(
link
,
link_content:
inner_html
)
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
project_link_filter
(
content
)
end
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_pattern_start
replace_link_node_with_href
(
node
,
index
,
link
)
do
project_link_filter
(
link
,
link_content:
inner_html
)
end
end
end
end
end
end
doc
end
doc
end
# Replace `namespace/project>` project references in text with links to the referenced
# project page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `namespace/project>` references replaced with links. All links
# have `gfm` and `gfm-project` class names attached for styling.
def
project_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
)
do
|
match
,
project_path
|
cached_call
(
:banzai_url_for_object
,
match
,
path:
[
Project
,
project_path
.
downcase
])
do
if
project
=
projects_hash
[
project_path
.
downcase
]
link_to_project
(
project
,
link_content:
link_content
)
||
match
else
match
# Replace `namespace/project>` project references in text with links to the referenced
# project page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `namespace/project>` references replaced with links. All links
# have `gfm` and `gfm-project` class names attached for styling.
def
project_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
)
do
|
match
,
project_path
|
cached_call
(
:banzai_url_for_object
,
match
,
path:
[
Project
,
project_path
.
downcase
])
do
if
project
=
projects_hash
[
project_path
.
downcase
]
link_to_project
(
project
,
link_content:
link_content
)
||
match
else
match
end
end
end
end
end
# Returns a Hash containing all Project objects for the project
# references in the current document.
#
# The keys of this Hash are the project paths, the values the
# corresponding Project objects.
def
projects_hash
@projects
||=
Project
.
eager_load
(
:route
,
namespace:
[
:route
])
.
where_full_path_in
(
projects
)
.
index_by
(
&
:full_path
)
.
transform_keys
(
&
:downcase
)
end
# Returns a Hash containing all Project objects for the project
# references in the current document.
#
# The keys of this Hash are the project paths, the values the
# corresponding Project objects.
def
projects_hash
@projects
||=
Project
.
eager_load
(
:route
,
namespace:
[
:route
])
.
where_full_path_in
(
projects
)
.
index_by
(
&
:full_path
)
.
transform_keys
(
&
:downcase
)
end
# Returns all projects referenced in the current document.
def
projects
refs
=
Set
.
new
# Returns all projects referenced in the current document.
def
projects
refs
=
Set
.
new
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
Project
.
markdown_reference_pattern
)
do
refs
<<
"
#{
$~
[
:namespace
]
}
/
#{
$~
[
:project
]
}
"
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
Project
.
markdown_reference_pattern
)
do
refs
<<
"
#{
$~
[
:namespace
]
}
/
#{
$~
[
:project
]
}
"
end
end
end
refs
.
to_a
end
refs
.
to_a
end
private
private
def
urls
Gitlab
::
Routing
.
url_helpers
end
def
urls
Gitlab
::
Routing
.
url_helpers
end
def
link_class
reference_class
(
:project
)
end
def
link_class
reference_class
(
:project
)
end
def
link_to_project
(
project
,
link_content:
nil
)
url
=
urls
.
project_url
(
project
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
project:
project
.
id
)
content
=
link_content
||
project
.
to_reference
def
link_to_project
(
project
,
link_content:
nil
)
url
=
urls
.
project_url
(
project
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
project:
project
.
id
)
content
=
link_content
||
project
.
to_reference
link_tag
(
url
,
data
,
content
,
project
.
name
)
end
link_tag
(
url
,
data
,
content
,
project
.
name
)
end
def
link_tag
(
url
,
data
,
link_content
,
title
)
%(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
def
link_tag
(
url
,
data
,
link_content
,
title
)
%(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
end
end
end
end
...
...
lib/banzai/filter/references/reference_filter.rb
View file @
7ecc247a
...
...
@@ -3,212 +3,214 @@
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/reference.js
module
Banzai
module
Filter
# Base class for GitLab Flavored Markdown reference filters.
#
# References within <pre>, <code>, <a>, and <style> elements are ignored.
#
# Context options:
# :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links.
class
ReferenceFilter
<
HTML
::
Pipeline
::
Filter
include
RequestStoreReferenceCache
include
OutputSafety
class
<<
self
attr_accessor
:reference_type
def
call
(
doc
,
context
=
nil
,
result
=
nil
)
new
(
doc
,
context
,
result
).
call_and_update_nodes
end
end
def
initialize
(
doc
,
context
=
nil
,
result
=
nil
)
super
@new_nodes
=
{}
@nodes
=
self
.
result
[
:reference_filter_nodes
]
end
def
call_and_update_nodes
with_update_nodes
{
call
}
end
# Returns a data attribute String to attach to a reference link
module
References
# Base class for GitLab Flavored Markdown reference filters.
#
# attributes - Hash, where the key becomes the data attribute name and the
# value is the data attribute value
# References within <pre>, <code>, <a>, and <style> elements are ignored.
#
# Examples:
#
# data_attribute(project: 1, issue: 2)
# # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
#
# data_attribute(project: 3, merge_request: 4)
# # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
#
# Returns a String
def
data_attribute
(
attributes
=
{})
attributes
=
attributes
.
reject
{
|
_
,
v
|
v
.
nil?
}
attributes
[
:reference_type
]
||=
self
.
class
.
reference_type
attributes
[
:container
]
||=
'body'
attributes
[
:placement
]
||=
'top'
attributes
.
delete
(
:original
)
if
context
[
:no_original_data
]
attributes
.
map
do
|
key
,
value
|
%Q(data-
#{
key
.
to_s
.
dasherize
}
="
#{
escape_once
(
value
)
}
")
end
.
join
(
' '
)
end
# Context options:
# :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links.
class
ReferenceFilter
<
HTML
::
Pipeline
::
Filter
include
RequestStoreReferenceCache
include
OutputSafety
class
<<
self
attr_accessor
:reference_type
def
call
(
doc
,
context
=
nil
,
result
=
nil
)
new
(
doc
,
context
,
result
).
call_and_update_nodes
end
end
def
ignore_ancestor_query
@ignore_ancestor_query
||=
begin
parents
=
%w(pre code a style)
parents
<<
'blockquote'
if
context
[
:ignore_blockquotes
]
def
initialize
(
doc
,
context
=
nil
,
result
=
nil
)
super
parents
.
map
{
|
n
|
"ancestor::
#{
n
}
"
}.
join
(
' or '
)
@new_nodes
=
{}
@nodes
=
self
.
result
[
:reference_filter_nodes
]
end
end
def
project
context
[
:project
]
end
def
call_and_update_nodes
with_update_nodes
{
call
}
end
def
group
context
[
:group
]
end
# Returns a data attribute String to attach to a reference link
#
# attributes - Hash, where the key becomes the data attribute name and the
# value is the data attribute value
#
# Examples:
#
# data_attribute(project: 1, issue: 2)
# # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
#
# data_attribute(project: 3, merge_request: 4)
# # => "data-reference-type=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
#
# Returns a String
def
data_attribute
(
attributes
=
{})
attributes
=
attributes
.
reject
{
|
_
,
v
|
v
.
nil?
}
attributes
[
:reference_type
]
||=
self
.
class
.
reference_type
attributes
[
:container
]
||=
'body'
attributes
[
:placement
]
||=
'top'
attributes
.
delete
(
:original
)
if
context
[
:no_original_data
]
attributes
.
map
do
|
key
,
value
|
%Q(data-
#{
key
.
to_s
.
dasherize
}
="
#{
escape_once
(
value
)
}
")
end
.
join
(
' '
)
end
def
user
context
[
:user
]
end
def
ignore_ancestor_query
@ignore_ancestor_query
||=
begin
parents
=
%w(pre code a style)
parents
<<
'blockquote'
if
context
[
:ignore_blockquotes
]
def
skip_project_check?
context
[
:skip_project_check
]
end
parents
.
map
{
|
n
|
"ancestor::
#{
n
}
"
}.
join
(
' or '
)
end
end
def
reference_class
(
type
,
tooltip:
true
)
gfm_klass
=
"gfm gfm-
#{
type
}
"
def
project
context
[
:project
]
end
return
gfm_klass
unless
tooltip
def
group
context
[
:group
]
end
"
#{
gfm_klass
}
has-tooltip"
end
def
user
context
[
:user
]
end
# Ensure that a :project key exists in context
#
# Note that while the key might exist, its value could be nil!
def
validate
needs
:project
unless
skip_project_check?
end
def
skip_project_check?
context
[
:skip_project_check
]
end
# Iterates over all <a> and text() nodes in a document.
#
# Nodes are skipped whenever their ancestor is one of the nodes returned
# by `ignore_ancestor_query`. Link tags are not processed if they have a
# "gfm" class or the "href" attribute is empty.
def
each_node
return
to_enum
(
__method__
)
unless
block_given?
def
reference_class
(
type
,
tooltip:
true
)
gfm_klass
=
"gfm gfm-
#{
type
}
"
doc
.
xpath
(
query
).
each
do
|
node
|
yield
node
end
end
return
gfm_klass
unless
tooltip
# Returns an Array containing all HTML nodes.
def
nodes
@nodes
||=
each_node
.
to_a
end
"
#{
gfm_klass
}
has-tooltip"
end
# Yields the link's URL and inner HTML whenever the node is a valid <a> tag.
def
yield_valid_link
(
node
)
link
=
unescape_link
(
node
.
attr
(
'href'
).
to_s
)
inner_html
=
node
.
inner_html
# Ensure that a :project key exists in context
#
# Note that while the key might exist, its value could be nil!
def
validate
needs
:project
unless
skip_project_check?
end
return
unless
link
.
force_encoding
(
'UTF-8'
).
valid_encoding?
# Iterates over all <a> and text() nodes in a document.
#
# Nodes are skipped whenever their ancestor is one of the nodes returned
# by `ignore_ancestor_query`. Link tags are not processed if they have a
# "gfm" class or the "href" attribute is empty.
def
each_node
return
to_enum
(
__method__
)
unless
block_given?
doc
.
xpath
(
query
).
each
do
|
node
|
yield
node
end
end
yield
link
,
inner_html
end
# Returns an Array containing all HTML nodes.
def
nodes
@nodes
||=
each_node
.
to_a
end
def
unescape_link
(
href
)
CGI
.
unescape
(
href
)
end
# Yields the link's URL and inner HTML whenever the node is a valid <a> tag.
def
yield_valid_link
(
node
)
link
=
unescape_link
(
node
.
attr
(
'href'
).
to_s
)
inner_html
=
node
.
inner_html
def
replace_text_when_pattern_matches
(
node
,
index
,
pattern
)
return
unless
node
.
text
=~
pattern
return
unless
link
.
force_encoding
(
'UTF-8'
).
valid_encoding?
content
=
node
.
to
_html
html
=
yield
content
yield
link
,
inner
_html
end
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
content
end
def
unescape_link
(
href
)
CGI
.
unescape
(
href
)
end
def
replace_link_node_with_text
(
node
,
index
)
html
=
yield
def
replace_text_when_pattern_matches
(
node
,
index
,
pattern
)
return
unless
node
.
text
=~
pattern
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
node
.
text
end
content
=
node
.
to_html
html
=
yield
content
def
replace_link_node_with_href
(
node
,
index
,
link
)
html
=
yiel
d
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
content
en
d
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
link
en
d
def
replace_link_node_with_text
(
node
,
index
)
html
=
yiel
d
def
text_node?
(
node
)
node
.
is_a?
(
Nokogiri
::
XML
::
Text
)
end
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
node
.
text
end
def
element_node?
(
node
)
node
.
is_a?
(
Nokogiri
::
XML
::
Element
)
end
def
replace_link_node_with_href
(
node
,
index
,
link
)
html
=
yield
private
replace_text_with_html
(
node
,
index
,
html
)
unless
html
==
link
end
def
query
@query
||=
%Q{descendant-or-self::text()[not(
#{
ignore_ancestor_query
}
)]
| descendant-or-self::a[
not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
]}
end
def
text_node?
(
node
)
node
.
is_a?
(
Nokogiri
::
XML
::
Text
)
end
def
replace_text_with_html
(
node
,
index
,
html
)
replace_and_update_new_nodes
(
node
,
index
,
html
)
end
def
element_node?
(
node
)
node
.
is_a?
(
Nokogiri
::
XML
::
Element
)
end
def
replace_and_update_new_nodes
(
node
,
index
,
html
)
previous_node
=
node
.
previous
next_node
=
node
.
next
parent_node
=
node
.
parent
# Unfortunately node.replace(html) returns re-parented nodes, not the actual replaced nodes in the doc
# We need to find the actual nodes in the doc that were replaced
node
.
replace
(
html
)
@new_nodes
[
index
]
=
[]
private
# We replaced node with new nodes, so we find first new node. If previous_node is nil, we take first parent child
new_node
=
previous_node
?
previous_node
.
next
:
parent_node
&
.
children
&
.
first
def
query
@query
||=
%Q{descendant-or-self::text()[not(
#{
ignore_ancestor_query
}
)]
| descendant-or-self::a[
not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
]}
end
# We iterate from first to last replaced node and store replaced nodes in @new_nodes
while
new_node
&&
new_node
!=
next_node
@new_nodes
[
index
]
<<
new_node
.
xpath
(
query
)
new_node
=
new_node
.
next
def
replace_text_with_html
(
node
,
index
,
html
)
replace_and_update_new_nodes
(
node
,
index
,
html
)
end
@new_nodes
[
index
].
flatten!
end
def
replace_and_update_new_nodes
(
node
,
index
,
html
)
previous_node
=
node
.
previous
next_node
=
node
.
next
parent_node
=
node
.
parent
# Unfortunately node.replace(html) returns re-parented nodes, not the actual replaced nodes in the doc
# We need to find the actual nodes in the doc that were replaced
node
.
replace
(
html
)
@new_nodes
[
index
]
=
[]
# We replaced node with new nodes, so we find first new node. If previous_node is nil, we take first parent child
new_node
=
previous_node
?
previous_node
.
next
:
parent_node
&
.
children
&
.
first
# We iterate from first to last replaced node and store replaced nodes in @new_nodes
while
new_node
&&
new_node
!=
next_node
@new_nodes
[
index
]
<<
new_node
.
xpath
(
query
)
new_node
=
new_node
.
next
end
@new_nodes
[
index
].
flatten!
end
def
only_path?
context
[
:only_path
]
end
def
only_path?
context
[
:only_path
]
end
def
with_update_nodes
@new_nodes
=
{}
yield
.
tap
{
update_nodes!
}
end
def
with_update_nodes
@new_nodes
=
{}
yield
.
tap
{
update_nodes!
}
end
# Once Filter completes replacing nodes, we update nodes with @new_nodes
def
update_nodes!
@new_nodes
.
sort_by
{
|
index
,
_new_nodes
|
-
index
}.
each
do
|
index
,
new_nodes
|
nodes
[
index
,
1
]
=
new_nodes
# Once Filter completes replacing nodes, we update nodes with @new_nodes
def
update_nodes!
@new_nodes
.
sort_by
{
|
index
,
_new_nodes
|
-
index
}.
each
do
|
index
,
new_nodes
|
nodes
[
index
,
1
]
=
new_nodes
end
result
[
:reference_filter_nodes
]
=
nodes
end
result
[
:reference_filter_nodes
]
=
nodes
end
end
end
...
...
lib/banzai/filter/references/snippet_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,27 +2,29 @@
module
Banzai
module
Filter
# HTML filter that replaces snippet references with links. References to
# snippets that do not exist are ignored.
#
# This filter supports cross-project references.
class
SnippetReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:snippet
module
References
# HTML filter that replaces snippet references with links. References to
# snippets that do not exist are ignored.
#
# This filter supports cross-project references.
class
SnippetReferenceFilter
<
AbstractReferenceFilter
self
.
reference_type
=
:snippet
def
self
.
object_class
Snippet
end
def
self
.
object_class
Snippet
end
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
def
find_object
(
project
,
id
)
return
unless
project
.
is_a?
(
Project
)
project
.
snippets
.
find_by
(
id:
id
)
end
project
.
snippets
.
find_by
(
id:
id
)
end
def
url_for_object
(
snippet
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_snippet_url
(
project
,
snippet
,
only_path:
context
[
:only_path
])
def
url_for_object
(
snippet
,
project
)
h
=
Gitlab
::
Routing
.
url_helpers
h
.
project_snippet_url
(
project
,
snippet
,
only_path:
context
[
:only_path
])
end
end
end
end
...
...
lib/banzai/filter/references/user_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,178 +2,180 @@
module
Banzai
module
Filter
# HTML filter that replaces user or group references with links.
#
# A special `@all` reference is also supported.
class
UserReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:user
# Public: Find `@user` user references in text
#
# UserReferenceFilter.references_in(text) do |match, username|
# "<a href=...>@#{user}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, and the String user name.
module
References
# HTML filter that replaces user or group references with links.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
User
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
:user
]
# A special `@all` reference is also supported.
class
UserReferenceFilter
<
ReferenceFilter
self
.
reference_type
=
:user
# Public: Find `@user` user references in text
#
# UserReferenceFilter.references_in(text) do |match, username|
# "<a href=...>@#{user}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, and the String user name.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
User
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
:user
]
end
end
end
def
call
return
doc
if
project
.
nil?
&&
group
.
nil?
&&
!
skip_project_check?
def
call
return
doc
if
project
.
nil?
&&
group
.
nil?
&&
!
skip_project_check?
ref_pattern
=
User
.
reference_pattern
ref_pattern_start
=
/\A
#{
ref_pattern
}
\z/
ref_pattern
=
User
.
reference_pattern
ref_pattern_start
=
/\A
#{
ref_pattern
}
\z/
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
user_link_filter
(
content
)
end
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_pattern_start
replace_link_node_with_href
(
node
,
index
,
link
)
do
user_link_filter
(
link
,
link_content:
inner_html
)
nodes
.
each_with_index
do
|
node
,
index
|
if
text_node?
(
node
)
replace_text_when_pattern_matches
(
node
,
index
,
ref_pattern
)
do
|
content
|
user_link_filter
(
content
)
end
elsif
element_node?
(
node
)
yield_valid_link
(
node
)
do
|
link
,
inner_html
|
if
link
=~
ref_pattern_start
replace_link_node_with_href
(
node
,
index
,
link
)
do
user_link_filter
(
link
,
link_content:
inner_html
)
end
end
end
end
end
end
doc
end
doc
end
# Replace `@user` user references in text with links to the referenced
# user's profile page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `@user` references replaced with links. All links
# have `gfm` and `gfm-project_member` class names attached for styling.
def
user_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
)
do
|
match
,
username
|
if
username
==
'all'
&&
!
skip_project_check?
link_to_all
(
link_content:
link_content
)
else
cached_call
(
:banzai_url_for_object
,
match
,
path:
[
User
,
username
.
downcase
])
do
if
namespace
=
namespaces
[
username
.
downcase
]
link_to_namespace
(
namespace
,
link_content:
link_content
)
||
match
else
match
# Replace `@user` user references in text with links to the referenced
# user's profile page.
#
# text - String text to replace references in.
# link_content - Original content of the link being replaced.
#
# Returns a String with `@user` references replaced with links. All links
# have `gfm` and `gfm-project_member` class names attached for styling.
def
user_link_filter
(
text
,
link_content:
nil
)
self
.
class
.
references_in
(
text
)
do
|
match
,
username
|
if
username
==
'all'
&&
!
skip_project_check?
link_to_all
(
link_content:
link_content
)
else
cached_call
(
:banzai_url_for_object
,
match
,
path:
[
User
,
username
.
downcase
])
do
if
namespace
=
namespaces
[
username
.
downcase
]
link_to_namespace
(
namespace
,
link_content:
link_content
)
||
match
else
match
end
end
end
end
end
end
# Returns a Hash containing all Namespace objects for the username
# references in the current document.
#
# The keys of this Hash are the namespace paths, the values the
# corresponding Namespace objects.
def
namespaces
@namespaces
||=
Namespace
.
eager_load
(
:owner
,
:route
)
.
where_full_path_in
(
usernames
)
.
index_by
(
&
:full_path
)
.
transform_keys
(
&
:downcase
)
end
# Returns a Hash containing all Namespace objects for the username
# references in the current document.
#
# The keys of this Hash are the namespace paths, the values the
# corresponding Namespace objects.
def
namespaces
@namespaces
||=
Namespace
.
eager_load
(
:owner
,
:route
)
.
where_full_path_in
(
usernames
)
.
index_by
(
&
:full_path
)
.
transform_keys
(
&
:downcase
)
end
# Returns all usernames referenced in the current document.
def
usernames
refs
=
Set
.
new
# Returns all usernames referenced in the current document.
def
usernames
refs
=
Set
.
new
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
User
.
reference_pattern
)
do
refs
<<
$~
[
:user
]
nodes
.
each
do
|
node
|
node
.
to_html
.
scan
(
User
.
reference_pattern
)
do
refs
<<
$~
[
:user
]
end
end
end
refs
.
to_a
end
refs
.
to_a
end
private
private
def
urls
Gitlab
::
Routing
.
url_helpers
end
def
urls
Gitlab
::
Routing
.
url_helpers
end
def
link_class
[
reference_class
(
:project_member
,
tooltip:
false
),
"js-user-link"
].
join
(
" "
)
end
def
link_class
[
reference_class
(
:project_member
,
tooltip:
false
),
"js-user-link"
].
join
(
" "
)
end
def
link_to_all
(
link_content:
nil
)
author
=
context
[
:author
]
def
link_to_all
(
link_content:
nil
)
author
=
context
[
:author
]
if
author
&&
!
team_member?
(
author
)
link_content
else
parent_url
(
link_content
,
author
)
if
author
&&
!
team_member?
(
author
)
link_content
else
parent_url
(
link_content
,
author
)
end
end
end
def
link_to_namespace
(
namespace
,
link_content:
nil
)
if
namespace
.
is_a?
(
Group
)
link_to_group
(
namespace
.
full_path
,
namespace
,
link_content:
link_content
)
else
link_to_user
(
namespace
.
path
,
namespace
,
link_content:
link_content
)
def
link_to_namespace
(
namespace
,
link_content:
nil
)
if
namespace
.
is_a?
(
Group
)
link_to_group
(
namespace
.
full_path
,
namespace
,
link_content:
link_content
)
else
link_to_user
(
namespace
.
path
,
namespace
,
link_content:
link_content
)
end
end
end
def
link_to_group
(
group
,
namespace
,
link_content:
nil
)
url
=
urls
.
group_url
(
group
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
group:
namespace
.
id
)
content
=
link_content
||
Group
.
reference_prefix
+
group
link_tag
(
url
,
data
,
content
,
namespace
.
full_name
)
end
def
link_to_group
(
group
,
namespace
,
link_content:
nil
)
url
=
urls
.
group_url
(
group
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
group:
namespace
.
id
)
content
=
link_content
||
Group
.
reference_prefix
+
group
def
link_to_user
(
user
,
namespace
,
link_content:
nil
)
url
=
urls
.
user_url
(
user
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
user:
namespace
.
owner_id
)
content
=
link_content
||
User
.
reference_prefix
+
user
link_tag
(
url
,
data
,
content
,
namespace
.
full_name
)
end
link_tag
(
url
,
data
,
content
,
namespace
.
owner_name
)
end
def
link_to_user
(
user
,
namespace
,
link_content:
nil
)
url
=
urls
.
user_url
(
user
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
user:
namespace
.
owner_id
)
content
=
link_content
||
User
.
reference_prefix
+
user
def
link_tag
(
url
,
data
,
link_content
,
title
)
%(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
end
link_tag
(
url
,
data
,
content
,
namespace
.
owner_name
)
end
def
parent
context
[
:project
]
||
context
[
:group
]
end
def
link_tag
(
url
,
data
,
link_content
,
title
)
%(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{link_content}</a>)
end
def
parent_group?
parent
.
is_a?
(
Group
)
end
def
parent
context
[
:project
]
||
context
[
:group
]
end
def
team_member?
(
user
)
if
parent_group?
parent
.
member?
(
user
)
else
parent
.
team
.
member?
(
user
)
def
parent_group?
parent
.
is_a?
(
Group
)
end
end
def
parent_url
(
link_content
,
author
)
if
parent_group?
url
=
urls
.
group_url
(
parent
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
group:
group
.
id
,
author:
author
.
try
(
:id
))
else
url
=
urls
.
project_url
(
parent
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
project:
project
.
id
,
author:
author
.
try
(
:id
))
def
team_member?
(
user
)
if
parent_group?
parent
.
member?
(
user
)
else
parent
.
team
.
member?
(
user
)
end
end
content
=
link_content
||
User
.
reference_prefix
+
'all'
link_tag
(
url
,
data
,
content
,
'All Project and Group Members'
)
def
parent_url
(
link_content
,
author
)
if
parent_group?
url
=
urls
.
group_url
(
parent
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
group:
group
.
id
,
author:
author
.
try
(
:id
))
else
url
=
urls
.
project_url
(
parent
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
project:
project
.
id
,
author:
author
.
try
(
:id
))
end
content
=
link_content
||
User
.
reference_prefix
+
'all'
link_tag
(
url
,
data
,
content
,
'All Project and Group Members'
)
end
end
end
end
...
...
lib/banzai/filter/references/vulnerability_reference_filter.rb
View file @
7ecc247a
...
...
@@ -2,21 +2,23 @@
module
Banzai
module
Filter
# The actual filter is implemented in the EE mixin
class
VulnerabilityReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:vulnerability
module
References
# The actual filter is implemented in the EE mixin
class
VulnerabilityReferenceFilter
<
IssuableReferenceFilter
self
.
reference_type
=
:vulnerability
def
self
.
object_class
Vulnerability
end
def
self
.
object_class
Vulnerability
end
private
private
def
project
context
[
:project
]
def
project
context
[
:project
]
end
end
end
end
end
Banzai
::
Filter
::
VulnerabilityReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter
::VulnerabilityReferenceFilter'
)
Banzai
::
Filter
::
References
::
VulnerabilityReferenceFilter
.
prepend_if_ee
(
'EE::Banzai::Filter::References
::VulnerabilityReferenceFilter'
)
lib/banzai/pipeline/gfm_pipeline.rb
View file @
7ecc247a
...
...
@@ -51,19 +51,19 @@ module Banzai
def
self
.
reference_filters
[
Filter
::
UserReferenceFilter
,
Filter
::
ProjectReferenceFilter
,
Filter
::
DesignReferenceFilter
,
Filter
::
IssueReferenceFilter
,
Filter
::
ExternalIssueReferenceFilter
,
Filter
::
MergeRequestReferenceFilter
,
Filter
::
SnippetReferenceFilter
,
Filter
::
CommitRangeReferenceFilter
,
Filter
::
CommitReferenceFilter
,
Filter
::
LabelReferenceFilter
,
Filter
::
MilestoneReferenceFilter
,
Filter
::
AlertReferenceFilter
,
Filter
::
FeatureFlagReferenceFilter
Filter
::
References
::
UserReferenceFilter
,
Filter
::
References
::
ProjectReferenceFilter
,
Filter
::
References
::
DesignReferenceFilter
,
Filter
::
References
::
IssueReferenceFilter
,
Filter
::
References
::
ExternalIssueReferenceFilter
,
Filter
::
References
::
MergeRequestReferenceFilter
,
Filter
::
References
::
SnippetReferenceFilter
,
Filter
::
References
::
CommitRangeReferenceFilter
,
Filter
::
References
::
CommitReferenceFilter
,
Filter
::
References
::
LabelReferenceFilter
,
Filter
::
References
::
MilestoneReferenceFilter
,
Filter
::
References
::
AlertReferenceFilter
,
Filter
::
References
::
FeatureFlagReferenceFilter
]
end
...
...
lib/banzai/pipeline/label_pipeline.rb
View file @
7ecc247a
...
...
@@ -6,7 +6,7 @@ module Banzai
def
self
.
filters
@filters
||=
FilterArray
[
Filter
::
SanitizationFilter
,
Filter
::
LabelReferenceFilter
Filter
::
References
::
LabelReferenceFilter
]
end
end
...
...
lib/banzai/pipeline/single_line_pipeline.rb
View file @
7ecc247a
...
...
@@ -17,15 +17,15 @@ module Banzai
def
self
.
reference_filters
[
Filter
::
UserReferenceFilter
,
Filter
::
IssueReferenceFilter
,
Filter
::
ExternalIssueReferenceFilter
,
Filter
::
MergeRequestReferenceFilter
,
Filter
::
SnippetReferenceFilter
,
Filter
::
CommitRangeReferenceFilter
,
Filter
::
CommitReferenceFilter
,
Filter
::
AlertReferenceFilter
,
Filter
::
FeatureFlagReferenceFilter
Filter
::
References
::
UserReferenceFilter
,
Filter
::
References
::
IssueReferenceFilter
,
Filter
::
References
::
ExternalIssueReferenceFilter
,
Filter
::
References
::
MergeRequestReferenceFilter
,
Filter
::
References
::
SnippetReferenceFilter
,
Filter
::
References
::
CommitRangeReferenceFilter
,
Filter
::
References
::
CommitReferenceFilter
,
Filter
::
References
::
AlertReferenceFilter
,
Filter
::
References
::
FeatureFlagReferenceFilter
]
end
...
...
spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
AbstractReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
AbstractReferenceFilter
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let
(
:doc
)
{
Nokogiri
::
HTML
.
fragment
(
''
)
}
...
...
spec/lib/banzai/filter/references/alert_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
AlertReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
AlertReferenceFilter
do
include
FilterSpecHelper
let_it_be
(
:project
)
{
create
(
:project
,
:public
)
}
...
...
spec/lib/banzai/filter/references/commit_range_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
CommitRangeReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
CommitRangeReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
...
...
spec/lib/banzai/filter/references/commit_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
CommitReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
CommitReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
...
...
spec/lib/banzai/filter/references/design_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
DesignReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
DesignReferenceFilter
do
include
FilterSpecHelper
include
DesignManagementTestHelpers
...
...
spec/lib/banzai/filter/references/external_issue_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
ExternalIssueReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
ExternalIssueReferenceFilter
do
include
FilterSpecHelper
let_it_be_with_refind
(
:project
)
{
create
(
:project
)
}
...
...
spec/lib/banzai/filter/references/feature_flag_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
FeatureFlagReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
FeatureFlagReferenceFilter
do
include
FilterSpecHelper
let_it_be
(
:project
)
{
create
(
:project
,
:public
)
}
...
...
spec/lib/banzai/filter/references/issue_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
IssueReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
IssueReferenceFilter
do
include
FilterSpecHelper
include
DesignManagementTestHelpers
...
...
spec/lib/banzai/filter/references/label_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -3,7 +3,7 @@
require
'spec_helper'
require
'html/pipeline'
RSpec
.
describe
Banzai
::
Filter
::
LabelReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
LabelReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
,
name:
'sample-project'
)
}
...
...
spec/lib/banzai/filter/references/merge_request_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
MergeRequestReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
MergeRequestReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
)
}
...
...
spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
MilestoneReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
MilestoneReferenceFilter
do
include
FilterSpecHelper
let_it_be
(
:parent_group
)
{
create
(
:group
,
:public
)
}
...
...
spec/lib/banzai/filter/references/project_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
ProjectReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
ProjectReferenceFilter
do
include
FilterSpecHelper
def
invalidate_reference
(
reference
)
...
...
spec/lib/banzai/filter/references/reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
ReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
Reference
s
::
Reference
Filter
do
let
(
:project
)
{
build_stubbed
(
:project
)
}
describe
'#each_node'
do
...
...
spec/lib/banzai/filter/references/snippet_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
SnippetReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
SnippetReferenceFilter
do
include
FilterSpecHelper
let
(
:project
)
{
create
(
:project
,
:public
)
}
...
...
spec/lib/banzai/filter/references/user_reference_filter_spec.rb
View file @
7ecc247a
...
...
@@ -2,7 +2,7 @@
require
'spec_helper'
RSpec
.
describe
Banzai
::
Filter
::
UserReferenceFilter
do
RSpec
.
describe
Banzai
::
Filter
::
References
::
UserReferenceFilter
do
include
FilterSpecHelper
def
get_reference
(
user
)
...
...
spec/lib/banzai/pipeline/gfm_pipeline_spec.rb
View file @
7ecc247a
...
...
@@ -25,7 +25,7 @@ RSpec.describe Banzai::Pipeline::GfmPipeline do
issue
=
create
(
:issue
,
project:
project
)
markdown
=
"text
#{
issue
.
to_reference
(
project
,
full:
true
)
}
"
expect_any_instance_of
(
Banzai
::
Filter
::
ReferenceFilter
).
to
receive
(
:each_node
).
once
expect_any_instance_of
(
Banzai
::
Filter
::
Reference
s
::
Reference
Filter
).
to
receive
(
:each_node
).
once
described_class
.
call
(
markdown
,
project:
project
)
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment