class ModelFilesController < ApplicationController include ActionController::Live rate_limit to: 10, within: 3.minutes, only: :create before_action :get_model before_action -> { @purpose = "download" }, only: [:show, :raw] before_action -> { @purpose = "write" }, only: [:raw_put] before_action :get_file, except: [:create, :bulk_edit, :bulk_update] before_action -> { set_indexable @file }, except: [:create, :bulk_edit, :bulk_update] skip_after_action :verify_authorized, only: [:bulk_edit, :bulk_update] after_action :verify_policy_scoped, only: [:bulk_edit, :bulk_update] def configure_content_security_policy # If embed mode, allow any frame ancestor content_security_policy.frame_ancestors [:https, :http] if embedded? end def show if embedded? respond_to do |format| format.html { render "embedded", layout: "embed" } end elsif stale?(@file) @duplicates = @file.duplicates @related = Naturally.sort([@file, @file.related_files, @file.files_related_to_me].flatten.uniq, by: :name_and_filename) if @file.relationships.any? || @file.reverse_relationships.any? respond_to do |format| format.html format.manyfold_api_v0 { render json: ManyfoldApi::V0::ModelFileSerializer.new(@file, current_user: current_user).serialize } format.any(*MediaType.indexable_types.map(&:to_sym)) do attachment = @file.attachment(params[:derivative]) || @file.attachment send_file_content attachment, derivative: params[:derivative], disposition: (params[:download] == "true") ? :attachment : :inline end end end # i18n-tasks-use t("activerecord.attributes.model_file.filename") # i18n-tasks-use t("activerecord.attributes.model_file.digest") # i18n-tasks-use t("activerecord.attributes.model_file.size") # i18n-tasks-use t("activerecord.attributes.model_file.dimensions") end def raw request.format = params[:format].downcase respond_to @file.mime_type.to_sym send_file_content @file.attachment, disposition: (params[:download] == "true") ? :attachment : :inline end def raw_put body_type = request.content_type.split(";", 2).first body = case body_type when @file.mime_type.to_s request.body when Mime[:url_encoded_form].to_s StringIO.new(params.expect(:body)) end if request.content_length > 0 && body @file.attachment_attacher.attach(body, metadata: {"mime_type" => @file.mime_type&.to_s}.compact) @file.save! @file.parse_metadata_later if body_type == Mime[:url_encoded_form].to_s redirect_to [@file.model, @file] # Redirect if this came from a form else head :ok end else head :bad_request end end def create authorize @model, :edit? if params[:convert] file = @model.model_files.find_param(params[:convert][:id]) authorize(file, :convert?) file.convert_later params[:convert][:to] redirect_back_or_to [@model, file], notice: t(".conversion_started") elsif !(p = upload_params).empty? p.dig(:model, :file).each_pair do |_id, tus_upload| AddUploadedFileToModelJob.perform_later(@model.id, tus_upload) end respond_to do |format| format.html { redirect_to @model, notice: t(".success") } format.manyfold_api_v0 { head :accepted } end else head :unprocessable_content end end def update result = @file.update(file_params) respond_to do |format| format.html do if result current_user.set_list_state(@file, :printed, params[:model_file][:printed] === "1") redirect_to [@model, @file], notice: t(".success") else render :edit, alert: t(".failure"), status: :unprocessable_content end end format.manyfold_api_v0 do if result render json: ManyfoldApi::V0::ModelFileSerializer.new(@file).serialize else render json: @file.errors.to_json, status: :unprocessable_content end end end end def edit_content @editor = FileHandlers.handlers_for(environment: :edit, mime_type: @file.mime_type).first raise ActionController::BadRequest unless @editor end def extract ExtractArchiveJob.perform_later(@file.id) respond_to do |format| format.html do redirect_back_or_to [@model, @file], notice: t(".extraction_started") end end end def bulk_edit authorize @model, :edit? @files = policy_scope(@model.model_files.without_special) end def bulk_update authorize @model, :edit? hash = bulk_update_params ids_to_update = params[:model_files].keep_if { |key, value| value == "1" }.keys files = policy_scope(@model.model_files.without_special).where(public_id: ids_to_update) files.each do |file| ActiveRecord::Base.transaction do current_user.set_list_state(file, :printed, params[:printed] === "1") options = {} if params[:pattern].present? options[:filename] = file.filename.split(file.extension).first.gsub(params[:pattern], params[:replacement]) + file.extension end file.update(hash.merge(options)) end end if params[:split] new_model = @model.split! files: files redirect_to model_path(new_model), notice: t(".success") else redirect_back_or_to model_path(@model), notice: t(".success") end end def destroy authorize @file @file.delete_from_disk_and_destroy respond_to do |format| format.html do if request.referer && (URI.parse(request.referer).path == model_model_file_path(@model, @file)) # If we're coming from the file page itself, we can't go back there redirect_to model_path(@model), notice: t(".success") else redirect_back_or_to model_path(@model), notice: t(".success") end end format.manyfold_api_v0 { head :no_content } end end private def bulk_update_params params.permit( :presupported, :y_up, :previewable ).compact_blank end def file_params if is_api_request? raise ActionController::BadRequest unless params[:json] ManyfoldApi::V0::ModelFileDeserializer.new(object: params[:json], user: current_user, record: @file).deserialize else Form::ModelFileDeserializer.new(params: params, user: current_user, record: @file).deserialize end end def upload_params if is_api_request? raise ActionController::BadRequest unless params[:json] ManyfoldApi::V0::UploadedFileDeserializer.new(object: params[:json], user: current_user).deserialize else Form::UploadedFileDeserializer.new(params: params, user: current_user).deserialize end end def get_model @model = Model.find_param(params[:model_id]) end def get_file scope = @model.model_files begin @file = scope.find_param(params[:id]) rescue ActiveRecord::RecordNotFound @file = scope.find_by!( filename: [ params[:filename], [params[:filename], params[:format]].join("."), [params[:id], params[:format]].join(".") ] ) request.format = params[:format].downcase end # Check for signed download URLs if has_signed_id? && @purpose @signed_file = @model.model_files.find_signed!(params[:sig], purpose: @purpose) if @file == @signed_file skip_authorization else raise ActiveRecord::RecordNotFound end else authorize @file end @title = @file.name rescue ActiveSupport::MessageVerifier::InvalidSignature raise ActiveRecord::RecordNotFound end def embedded? params[:embed] == "true" end end