upload_cache.rb 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. # A wrapper class around Store that handles temporary attachment uploads
  3. # and provides an interface for those.
  4. class UploadCache
  5. attr_reader :id
  6. # Initializes a UploadCache for a given form_id.
  7. #
  8. # @example
  9. # cache = UploadCache.new(form_id)
  10. #
  11. # @return [UploadCache]
  12. def initialize(id)
  13. @id = id
  14. end
  15. # Adds a Store item with the given attributes for the form_id.
  16. #
  17. # @see Store#create!
  18. #
  19. # @example
  20. # cache = UploadCache.new(form_id)
  21. # store = cache.add(
  22. # filename: file.original_filename,
  23. # data: file.read,
  24. # preferences: {
  25. # 'Content-Type' => 'application/octet-stream'
  26. # }
  27. # )
  28. #
  29. # @return [Store] the created Store item
  30. def add(args)
  31. Store.create!(
  32. args.merge(
  33. object: store_object,
  34. o_id: id,
  35. )
  36. )
  37. end
  38. # Provides all Store items associated to the form_id.
  39. #
  40. # @see Store#list
  41. #
  42. # @example
  43. # attachments = UploadCache.new(form_id).attachments
  44. #
  45. # @return [Array<Store>] an enumerator of Store items
  46. def attachments
  47. Store.list(
  48. object: store_object,
  49. o_id: id,
  50. )
  51. end
  52. # Removes all Store items associated to the form_id.
  53. #
  54. # @see Store#remove
  55. #
  56. # @example
  57. # UploadCache.new(form_id).destroy
  58. #
  59. def destroy
  60. Store.remove(
  61. object: store_object,
  62. o_id: id,
  63. )
  64. end
  65. # Removes all Store items associated to the form_id.
  66. #
  67. # @see Store#remove
  68. #
  69. # @example
  70. # UploadCache.new(form_id).remove_item(store_id)
  71. #
  72. # @raise [Exceptions::UnprocessableEntity] in cases where a Store item should get deleted that is not an UploadCache item
  73. #
  74. def remove_item(store_id = nil)
  75. store = Store.find(store_id)
  76. if store.o_id != id || store.store_object_id != store_object_id
  77. raise Exceptions::UnprocessableEntity, "Attempt to delete Store item #{store_id} that is not bound to UploadCache object"
  78. end
  79. Store.remove_item(store_id)
  80. end
  81. # Checks if files list includes a similar looking store attachment.
  82. # Similar-looking attachment is detected by name and file type if it is present.
  83. #
  84. # @param files [Array<Hash>] list of hashes with name and type keys.
  85. # @param single_attachment [Store] a Store object or a similar-looking hash.
  86. #
  87. # @see Store.check_attachment_match
  88. def self.files_include_attachment?(files, single_attachment)
  89. files.any? { |elem| attachment_matches?(single_attachment, elem) }
  90. end
  91. private
  92. def store_object
  93. self.class.name
  94. end
  95. def store_object_id
  96. Store::Object.lookup(name: store_object).id
  97. end
  98. # Checks if attachment is similar to the given file.
  99. #
  100. # @param attachment [Store] with filename key and preferences hash with Content-Type key.
  101. # @param file [Hash] with name and type keys.
  102. def self.attachment_matches?(attachment, file)
  103. return false if file[:name] != attachment.filename
  104. attachment_content_type = attachment.preferences['Content-Type']
  105. if file[:type].blank? || attachment_content_type.blank?
  106. return true
  107. end
  108. file[:type] == attachment_content_type
  109. end
  110. private_class_method :attachment_matches?
  111. end