diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/banzai/filter/audio_link_filter.rb | 66 | ||||
-rw-r--r-- | lib/banzai/filter/relative_link_filter.rb | 4 | ||||
-rw-r--r-- | lib/banzai/filter/wiki_link_filter.rb | 2 | ||||
-rw-r--r-- | lib/banzai/pipeline/gfm_pipeline.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/database/migration_helpers.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/file_markdown_link_builder.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/file_type_detection.rb | 18 | ||||
-rw-r--r-- | lib/gitlab/profiler.rb | 3 |
8 files changed, 88 insertions, 12 deletions
diff --git a/lib/banzai/filter/audio_link_filter.rb b/lib/banzai/filter/audio_link_filter.rb new file mode 100644 index 00000000000..83aa520dc4b --- /dev/null +++ b/lib/banzai/filter/audio_link_filter.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/audio.js +module Banzai + module Filter + # Find every image that isn't already wrapped in an `a` tag, and that has + # a `src` attribute ending with an audio extension, add a new audio node and + # a "Download" link in the case the audio cannot be played. + class AudioLinkFilter < HTML::Pipeline::Filter + def call + doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |el| + el.replace(audio_node(doc, el)) if has_audio_extension?(el) + end + + doc + end + + private + + def has_audio_extension?(element) + src = element.attr('data-canonical-src').presence || element.attr('src') + + return unless src.present? + + src_ext = File.extname(src).sub('.', '').downcase + Gitlab::FileTypeDetection::SAFE_AUDIO_EXT.include?(src_ext) + end + + def audio_node(doc, element) + container = doc.document.create_element( + 'div', + class: 'audio-container' + ) + + audio = doc.document.create_element( + 'audio', + src: element['src'], + controls: true, + 'data-setup' => '{}', + 'data-title' => element['title'] || element['alt']) + + link = doc.document.create_element( + 'a', + element['title'] || element['alt'], + href: element['src'], + target: '_blank', + rel: 'noopener noreferrer', + title: "Download '#{element['title'] || element['alt']}'") + + # make sure the original non-proxied src carries over + if element['data-canonical-src'] + audio['data-canonical-src'] = element['data-canonical-src'] + link['data-canonical-src'] = element['data-canonical-src'] + end + + download_paragraph = doc.document.create_element('p') + download_paragraph.children = link + + container.add_child(audio) + container.add_child(download_paragraph) + + container + end + end + end +end diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index df181406591..c7589e69262 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -65,7 +65,7 @@ module Banzai el.attribute('href') end - attrs += doc.search('img, video').flat_map do |el| + attrs += doc.search('img, video, audio').flat_map do |el| [el.attribute('src'), el.attribute('data-src')] end @@ -83,7 +83,7 @@ module Banzai get_blob_types(paths).each do |name, type| if type == :blob blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: name), project) - uri_types[name] = blob.image? || blob.video? ? :raw : :blob + uri_types[name] = blob.image? || blob.video? || blob.audio? ? :raw : :blob else uri_types[name] = type end diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb index 18947679b69..205f777bc90 100644 --- a/lib/banzai/filter/wiki_link_filter.rb +++ b/lib/banzai/filter/wiki_link_filter.rb @@ -15,7 +15,7 @@ module Banzai doc.search('a:not(.gfm)').each { |el| process_link(el.attribute('href'), el) } - doc.search('video').each { |el| process_link(el.attribute('src'), el) } + doc.search('video, audio').each { |el| process_link(el.attribute('src'), el) } doc.search('img').each do |el| attr = el.attribute('data-src') || el.attribute('src') diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index bb0d1eaa1e1..08e27257fdf 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -26,6 +26,7 @@ module Banzai Filter::ColorFilter, Filter::MermaidFilter, Filter::VideoLinkFilter, + Filter::AudioLinkFilter, Filter::ImageLazyLoadFilter, Filter::ImageLinkFilter, Filter::InlineMetricsFilter, diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index 5a42952796c..ae29546cdac 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -1018,7 +1018,7 @@ into similar problems in the future (e.g. when new tables are created). end model_class.each_batch(of: batch_size) do |relation, index| - start_id, end_id = relation.pluck('MIN(id), MAX(id)').first + start_id, end_id = relation.pluck(Arel.sql('MIN(id), MAX(id)')).first # `BackgroundMigrationWorker.bulk_perform_in` schedules all jobs for # the same time, which is not helpful in most cases where we wish to diff --git a/lib/gitlab/file_markdown_link_builder.rb b/lib/gitlab/file_markdown_link_builder.rb index e9e5172e6f8..09d799b859d 100644 --- a/lib/gitlab/file_markdown_link_builder.rb +++ b/lib/gitlab/file_markdown_link_builder.rb @@ -10,14 +10,14 @@ module Gitlab return unless name = markdown_name markdown = "[#{name.gsub(']', '\\]')}](#{secure_url})" - markdown = "!#{markdown}" if image_or_video? || dangerous_image_or_video? + markdown = "!#{markdown}" if embeddable? || dangerous_embeddable? markdown end def markdown_name return unless filename.present? - image_or_video? ? File.basename(filename, File.extname(filename)) : filename + embeddable? ? File.basename(filename, File.extname(filename)) : filename end end end diff --git a/lib/gitlab/file_type_detection.rb b/lib/gitlab/file_type_detection.rb index 7137720f204..ca78d49f99b 100644 --- a/lib/gitlab/file_type_detection.rb +++ b/lib/gitlab/file_type_detection.rb @@ -26,11 +26,13 @@ module Gitlab # on IE >= 9. # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html SAFE_VIDEO_EXT = %w[mp4 m4v mov webm ogv].freeze + SAFE_AUDIO_EXT = %w[mp3 oga ogg spx wav].freeze # These extension types can contain dangerous code and should only be embedded inline with # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline". DANGEROUS_IMAGE_EXT = %w[svg].freeze DANGEROUS_VIDEO_EXT = [].freeze # None, yet + DANGEROUS_AUDIO_EXT = [].freeze # None, yet def image? extension_match?(SAFE_IMAGE_EXT) @@ -40,8 +42,12 @@ module Gitlab extension_match?(SAFE_VIDEO_EXT) end - def image_or_video? - image? || video? + def audio? + extension_match?(SAFE_AUDIO_EXT) + end + + def embeddable? + image? || video? || audio? end def dangerous_image? @@ -52,8 +58,12 @@ module Gitlab extension_match?(DANGEROUS_VIDEO_EXT) end - def dangerous_image_or_video? - dangerous_image? || dangerous_video? + def dangerous_audio? + extension_match?(DANGEROUS_AUDIO_EXT) + end + + def dangerous_embeddable? + dangerous_image? || dangerous_video? || dangerous_audio? end private diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index 275151f7fc1..560618bb486 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -37,8 +37,7 @@ module Gitlab # - post_data: a string of raw POST data to use. Changes the HTTP verb to # POST. # - # - user: a user to authenticate as. Only works if the user has a valid - # personal access token. + # - user: a user to authenticate as. # # - private_token: instead of providing a user instance, the token can be # given as a string. Takes precedence over the user option. |