summaryrefslogtreecommitdiff
path: root/app/uploaders/file_uploader.rb
blob: 0b591e3bbbbe94cc177e5acfc0367bf050ab1261 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class FileUploader < GitlabUploader
  include RecordsUploads
  include UploaderHelper

  MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}

  storage :file

  def self.absolute_path(upload_record)
    File.join(
      self.dynamic_path_segment(upload_record.model),
      upload_record.path
    )
  end

  # Not using `GitlabUploader.base_dir` because all project namespaces are in
  # the `public/uploads` dir.
  #
  def self.base_dir
    root_dir
  end

  # Returns the part of `store_dir` that can change based on the model's current
  # path
  #
  # This is used to build Upload paths dynamically based on the model's current
  # namespace and path, allowing us to ignore renames or transfers.
  #
  # model - Object that responds to `full_path` and `disk_path`
  #
  # Returns a String without a trailing slash
  def self.dynamic_path_segment(model)
    if model.hashed_storage?(:attachments)
      dynamic_path_builder(model.disk_path)
    else
      dynamic_path_builder(model.full_path)
    end
  end

  # Auxiliary method to build dynamic path segment when not using a project model
  #
  # Prefer to use the `.dynamic_path_segment` as it includes Hashed Storage specific logic
  def self.dynamic_path_builder(path)
    File.join(CarrierWave.root, base_dir, path)
  end

  attr_accessor :model
  attr_reader :secret

  def initialize(model, secret = nil)
    @model = model
    @secret = secret || generate_secret
  end

  def store_dir
    File.join(dynamic_path_segment, @secret)
  end

  def relative_path
    self.file.path.sub("#{dynamic_path_segment}/", '')
  end

  def to_markdown
    to_h[:markdown]
  end

  def to_h
    filename = image_or_video? ? self.file.basename : self.file.filename
    escaped_filename = filename.gsub("]", "\\]")

    markdown = "[#{escaped_filename}](#{secure_url})"
    markdown.prepend("!") if image_or_video? || dangerous?

    {
      alt:      filename,
      url:      secure_url,
      markdown: markdown
    }
  end

  private

  def dynamic_path_segment
    self.class.dynamic_path_segment(model)
  end

  def generate_secret
    SecureRandom.hex
  end

  def secure_url
    File.join('/uploads', @secret, file.filename)
  end
end