Add rich blob viewers
This commit is contained in:
parent
f112a03cba
commit
5cb27e9fa7
21 changed files with 139 additions and 18 deletions
|
@ -134,12 +134,15 @@ module BlobHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blob_raw_url
|
||||||
|
namespace_project_raw_path(@project.namespace, @project, @id)
|
||||||
|
end
|
||||||
|
|
||||||
# SVGs can contain malicious JavaScript; only include whitelisted
|
# SVGs can contain malicious JavaScript; only include whitelisted
|
||||||
# elements and attributes. Note that this whitelist is by no means complete
|
# elements and attributes. Note that this whitelist is by no means complete
|
||||||
# and may omit some elements.
|
# and may omit some elements.
|
||||||
def sanitize_svg(blob)
|
def sanitize_svg_data(data)
|
||||||
blob.data = Gitlab::Sanitizers::SVG.clean(blob.data)
|
Gitlab::Sanitizers::SVG.clean(data)
|
||||||
blob
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we blindly set the 'real' content type when serving a Git blob we
|
# If we blindly set the 'real' content type when serving a Git blob we
|
||||||
|
|
|
@ -6,6 +6,14 @@ class Blob < SimpleDelegator
|
||||||
MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte
|
MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte
|
||||||
|
|
||||||
RICH_VIEWERS = [
|
RICH_VIEWERS = [
|
||||||
|
BlobViewer::Image,
|
||||||
|
BlobViewer::PDF,
|
||||||
|
BlobViewer::Sketch,
|
||||||
|
BlobViewer::BinarySTL,
|
||||||
|
BlobViewer::TextSTL,
|
||||||
|
BlobViewer::Notebook,
|
||||||
|
BlobViewer::SVG,
|
||||||
|
BlobViewer::Markup,
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
attr_reader :project
|
attr_reader :project
|
||||||
|
|
10
app/models/blob_viewer/binary_stl.rb
Normal file
10
app/models/blob_viewer/binary_stl.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module BlobViewer
|
||||||
|
class BinarySTL < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'stl'
|
||||||
|
self.extensions = %w(stl)
|
||||||
|
self.text_based = false
|
||||||
|
end
|
||||||
|
end
|
12
app/models/blob_viewer/image.rb
Normal file
12
app/models/blob_viewer/image.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module BlobViewer
|
||||||
|
class Image < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'image'
|
||||||
|
self.extensions = UploaderHelper::IMAGE_EXT
|
||||||
|
self.text_based = false
|
||||||
|
self.switcher_icon = 'picture-o'
|
||||||
|
self.switcher_title = 'image'
|
||||||
|
end
|
||||||
|
end
|
10
app/models/blob_viewer/markup.rb
Normal file
10
app/models/blob_viewer/markup.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module BlobViewer
|
||||||
|
class Markup < Base
|
||||||
|
include Rich
|
||||||
|
include ServerSide
|
||||||
|
|
||||||
|
self.partial_name = 'markup'
|
||||||
|
self.extensions = Gitlab::MarkupHelper::EXTENSIONS
|
||||||
|
self.text_based = true
|
||||||
|
end
|
||||||
|
end
|
12
app/models/blob_viewer/notebook.rb
Normal file
12
app/models/blob_viewer/notebook.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module BlobViewer
|
||||||
|
class Notebook < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'notebook'
|
||||||
|
self.extensions = %w(ipynb)
|
||||||
|
self.text_based = true
|
||||||
|
self.switcher_icon = 'file-text-o'
|
||||||
|
self.switcher_title = 'notebook'
|
||||||
|
end
|
||||||
|
end
|
12
app/models/blob_viewer/pdf.rb
Normal file
12
app/models/blob_viewer/pdf.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module BlobViewer
|
||||||
|
class PDF < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'pdf'
|
||||||
|
self.extensions = %w(pdf)
|
||||||
|
self.text_based = false
|
||||||
|
self.switcher_icon = 'file-pdf-o'
|
||||||
|
self.switcher_title = 'PDF'
|
||||||
|
end
|
||||||
|
end
|
11
app/models/blob_viewer/rich.rb
Normal file
11
app/models/blob_viewer/rich.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module BlobViewer
|
||||||
|
module Rich
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
self.type = :rich
|
||||||
|
self.switcher_icon = 'file-text-o'
|
||||||
|
self.switcher_title = 'rendered file'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
app/models/blob_viewer/server_side.rb
Normal file
11
app/models/blob_viewer/server_side.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module BlobViewer
|
||||||
|
module ServerSide
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
self.client_side = false
|
||||||
|
self.max_size = 2.megabytes
|
||||||
|
self.absolute_max_size = 5.megabytes
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
app/models/blob_viewer/sketch.rb
Normal file
12
app/models/blob_viewer/sketch.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module BlobViewer
|
||||||
|
class Sketch < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'sketch'
|
||||||
|
self.extensions = %w(sketch)
|
||||||
|
self.text_based = false
|
||||||
|
self.switcher_icon = 'file-image-o'
|
||||||
|
self.switcher_title = 'preview'
|
||||||
|
end
|
||||||
|
end
|
12
app/models/blob_viewer/svg.rb
Normal file
12
app/models/blob_viewer/svg.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module BlobViewer
|
||||||
|
class SVG < Base
|
||||||
|
include Rich
|
||||||
|
include ServerSide
|
||||||
|
|
||||||
|
self.partial_name = 'svg'
|
||||||
|
self.extensions = %w(svg)
|
||||||
|
self.text_based = true
|
||||||
|
self.switcher_icon = 'picture-o'
|
||||||
|
self.switcher_title = 'image'
|
||||||
|
end
|
||||||
|
end
|
10
app/models/blob_viewer/text_stl.rb
Normal file
10
app/models/blob_viewer/text_stl.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module BlobViewer
|
||||||
|
class TextSTL < Base
|
||||||
|
include Rich
|
||||||
|
include ClientSide
|
||||||
|
|
||||||
|
self.partial_name = 'stl'
|
||||||
|
self.extensions = %w(stl)
|
||||||
|
self.text_based = true
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,2 +0,0 @@
|
||||||
.file-content.image_file
|
|
||||||
%img{ src: namespace_project_raw_path(@project.namespace, @project, @id), alt: blob.name }
|
|
|
@ -1,9 +0,0 @@
|
||||||
- if blob.size_within_svg_limits?
|
|
||||||
-# We need to scrub SVG but we cannot do so in the RawController: it would
|
|
||||||
-# be wrong/strange if RawController modified the data.
|
|
||||||
- blob.load_all_data!(@repository)
|
|
||||||
- blob = sanitize_svg(blob)
|
|
||||||
.file-content.image_file
|
|
||||||
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: blob.name }
|
|
||||||
- else
|
|
||||||
= render 'too_large'
|
|
2
app/views/projects/blob/viewers/_image.html.haml
Normal file
2
app/views/projects/blob/viewers/_image.html.haml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.file-content.image_file
|
||||||
|
%img{ src: blob_raw_url, alt: viewer.blob.name }
|
3
app/views/projects/blob/viewers/_markup.html.haml
Normal file
3
app/views/projects/blob/viewers/_markup.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
- blob = viewer.blob
|
||||||
|
.file-content.wiki
|
||||||
|
= render_markup(blob.name, blob.data)
|
|
@ -2,4 +2,4 @@
|
||||||
= page_specific_javascript_bundle_tag('common_vue')
|
= page_specific_javascript_bundle_tag('common_vue')
|
||||||
= page_specific_javascript_bundle_tag('notebook_viewer')
|
= page_specific_javascript_bundle_tag('notebook_viewer')
|
||||||
|
|
||||||
.file-content#js-notebook-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
|
.file-content#js-notebook-viewer{ data: { endpoint: blob_raw_url } }
|
|
@ -2,4 +2,4 @@
|
||||||
= page_specific_javascript_bundle_tag('common_vue')
|
= page_specific_javascript_bundle_tag('common_vue')
|
||||||
= page_specific_javascript_bundle_tag('pdf_viewer')
|
= page_specific_javascript_bundle_tag('pdf_viewer')
|
||||||
|
|
||||||
.file-content#js-pdf-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
|
.file-content#js-pdf-viewer{ data: { endpoint: blob_raw_url } }
|
|
@ -2,6 +2,6 @@
|
||||||
= page_specific_javascript_bundle_tag('common_vue')
|
= page_specific_javascript_bundle_tag('common_vue')
|
||||||
= page_specific_javascript_bundle_tag('sketch_viewer')
|
= page_specific_javascript_bundle_tag('sketch_viewer')
|
||||||
|
|
||||||
.file-content#js-sketch-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
|
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_url } }
|
||||||
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
|
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
|
||||||
= icon('spinner spin 2x', 'aria-hidden' => 'true');
|
= icon('spinner spin 2x', 'aria-hidden' => 'true');
|
|
@ -2,7 +2,7 @@
|
||||||
= page_specific_javascript_bundle_tag('stl_viewer')
|
= page_specific_javascript_bundle_tag('stl_viewer')
|
||||||
|
|
||||||
.file-content.is-stl-loading
|
.file-content.is-stl-loading
|
||||||
.text-center#js-stl-viewer{ data: { endpoint: namespace_project_raw_path(@project.namespace, @project, @id) } }
|
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_url } }
|
||||||
= icon('spinner spin 2x', class: 'prepend-top-default append-bottom-default', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
|
= icon('spinner spin 2x', class: 'prepend-top-default append-bottom-default', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
|
||||||
.text-center.prepend-top-default.append-bottom-default.stl-controls
|
.text-center.prepend-top-default.append-bottom-default.stl-controls
|
||||||
.btn-group
|
.btn-group
|
4
app/views/projects/blob/viewers/_svg.html.haml
Normal file
4
app/views/projects/blob/viewers/_svg.html.haml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
- blob = viewer.blob
|
||||||
|
- data = sanitize_svg_data(blob.data)
|
||||||
|
.file-content.image_file
|
||||||
|
%img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(data)}", alt: blob.name }
|
Loading…
Reference in a new issue