summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorZeger-Jan van de Weg <zegerjan@gitlab.com>2016-04-05 14:35:01 +0200
committerZeger-Jan van de Weg <zegerjan@gitlab.com>2016-04-05 14:35:01 +0200
commit06e473e2d9cc3ba156bf9960e610c0f4729e0f88 (patch)
tree3d5a5540fd24f72934afb136c01c9b5388d68f72 /lib
parent9e5a11f4b679f4a11aab31ca82c347ba6b025b68 (diff)
parent97747233a99376fdf42869e9433ce9a638b2909d (diff)
downloadgitlab-ce-06e473e2d9cc3ba156bf9960e610c0f4729e0f88.tar.gz
Merge branch 'master' into assign-to-issuable-opener
Diffstat (limited to 'lib')
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/labels.rb24
-rw-r--r--lib/api/projects.rb28
-rw-r--r--lib/award_emoji.rb19
-rw-r--r--lib/banzai/filter.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb140
-rw-r--r--lib/banzai/filter/autolink_filter.rb1
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb2
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb2
-rw-r--r--lib/banzai/filter/emoji_filter.rb4
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb41
-rw-r--r--lib/banzai/filter/external_link_filter.rb2
-rw-r--r--lib/banzai/filter/gollum_tags_filter.rb3
-rw-r--r--lib/banzai/filter/image_link_filter.rb27
-rw-r--r--lib/banzai/filter/label_reference_filter.rb2
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb4
-rw-r--r--lib/banzai/filter/redactor_filter.rb2
-rw-r--r--lib/banzai/filter/reference_filter.rb151
-rw-r--r--lib/banzai/filter/reference_gatherer_filter.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb1
-rw-r--r--lib/banzai/filter/sanitization_filter.rb3
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb1
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb2
-rw-r--r--lib/banzai/filter/upload_link_filter.rb1
-rw-r--r--lib/banzai/filter/user_reference_filter.rb27
-rw-r--r--lib/banzai/filter/yaml_front_matter_filter.rb3
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb2
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb1
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb2
-rw-r--r--lib/gitlab/badge/build.rb24
-rw-r--r--lib/gitlab/current_settings.rb1
-rw-r--r--lib/gitlab/email/message/repository_push.rb2
-rw-r--r--lib/gitlab/email/receiver.rb15
-rw-r--r--lib/gitlab/fogbugz_import/client.rb2
-rw-r--r--lib/gitlab/gfm/reference_rewriter.rb9
-rw-r--r--lib/gitlab/gfm/uploads_rewriter.rb51
-rw-r--r--lib/gitlab/incoming_email.rb16
-rw-r--r--lib/gitlab/note_data_builder.rb2
-rw-r--r--lib/gitlab/routing.rb13
-rw-r--r--lib/gitlab/url_builder.rb16
-rw-r--r--lib/tasks/gemojione.rake48
-rw-r--r--lib/tasks/gitlab/check.rake15
45 files changed, 473 insertions, 253 deletions
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 197e826e5bc..340fc5452ab 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -8,7 +8,7 @@ module API
expose :id, :state, :avatar_url
expose :web_url do |user, options|
- Gitlab::Application.routes.url_helpers.user_url(user)
+ Gitlab::Routing.url_helpers.user_url(user)
end
end
@@ -89,7 +89,7 @@ module API
expose :avatar_url
expose :web_url do |group, options|
- Gitlab::Application.routes.url_helpers.group_url(group)
+ Gitlab::Routing.url_helpers.group_url(group)
end
end
@@ -292,7 +292,7 @@ module API
end
class Label < Grape::Entity
- expose :name, :color
+ expose :name, :color, :description
end
class Compare < Grape::Entity
@@ -334,7 +334,6 @@ module API
expose :updated_at
expose :home_page_url
expose :default_branch_protection
- expose :twitter_sharing_enabled
expose :restricted_visibility_levels
expose :max_attachment_size
expose :session_expire_delay
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 78ca58ad0d1..4af6bef0fa7 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -17,17 +17,18 @@ module API
# Creates a new label
#
# Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- # color (required) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be created
+ # color (required) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # description (optional) - The description of label to be created
# Example Request:
# POST /projects/:id/labels
post ':id/labels' do
authorize! :admin_label, user_project
required_attributes! [:name, :color]
- attrs = attributes_for_keys [:name, :color]
+ attrs = attributes_for_keys [:name, :color, :description]
label = user_project.find_label(attrs[:name])
conflict!('Label already exists') if label
@@ -62,11 +63,12 @@ module API
# Updates an existing label. At least one optional parameter is required.
#
# Parameters:
- # id (required) - The ID of a project
- # name (required) - The name of the label to be deleted
- # new_name (optional) - The new name of the label
- # color (optional) - Color of the label given in 6-digit hex
- # notation with leading '#' sign (e.g. #FFAABB)
+ # id (required) - The ID of a project
+ # name (required) - The name of the label to be deleted
+ # new_name (optional) - The new name of the label
+ # color (optional) - Color of the label given in 6-digit hex
+ # notation with leading '#' sign (e.g. #FFAABB)
+ # description (optional) - The description of label to be created
# Example Request:
# PUT /projects/:id/labels
put ':id/labels' do
@@ -76,7 +78,7 @@ module API
label = user_project.find_label(params[:name])
not_found!('Label not found') unless label
- attrs = attributes_for_keys [:new_name, :color]
+ attrs = attributes_for_keys [:new_name, :color, :description]
if attrs.empty?
render_api_error!('Required parameters "new_name" or "color" ' \
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6fcb5261e40..24b31005475 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -244,6 +244,34 @@ module API
end
end
+ # Archive project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # PUT /projects/:id/archive
+ post ':id/archive' do
+ authorize!(:archive_project, user_project)
+
+ user_project.archive!
+
+ present user_project, with: Entities::Project
+ end
+
+ # Unarchive project
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # Example Request:
+ # PUT /projects/:id/unarchive
+ post ':id/unarchive' do
+ authorize!(:archive_project, user_project)
+
+ user_project.unarchive!
+
+ present user_project, with: Entities::Project
+ end
+
# Remove project
#
# Parameters:
diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb
index 783fcfb61ad..4fc3443ac68 100644
--- a/lib/award_emoji.rb
+++ b/lib/award_emoji.rb
@@ -48,4 +48,23 @@ class AwardEmoji
JSON.parse(File.read(json_path))
end
end
+
+ # Returns an Array of Emoji names and their asset URLs.
+ def self.urls
+ @urls ||= begin
+ path = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+ prefix = Gitlab::Application.config.assets.prefix
+ digest = Gitlab::Application.config.assets.digest
+
+ JSON.parse(File.read(path)).map do |hash|
+ if digest
+ fname = "#{hash['unicode']}-#{hash['digest']}"
+ else
+ fname = hash['unicode']
+ end
+
+ { name: hash['name'], path: "#{prefix}/#{fname}.png" }
+ end
+ end
+ end
end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index 905c4c0144e..3eb544dfef9 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/string/output_safety'
-
module Banzai
module Filter
def self.[](name)
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 34c38913474..f21dbef216c 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -11,15 +11,19 @@ module Banzai
end
def self.object_name
- object_class.name.underscore
+ @object_name ||= object_class.name.underscore
end
def self.object_sym
- object_name.to_sym
+ @object_sym ||= object_name.to_sym
end
def self.data_reference
- "data-#{object_name.dasherize}"
+ @data_reference ||= "data-#{object_name.dasherize}"
+ end
+
+ def self.object_class_title
+ @object_title ||= object_class.name.titleize
end
# Public: Find references in text (like `!123` for merge requests)
@@ -53,6 +57,10 @@ module Banzai
self.class.object_sym
end
+ def object_class_title
+ self.class.object_class_title
+ end
+
def references_in(*args, &block)
self.class.references_in(*args, &block)
end
@@ -62,36 +70,81 @@ module Banzai
# Example: project.merge_requests.find
end
+ def find_object_cached(project, id)
+ if RequestStore.active?
+ cache = find_objects_cache[object_class][project.id]
+
+ get_or_set_cache(cache, id) { find_object(project, id) }
+ else
+ find_object(project, id)
+ end
+ end
+
+ def project_from_ref_cache(ref)
+ if RequestStore.active?
+ cache = project_refs_cache
+
+ get_or_set_cache(cache, ref) { project_from_ref(ref) }
+ else
+ project_from_ref(ref)
+ end
+ end
+
def url_for_object(object, project)
# Implement in child class
# Example: project_merge_request_url
end
- def call
- if object_class.reference_pattern
- # `#123`
- replace_text_nodes_matching(object_class.reference_pattern) do |content|
- object_link_filter(content, object_class.reference_pattern)
- end
+ def url_for_object_cached(object, project)
+ if RequestStore.active?
+ cache = url_for_object_cache[object_class][project.id]
- # `[Issue](#123)`, which is turned into
- # `<a href="#123">Issue</a>`
- replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
- object_link_filter(link, object_class.reference_pattern, link_text: text)
- end
+ get_or_set_cache(cache, object) { url_for_object(object, project) }
+ else
+ url_for_object(object, project)
end
+ end
- if object_class.link_reference_pattern
- # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
- # `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
- replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
- object_link_filter(text, object_class.link_reference_pattern)
- end
+ def call
+ return doc if project.nil?
+
+ ref_pattern = object_class.reference_pattern
+ link_pattern = object_class.link_reference_pattern
- # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
- # `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
- replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
- object_link_filter(link, object_class.link_reference_pattern, link_text: text)
+ each_node do |node|
+ if text_node?(node) && ref_pattern
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ object_link_filter(content, ref_pattern)
+ end
+
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if ref_pattern && link =~ /\A#{ref_pattern}/
+ replace_link_node_with_href(node, link) do
+ object_link_filter(link, ref_pattern, link_text: text)
+ end
+
+ next
+ end
+
+ next unless link_pattern
+
+ if link == text && text =~ /\A#{link_pattern}/
+ replace_link_node_with_text(node, link) do
+ object_link_filter(text, link_pattern)
+ end
+
+ next
+ end
+
+ if link =~ /\A#{link_pattern}\z/
+ replace_link_node_with_href(node, link) do
+ object_link_filter(link, link_pattern, link_text: text)
+ end
+
+ next
+ end
+ end
end
end
@@ -109,9 +162,9 @@ module Banzai
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text, pattern, link_text: nil)
references_in(text, pattern) do |match, id, project_ref, matches|
- project = project_from_ref(project_ref)
+ project = project_from_ref_cache(project_ref)
- if project && object = find_object(project, id)
+ if project && object = find_object_cached(project, id)
title = object_link_title(object)
klass = reference_class(object_sym)
@@ -121,8 +174,11 @@ module Banzai
object_sym => object.id
)
- url = matches[:url] if matches.names.include?("url")
- url ||= url_for_object(object, project)
+ if matches.names.include?("url") && matches[:url]
+ url = matches[:url]
+ else
+ url = url_for_object_cached(object, project)
+ end
text = link_text || object_link_text(object, matches)
@@ -146,7 +202,7 @@ module Banzai
end
def object_link_title(object)
- "#{object_class.name.titleize}: #{object.title}"
+ "#{object_class_title}: #{object.title}"
end
def object_link_text(object, matches)
@@ -157,6 +213,32 @@ module Banzai
text
end
+
+ private
+
+ def project_refs_cache
+ RequestStore[:banzai_project_refs] ||= {}
+ end
+
+ def find_objects_cache
+ RequestStore[:banzai_find_objects_cache] ||= Hash.new do |hash, key|
+ hash[key] = Hash.new { |h, k| h[k] = {} }
+ end
+ end
+
+ def url_for_object_cache
+ RequestStore[:banzai_url_for_object] ||= Hash.new do |hash, key|
+ hash[key] = Hash.new { |h, k| h[k] = {} }
+ end
+ end
+
+ def get_or_set_cache(cache, key)
+ if cache.key?(key)
+ cache[key]
+ else
+ cache[key] = yield
+ end
+ end
end
end
end
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index 856f56fb175..fac7dad3243 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index 470727ee312..b469ea0f626 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -43,7 +43,7 @@ module Banzai
end
def url_for_object(range, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_compare_url(project.namespace, project,
range.to_param.merge(only_path: context[:only_path]))
end
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 713a56ba949..bd88207326c 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -37,7 +37,7 @@ module Banzai
end
def url_for_object(commit, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_commit_url(project.namespace, project, commit,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 207437ba7cf..d25de900674 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,7 +1,3 @@
-require 'action_controller'
-require 'gitlab_emoji'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that replaces :emoji: with images.
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index edc26386903..37344b90576 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -35,15 +35,29 @@ module Banzai
def call
# Early return if the project isn't using an external tracker
- return doc if project.nil? || project.default_issues_tracker?
+ return doc if project.nil? || default_issues_tracker?
- replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
- issue_link_filter(content)
- end
+ ref_pattern = ExternalIssue.reference_pattern
+ ref_start_pattern = /\A#{ref_pattern}\z/
+
+ each_node do |node|
+ if text_node?(node)
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ issue_link_filter(content)
+ end
- replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text|
- issue_link_filter(link, link_text: text)
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if link =~ ref_start_pattern
+ replace_link_node_with_href(node, link) do
+ issue_link_filter(link, link_text: text)
+ end
+ end
+ end
+ end
end
+
+ doc
end
# Replace `JIRA-123` issue references in text with links to the referenced
@@ -76,6 +90,21 @@ module Banzai
def url_for_issue(*args)
IssuesHelper.url_for_issue(*args)
end
+
+ def default_issues_tracker?
+ if RequestStore.active?
+ default_issues_tracker_cache[project.id] ||=
+ project.default_issues_tracker?
+ else
+ project.default_issues_tracker?
+ end
+ end
+
+ private
+
+ def default_issues_tracker_cache
+ RequestStore[:banzai_default_issues_tracker_cache] ||= {}
+ end
end
end
end
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index 8d368f3b9e7..d179bea181e 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML Filter to add a `rel="nofollow"` attribute to external links
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
index f31f921903b..7ce26db1b90 100644
--- a/lib/banzai/filter/gollum_tags_filter.rb
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -1,6 +1,3 @@
-require 'banzai'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML Filter for parsing Gollum's tags in HTML. It's only parses the
diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb
new file mode 100644
index 00000000000..ccd106860bd
--- /dev/null
+++ b/lib/banzai/filter/image_link_filter.rb
@@ -0,0 +1,27 @@
+module Banzai
+ module Filter
+ # HTML filter that wraps links around inline images.
+ class ImageLinkFilter < HTML::Pipeline::Filter
+
+ # Find every image that isn't already wrapped in an `a` tag, create
+ # a new node (a link to the image source), copy the image as a child
+ # of the anchor, and then replace the img with the link-wrapped version.
+ def call
+ doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |img|
+
+ link = doc.document.create_element(
+ 'a',
+ class: 'no-attachment-icon',
+ href: img['src'],
+ target: '_blank'
+ )
+
+ link.children = img.clone
+ img.replace(link)
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 8147e5ed3c7..a2987850d03 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -31,7 +31,7 @@ module Banzai
end
def url_for_object(label, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_issues_url(project.namespace, project, label_name: label.name,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index 0659fed1419..9b209533a89 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
class MarkdownFilter < HTML::Pipeline::TextFilter
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 57c71708992..cad38a51851 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -14,7 +14,7 @@ module Banzai
end
def url_for_object(mr, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_merge_request_url(project.namespace, project, mr,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index e88b27c1fae..4cb82178024 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
module Banzai
module Filter
# HTML filter that replaces milestone references with links.
@@ -13,7 +11,7 @@ module Banzai
end
def url_for_object(issue, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_milestone_url(project.namespace, project, milestone,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index 7141ed7c9bd..e589b5df6ec 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that removes references to records that the current user does
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 132f0a4bd93..31386cf851c 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/string/output_safety'
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# Base class for GitLab Flavored Markdown reference filters.
@@ -55,18 +52,13 @@ module Banzai
html.html_safe? ? html : ERB::Util.html_escape_once(html)
end
- def ignore_parents
- @ignore_parents ||= begin
- # Don't look for references in text nodes that are children of these
- # elements.
+ def ignore_ancestor_query
+ @ignore_ancestor_query ||= begin
parents = %w(pre code a style)
parents << 'blockquote' if context[:ignore_blockquotes]
- parents.to_set
- end
- end
- def ignored_ancestry?(node)
- has_ancestor?(node, ignore_parents)
+ parents.map { |n| "ancestor::#{n}" }.join(' or ')
+ end
end
def project
@@ -77,119 +69,66 @@ module Banzai
"gfm gfm-#{type}"
end
- # Iterate through the document's text nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's content matches `pattern` AND
- # * The node is not an ancestor of an ignored node type
- #
- # pattern - Regex pattern against which to match the node's content
- #
- # Yields the current node's String contents. The result of the block will
- # replace the node's existing content and update the current document.
+ # Ensure that a :project key exists in context
#
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_text_nodes_matching(pattern)
- return doc if project.nil?
-
- search_text_nodes(doc).each do |node|
- next if ignored_ancestry?(node)
- next unless node.text =~ pattern
-
- content = node.to_html
-
- html = yield content
-
- next if html == content
-
- node.replace(html)
- end
-
- doc
+ # Note that while the key might exist, its value could be nil!
+ def validate
+ needs :project
end
- # Iterate through the document's link nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's content matches `pattern`
- #
- # pattern - Regex pattern against which to match the node's content
- #
- # Yields the current node's String contents. The result of the block will
- # replace the node and update the current document.
+ # Iterates over all <a> and text() nodes in a document.
#
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_link_nodes_with_text(pattern)
- return doc if project.nil?
+ # 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
+ query = %Q{descendant-or-self::text()[not(#{ignore_ancestor_query})]
+ | descendant-or-self::a[
+ not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
+ ]}
- doc.xpath('descendant-or-self::a').each do |node|
- klass = node.attr('class')
- next if klass && klass.include?('gfm')
-
- link = node.attr('href')
- text = node.text
-
- next unless link && text
-
- link = CGI.unescape(link)
- next unless link.force_encoding('UTF-8').valid_encoding?
- # Ignore ending punctionation like periods or commas
- next unless link == text && text =~ /\A#{pattern}/
-
- html = yield text
+ doc.xpath(query).each do |node|
+ yield node
+ end
+ end
- next if html == text
+ # Yields the link's URL and text whenever the node is a valid <a> tag.
+ def yield_valid_link(node)
+ link = CGI.unescape(node.attr('href').to_s)
+ text = node.text
- node.replace(html)
- end
+ return unless link.force_encoding('UTF-8').valid_encoding?
- doc
+ yield link, text
end
- # Iterate through the document's link nodes, yielding the current node's
- # content if:
- #
- # * The `project` context value is present AND
- # * The node's HREF matches `pattern`
- #
- # pattern - Regex pattern against which to match the node's HREF
- #
- # Yields the current node's String HREF and String content.
- # The result of the block will replace the node and update the current document.
- #
- # Returns the updated Nokogiri::HTML::DocumentFragment object.
- def replace_link_nodes_with_href(pattern)
- return doc if project.nil?
+ def replace_text_when_pattern_matches(node, pattern)
+ return unless node.text =~ pattern
- doc.xpath('descendant-or-self::a').each do |node|
- klass = node.attr('class')
- next if klass && klass.include?('gfm')
+ content = node.to_html
+ html = yield content
- link = node.attr('href')
- text = node.text
+ node.replace(html) unless content == html
+ end
- next unless link && text
- link = CGI.unescape(link)
- next unless link.force_encoding('UTF-8').valid_encoding?
- next unless link && link =~ /\A#{pattern}\z/
+ def replace_link_node_with_text(node, link)
+ html = yield
- html = yield link, text
+ node.replace(html) unless html == node.text
+ end
- next if html == link
+ def replace_link_node_with_href(node, link)
+ html = yield
- node.replace(html)
- end
+ node.replace(html) unless html == link
+ end
- doc
+ def text_node?(node)
+ node.is_a?(Nokogiri::XML::Text)
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
+ def element_node?(node)
+ node.is_a?(Nokogiri::XML::Element)
end
end
end
diff --git a/lib/banzai/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
index 86d484feb90..96fdb06304e 100644
--- a/lib/banzai/filter/reference_gatherer_filter.rb
+++ b/lib/banzai/filter/reference_gatherer_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that gathers all referenced records that the current user has
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 41380627d39..ea21c7b041c 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index e8011519608..42dbab9d27e 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,6 +1,3 @@
-require 'html/pipeline/filter'
-require 'html/pipeline/sanitization_filter'
-
module Banzai
module Filter
# Sanitize HTML
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index c870a42f741..d507eb5ebe1 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -14,7 +14,7 @@ module Banzai
end
def url_for_object(snippet, project)
- h = Gitlab::Application.routes.url_helpers
+ h = Gitlab::Routing.url_helpers
h.namespace_project_snippet_url(project.namespace, project, snippet,
only_path: context[:only_path])
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 8c5855e5ffc..62a79c62e20 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'rouge/plugins/redcarpet'
module Banzai
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index 4056dcd6d64..a4eda6fdf76 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline/filter'
-
module Banzai
module Filter
# HTML filter that adds an anchor child element to all Headers in a
diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index f642aee0967..7edfe5ade2d 100644
--- a/lib/banzai/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -1,4 +1,3 @@
-require 'html/pipeline/filter'
require 'uri'
module Banzai
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 24f16f8b547..eea3af842b6 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -59,13 +59,28 @@ module Banzai
end
def call
- replace_text_nodes_matching(User.reference_pattern) do |content|
- user_link_filter(content)
+ return doc if project.nil?
+
+ ref_pattern = User.reference_pattern
+ ref_pattern_start = /\A#{ref_pattern}\z/
+
+ each_node do |node|
+ if text_node?(node)
+ replace_text_when_pattern_matches(node, ref_pattern) do |content|
+ user_link_filter(content)
+ end
+ elsif element_node?(node)
+ yield_valid_link(node) do |link, text|
+ if link =~ ref_pattern_start
+ replace_link_node_with_href(node, link) do
+ user_link_filter(link, link_text: text)
+ end
+ end
+ end
+ end
end
- replace_link_nodes_with_href(User.reference_pattern) do |link, text|
- user_link_filter(link, link_text: text)
- end
+ doc
end
# Replace `@user` user references in text with links to the referenced
@@ -90,7 +105,7 @@ module Banzai
private
def urls
- Gitlab::Application.routes.url_helpers
+ Gitlab::Routing.url_helpers
end
def link_class
diff --git a/lib/banzai/filter/yaml_front_matter_filter.rb b/lib/banzai/filter/yaml_front_matter_filter.rb
index e4e2f3f228d..58e3e81209e 100644
--- a/lib/banzai/filter/yaml_front_matter_filter.rb
+++ b/lib/banzai/filter/yaml_front_matter_filter.rb
@@ -1,6 +1,3 @@
-require 'html/pipeline/filter'
-require 'yaml'
-
module Banzai
module Filter
class YamlFrontMatterFilter < HTML::Pipeline::Filter
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index f60966c3c0f..321fd5bbe14 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -1,5 +1,3 @@
-require 'html/pipeline'
-
module Banzai
module Pipeline
class BasePipeline
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 8cd4b50e65a..ed3cfd6b023 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -7,6 +7,7 @@ module Banzai
Filter::SanitizationFilter,
Filter::UploadLinkFilter,
+ Filter::ImageLinkFilter,
Filter::EmojiFilter,
Filter::TableOfContentsFilter,
Filter::AutolinkFilter,
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index 9b4ff0f0f80..0b5a9e0b2b8 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
module Banzai
module Pipeline
class WikiPipeline < FullPipeline
diff --git a/lib/gitlab/badge/build.rb b/lib/gitlab/badge/build.rb
new file mode 100644
index 00000000000..28a2391dbf8
--- /dev/null
+++ b/lib/gitlab/badge/build.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ module Badge
+ ##
+ # Build badge
+ #
+ class Build
+ def initialize(project, ref)
+ @image = ::Ci::ImageForBuildService.new.execute(project, ref: ref)
+ end
+
+ def to_s
+ @image[:name].sub(/\.svg$/, '')
+ end
+
+ def type
+ 'image/svg+xml'
+ end
+
+ def data
+ File.read(@image[:path])
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 761b63e98f6..1acc22fe5bf 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -21,7 +21,6 @@ module Gitlab
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
- twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index 41f0edcaf7e..8f9be6cd9a3 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -5,7 +5,7 @@ module Gitlab
attr_accessor :recipient
attr_reader :author_id, :ref, :action
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
delegate :namespace, :name_with_namespace, to: :project, prefix: :project
delegate :name, to: :author, prefix: :author
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index d4b6f6d120d..97ef9851d71 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -63,6 +63,10 @@ module Gitlab
end
def reply_key
+ key_from_to_header || key_from_additional_headers
+ end
+
+ def key_from_to_header
key = nil
message.to.each do |address|
key = Gitlab::IncomingEmail.key_from_address(address)
@@ -72,6 +76,17 @@ module Gitlab
key
end
+ def key_from_additional_headers
+ reply_key = nil
+
+ Array(message.references).each do |message_id|
+ reply_key = Gitlab::IncomingEmail.key_from_fallback_reply_message_id(message_id)
+ break if reply_key
+ end
+
+ reply_key
+ end
+
def sent_notification
return nil unless reply_key
diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb
index 431d50882fd..2152182b37f 100644
--- a/lib/gitlab/fogbugz_import/client.rb
+++ b/lib/gitlab/fogbugz_import/client.rb
@@ -26,7 +26,7 @@ module Gitlab
def user_map
users = {}
res = @api.command(:listPeople)
- res['people']['person'].each do |user|
+ [res['people']['person']].flatten.each do |user|
users[user['ixPerson']] = { name: user['sFullName'], email: user['sEmail'] }
end
users
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
index a1c6ee7bd69..78d7a4f27cf 100644
--- a/lib/gitlab/gfm/reference_rewriter.rb
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -34,16 +34,21 @@ module Gitlab
@source_project = source_project
@current_user = current_user
@original_html = markdown(text)
+ @pattern = Gitlab::ReferenceExtractor.references_pattern
end
def rewrite(target_project)
- pattern = Gitlab::ReferenceExtractor.references_pattern
+ return @text unless needs_rewrite?
- @text.gsub(pattern) do |reference|
+ @text.gsub(@pattern) do |reference|
unfold_reference(reference, Regexp.last_match, target_project)
end
end
+ def needs_rewrite?
+ @text =~ @pattern
+ end
+
private
def unfold_reference(reference, match, target_project)
diff --git a/lib/gitlab/gfm/uploads_rewriter.rb b/lib/gitlab/gfm/uploads_rewriter.rb
new file mode 100644
index 00000000000..abc8c8c55e6
--- /dev/null
+++ b/lib/gitlab/gfm/uploads_rewriter.rb
@@ -0,0 +1,51 @@
+module Gitlab
+ module Gfm
+ ##
+ # Class that rewrites markdown links for uploads
+ #
+ # Using a pattern defined in `FileUploader` it copies files to a new
+ # project and rewrites all links to uploads in in a given text.
+ #
+ #
+ class UploadsRewriter
+ def initialize(text, source_project, _current_user)
+ @text = text
+ @source_project = source_project
+ @pattern = FileUploader::MARKDOWN_PATTERN
+ end
+
+ def rewrite(target_project)
+ return @text unless needs_rewrite?
+
+ @text.gsub(@pattern) do |markdown|
+ file = find_file(@source_project, $~[:secret], $~[:file])
+ return markdown unless file.try(:exists?)
+
+ new_uploader = FileUploader.new(target_project)
+ new_uploader.store!(file)
+ new_uploader.to_markdown
+ end
+ end
+
+ def needs_rewrite?
+ files.any?
+ end
+
+ def files
+ referenced_files = @text.scan(@pattern).map do
+ find_file(@source_project, $~[:secret], $~[:file])
+ end
+
+ referenced_files.compact.select(&:exists?)
+ end
+
+ private
+
+ def find_file(project, secret, file)
+ uploader = FileUploader.new(project, secret)
+ uploader.retrieve_from_store!(file)
+ uploader.file
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index 9068d79c95e..8ce9d32abe0 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -1,13 +1,10 @@
module Gitlab
module IncomingEmail
class << self
- def enabled?
- config.enabled && address_formatted_correctly?
- end
+ FALLBACK_REPLY_MESSAGE_ID_REGEX = /\Areply\-(.+)@#{Gitlab.config.gitlab.host}\Z/.freeze
- def address_formatted_correctly?
- config.address &&
- config.address.include?("%{key}")
+ def enabled?
+ config.enabled && config.address
end
def reply_address(key)
@@ -24,6 +21,13 @@ module Gitlab
match[1]
end
+ def key_from_fallback_reply_message_id(message_id)
+ match = message_id.match(FALLBACK_REPLY_MESSAGE_ID_REGEX)
+ return unless match
+
+ match[1]
+ end
+
def config
Gitlab.config.incoming_email
end
diff --git a/lib/gitlab/note_data_builder.rb b/lib/gitlab/note_data_builder.rb
index 71cf6a0d886..18523e0aefe 100644
--- a/lib/gitlab/note_data_builder.rb
+++ b/lib/gitlab/note_data_builder.rb
@@ -41,7 +41,7 @@ module Gitlab
data[:issue] = note.noteable.hook_attrs
elsif note.for_merge_request?
data[:merge_request] = note.noteable.hook_attrs
- elsif note.for_project_snippet?
+ elsif note.for_snippet?
data[:snippet] = note.noteable.hook_attrs
end
diff --git a/lib/gitlab/routing.rb b/lib/gitlab/routing.rb
new file mode 100644
index 00000000000..5132177de51
--- /dev/null
+++ b/lib/gitlab/routing.rb
@@ -0,0 +1,13 @@
+module Gitlab
+ module Routing
+ # Returns the URL helpers Module.
+ #
+ # This method caches the output as Rails' "url_helpers" method creates an
+ # anonymous module every time it's called.
+ #
+ # Returns a Module.
+ def self.url_helpers
+ @url_helpers ||= Gitlab::Application.routes.url_helpers
+ end
+ end
+end
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index 6f0d02cafd1..f301d42939d 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -1,7 +1,8 @@
module Gitlab
class UrlBuilder
- include Gitlab::Application.routes.url_helpers
+ include Gitlab::Routing.url_helpers
include GitlabRoutingHelper
+ include ActionView::RecordIdentifier
def initialize(type)
@type = type
@@ -37,19 +38,16 @@ module Gitlab
namespace_project_commit_url(namespace_id: note.project.namespace,
id: note.commit_id,
project_id: note.project,
- anchor: "note_#{note.id}")
+ anchor: dom_id(note))
elsif note.for_issue?
issue = Issue.find(note.noteable_id)
- issue_url(issue,
- anchor: "note_#{note.id}")
+ issue_url(issue, anchor: dom_id(note))
elsif note.for_merge_request?
merge_request = MergeRequest.find(note.noteable_id)
- merge_request_url(merge_request,
- anchor: "note_#{note.id}")
- elsif note.for_project_snippet?
+ merge_request_url(merge_request, anchor: dom_id(note))
+ elsif note.for_snippet?
snippet = Snippet.find(note.noteable_id)
- project_snippet_url(snippet,
- anchor: "note_#{note.id}")
+ project_snippet_url(snippet, anchor: dom_id(note))
end
end
end
diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake
index cfaf4a129b1..7ec00a898fd 100644
--- a/lib/tasks/gemojione.rake
+++ b/lib/tasks/gemojione.rake
@@ -1,19 +1,39 @@
-# This task will generate a standard and Retina sprite of all of the current
-# Gemojione Emojis, with the accompanying SCSS map.
-#
-# It will not appear in `rake -T` output, and the dependent gems are not
-# included in the Gemfile by default, because this task will only be needed
-# occasionally, such as when new Emojis are added to Gemojione.
-
-begin
- require 'sprite_factory'
- require 'rmagick'
-rescue LoadError
- # noop
-end
-
namespace :gemojione do
+ desc 'Generates Emoji SHA256 digests'
+ task digests: :environment do
+ require 'digest/sha2'
+ require 'json'
+
+ dir = Gemojione.index.images_path
+
+ digests = AwardEmoji.emojis.map do |name, emoji_hash|
+ fpath = File.join(dir, "#{emoji_hash['unicode']}.png")
+ digest = Digest::SHA256.file(fpath).hexdigest
+
+ { name: name, unicode: emoji_hash['unicode'], digest: digest }
+ end
+
+ out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+
+ File.open(out, 'w') do |handle|
+ handle.write(JSON.pretty_generate(digests))
+ end
+ end
+
+ # This task will generate a standard and Retina sprite of all of the current
+ # Gemojione Emojis, with the accompanying SCSS map.
+ #
+ # It will not appear in `rake -T` output, and the dependent gems are not
+ # included in the Gemfile by default, because this task will only be needed
+ # occasionally, such as when new Emojis are added to Gemojione.
task sprite: :environment do
+ begin
+ require 'sprite_factory'
+ require 'rmagick'
+ rescue LoadError
+ # noop
+ end
+
check_requirements!
SIZE = 20
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 27ed57efe55..effb8eb6001 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -623,7 +623,6 @@ namespace :gitlab do
start_checking "Reply by email"
if Gitlab.config.incoming_email.enabled
- check_address_formatted_correctly
check_imap_authentication
if Rails.env.production?
@@ -643,20 +642,6 @@ namespace :gitlab do
# Checks
########################
- def check_address_formatted_correctly
- print "Address formatted correctly? ... "
-
- if Gitlab::IncomingEmail.address_formatted_correctly?
- puts "yes".green
- else
- puts "no".red
- try_fixing_it(
- "Make sure that the address in config/gitlab.yml includes the '%{key}' placeholder."
- )
- fix_and_rerun
- end
- end
-
def check_initd_configured_correctly
print "Init.d configured correctly? ... "