2016-02-18 23:19:43 +00:00
|
|
|
# Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects
|
|
|
|
class Blob < SimpleDelegator
|
2016-03-07 13:27:53 +00:00
|
|
|
CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute
|
|
|
|
CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
MAXIMUM_TEXT_HIGHLIGHT_SIZE = 1.megabyte
|
|
|
|
|
|
|
|
RICH_VIEWERS = [
|
2017-04-13 17:21:07 +00:00
|
|
|
BlobViewer::Image,
|
|
|
|
BlobViewer::PDF,
|
|
|
|
BlobViewer::Sketch,
|
|
|
|
BlobViewer::BinarySTL,
|
|
|
|
BlobViewer::TextSTL,
|
|
|
|
BlobViewer::Notebook,
|
|
|
|
BlobViewer::SVG,
|
|
|
|
BlobViewer::Markup,
|
2017-04-13 17:14:08 +00:00
|
|
|
].freeze
|
2016-08-12 15:19:17 +00:00
|
|
|
|
2017-04-13 16:59:52 +00:00
|
|
|
attr_reader :project
|
|
|
|
|
2016-02-18 23:19:43 +00:00
|
|
|
# Wrap a Gitlab::Git::Blob object, or return nil when given nil
|
|
|
|
#
|
|
|
|
# This method prevents the decorated object from evaluating to "truthy" when
|
|
|
|
# given a nil value. For example:
|
|
|
|
#
|
|
|
|
# blob = Blob.new(nil)
|
|
|
|
# puts "truthy" if blob # => "truthy"
|
|
|
|
#
|
|
|
|
# blob = Blob.decorate(nil)
|
|
|
|
# puts "truthy" if blob # No output
|
2017-04-20 23:34:57 +00:00
|
|
|
def self.decorate(blob, project = nil)
|
2016-02-18 23:19:43 +00:00
|
|
|
return if blob.nil?
|
|
|
|
|
2017-04-13 16:59:52 +00:00
|
|
|
new(blob, project)
|
|
|
|
end
|
|
|
|
|
2017-04-20 23:34:57 +00:00
|
|
|
def initialize(blob, project = nil)
|
2017-04-13 16:59:52 +00:00
|
|
|
@project = project
|
|
|
|
|
|
|
|
super(blob)
|
2016-02-18 23:19:43 +00:00
|
|
|
end
|
|
|
|
|
2016-09-12 15:25:35 +00:00
|
|
|
# Returns the data of the blob.
|
|
|
|
#
|
|
|
|
# If the blob is a text based blob the content is converted to UTF-8 and any
|
|
|
|
# invalid byte sequences are replaced.
|
|
|
|
def data
|
|
|
|
if binary?
|
|
|
|
super
|
|
|
|
else
|
|
|
|
@data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-14 10:44:35 +00:00
|
|
|
def no_highlighting?
|
2017-04-13 17:14:08 +00:00
|
|
|
size && size > MAXIMUM_TEXT_HIGHLIGHT_SIZE
|
2016-04-14 10:44:35 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def too_large?
|
2016-05-24 05:59:35 +00:00
|
|
|
size && truncated?
|
2016-04-14 10:44:35 +00:00
|
|
|
end
|
|
|
|
|
2017-04-24 21:27:43 +00:00
|
|
|
# Returns the size of the file that this blob represents. If this blob is an
|
|
|
|
# LFS pointer, this is the size of the file stored in LFS. Otherwise, this is
|
|
|
|
# the size of the blob itself.
|
2017-04-13 17:14:08 +00:00
|
|
|
def raw_size
|
|
|
|
if valid_lfs_pointer?
|
|
|
|
lfs_size
|
|
|
|
else
|
|
|
|
size
|
|
|
|
end
|
2017-04-10 21:23:19 +00:00
|
|
|
end
|
|
|
|
|
2017-04-24 21:27:43 +00:00
|
|
|
# Returns whether the file that this blob represents is binary. If this blob is
|
|
|
|
# an LFS pointer, we assume the file stored in LFS is binary, unless a
|
|
|
|
# text-based rich blob viewer matched on the file's extension. Otherwise, this
|
|
|
|
# depends on the type of the blob itself.
|
2017-04-13 17:14:08 +00:00
|
|
|
def raw_binary?
|
|
|
|
if valid_lfs_pointer?
|
2017-04-24 14:27:19 +00:00
|
|
|
if rich_viewer
|
|
|
|
rich_viewer.binary?
|
|
|
|
else
|
|
|
|
true
|
|
|
|
end
|
2017-04-13 17:14:08 +00:00
|
|
|
else
|
|
|
|
binary?
|
|
|
|
end
|
2016-02-18 23:19:43 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def extension
|
|
|
|
@extension ||= extname.downcase.delete('.')
|
2017-04-03 18:39:50 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def video?
|
|
|
|
UploaderHelper::VIDEO_EXT.include?(extension)
|
2017-03-16 17:04:58 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def readable_text?
|
|
|
|
text? && !valid_lfs_pointer? && !too_large?
|
2017-03-29 05:16:59 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def valid_lfs_pointer?
|
|
|
|
lfs_pointer? && project.lfs_enabled?
|
2017-04-10 21:23:19 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def invalid_lfs_pointer?
|
|
|
|
lfs_pointer? && !project.lfs_enabled?
|
2017-04-06 10:02:24 +00:00
|
|
|
end
|
|
|
|
|
2017-04-13 17:14:08 +00:00
|
|
|
def simple_viewer
|
|
|
|
@simple_viewer ||= simple_viewer_class.new(self)
|
|
|
|
end
|
|
|
|
|
|
|
|
def rich_viewer
|
|
|
|
return @rich_viewer if defined?(@rich_viewer)
|
|
|
|
|
2017-04-24 21:27:43 +00:00
|
|
|
@rich_viewer = rich_viewer_class&.new(self)
|
2017-04-13 17:14:08 +00:00
|
|
|
end
|
|
|
|
|
2017-04-21 18:59:34 +00:00
|
|
|
def rendered_as_text?(ignore_errors: true)
|
2017-04-24 14:27:19 +00:00
|
|
|
simple_viewer.text? && (ignore_errors || simple_viewer.render_error.nil?)
|
2017-04-13 17:14:08 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def show_viewer_switcher?
|
2017-04-21 18:59:34 +00:00
|
|
|
rendered_as_text? && rich_viewer
|
|
|
|
end
|
|
|
|
|
|
|
|
def override_max_size!
|
|
|
|
simple_viewer&.override_max_size = true
|
|
|
|
rich_viewer&.override_max_size = true
|
2017-04-13 17:14:08 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2017-04-24 14:27:19 +00:00
|
|
|
def simple_viewer_class
|
|
|
|
if empty?
|
|
|
|
BlobViewer::Empty
|
|
|
|
elsif raw_binary?
|
|
|
|
BlobViewer::Download
|
|
|
|
else # text
|
|
|
|
BlobViewer::Text
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def rich_viewer_class
|
|
|
|
return if invalid_lfs_pointer? || empty?
|
|
|
|
|
2017-04-24 21:27:43 +00:00
|
|
|
classes =
|
|
|
|
if valid_lfs_pointer?
|
|
|
|
RICH_VIEWERS
|
|
|
|
elsif binary?
|
|
|
|
RICH_VIEWERS.select(&:binary?)
|
|
|
|
else # text
|
|
|
|
RICH_VIEWERS.select(&:text?)
|
|
|
|
end
|
|
|
|
|
|
|
|
classes.find { |viewer_class| viewer_class.can_render?(self) }
|
2017-04-24 14:27:19 +00:00
|
|
|
end
|
2016-02-18 23:19:43 +00:00
|
|
|
end
|