diff --git a/.ruby-version b/.ruby-version index e4604e3a..5ae69bd5 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.2.1 +3.2.5 diff --git a/Gemfile b/Gemfile index 9126b81f..cd469d4c 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,11 @@ -# ruby=3.2.1 +# ruby=3.2.5 # frozen_string_literal: true source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } # The following line is necessary to allow RVM choosing the correct ruby version. RVM 2.0 will probably be able to interpret the "~>" symbol and we will be able to safely remove the "#ruby=3.2.2" line. -ruby "~> 3.2.1" +ruby "~> 3.2.5" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 7.0.8.1" @@ -89,6 +89,7 @@ gem "prawn" gem "prawn-table" gem "prawn-rails" gem "matrix", "~> 0.4.2" +gem "prawn-qrcode" # Redcarpet for Readme MarkDown (or README.md) - Credits Page gem "redcarpet" diff --git a/Gemfile.lock b/Gemfile.lock index 9f645c06..48c84ad6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,61 +29,61 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.4) - actionpack (= 7.0.8.4) - activesupport (= 7.0.8.4) + actioncable (7.0.8.5) + actionpack (= 7.0.8.5) + activesupport (= 7.0.8.5) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.4) - actionpack (= 7.0.8.4) - activejob (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionmailbox (7.0.8.5) + actionpack (= 7.0.8.5) + activejob (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.8.4) - actionpack (= 7.0.8.4) - actionview (= 7.0.8.4) - activejob (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionmailer (7.0.8.5) + actionpack (= 7.0.8.5) + actionview (= 7.0.8.5) + activejob (= 7.0.8.5) + activesupport (= 7.0.8.5) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.8.4) - actionview (= 7.0.8.4) - activesupport (= 7.0.8.4) + actionpack (7.0.8.5) + actionview (= 7.0.8.5) + activesupport (= 7.0.8.5) rack (~> 2.0, >= 2.2.4) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.4) - actionpack (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + actiontext (7.0.8.5) + actionpack (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.4) - activesupport (= 7.0.8.4) + actionview (7.0.8.5) + activesupport (= 7.0.8.5) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_scaffold_duplicate (1.1.4) active_scaffold (>= 3.6.0.pre) - activejob (7.0.8.4) - activesupport (= 7.0.8.4) + activejob (7.0.8.5) + activesupport (= 7.0.8.5) globalid (>= 0.3.6) - activemodel (7.0.8.4) - activesupport (= 7.0.8.4) - activerecord (7.0.8.4) - activemodel (= 7.0.8.4) - activesupport (= 7.0.8.4) + activemodel (7.0.8.5) + activesupport (= 7.0.8.5) + activerecord (7.0.8.5) + activemodel (= 7.0.8.5) + activesupport (= 7.0.8.5) activerecord-session_store (2.1.0) actionpack (>= 6.1) activerecord (>= 6.1) @@ -91,14 +91,14 @@ GEM multi_json (~> 1.11, >= 1.11.2) rack (>= 2.0.8, < 4) railties (>= 6.1) - activestorage (7.0.8.4) - actionpack (= 7.0.8.4) - activejob (= 7.0.8.4) - activerecord (= 7.0.8.4) - activesupport (= 7.0.8.4) + activestorage (7.0.8.5) + actionpack (= 7.0.8.5) + activejob (= 7.0.8.5) + activerecord (= 7.0.8.5) + activesupport (= 7.0.8.5) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.8.4) + activesupport (7.0.8.5) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -148,6 +148,7 @@ GEM childprocess (5.1.0) logger (~> 1.5) choice (0.2.0) + chunky_png (1.4.0) cocoon (1.2.15) coderay (1.1.3) coffee-rails (5.0.0) @@ -206,7 +207,7 @@ GEM globalid (1.2.1) activesupport (>= 6.1) htmlentities (4.3.4) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) ice_nine (0.11.2) image_processing (1.12.2) @@ -262,7 +263,7 @@ GEM mini_magick (4.12.0) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.25.0) + minitest (5.25.1) msgpack (1.7.2) multi_json (1.15.0) mysql2 (0.5.5) @@ -277,7 +278,7 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.5.9) + nio4r (2.7.3) nokogiri (1.16.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -295,6 +296,9 @@ GEM prawn (2.4.0) pdf-core (~> 0.9.0) ttfunk (~> 1.7) + prawn-qrcode (0.5.2) + prawn (>= 1) + rqrcode (>= 1.0.0) prawn-rails (1.4.2) actionview (>= 3.1.0) prawn @@ -307,27 +311,27 @@ GEM psych (5.1.2) stringio public_suffix (6.0.1) - puma (6.4.2) + puma (6.4.3) nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) rack (2.2.8.1) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.8.4) - actioncable (= 7.0.8.4) - actionmailbox (= 7.0.8.4) - actionmailer (= 7.0.8.4) - actionpack (= 7.0.8.4) - actiontext (= 7.0.8.4) - actionview (= 7.0.8.4) - activejob (= 7.0.8.4) - activemodel (= 7.0.8.4) - activerecord (= 7.0.8.4) - activestorage (= 7.0.8.4) - activesupport (= 7.0.8.4) + rails (7.0.8.5) + actioncable (= 7.0.8.5) + actionmailbox (= 7.0.8.5) + actionmailer (= 7.0.8.5) + actionpack (= 7.0.8.5) + actiontext (= 7.0.8.5) + actionview (= 7.0.8.5) + activejob (= 7.0.8.5) + activemodel (= 7.0.8.5) + activerecord (= 7.0.8.5) + activestorage (= 7.0.8.5) + activesupport (= 7.0.8.5) bundler (>= 1.15.0) - railties (= 7.0.8.4) + railties (= 7.0.8.5) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -340,9 +344,9 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.0.8.4) - actionpack (= 7.0.8.4) - activesupport (= 7.0.8.4) + railties (7.0.8.5) + actionpack (= 7.0.8.5) + activesupport (= 7.0.8.5) method_source rake (>= 12.2) thor (~> 1.0) @@ -363,9 +367,12 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.3.6) - strscan + rexml (3.3.9) rouge (4.2.0) + rqrcode (2.2.0) + chunky_png (~> 1.0) + rqrcode_core (~> 1.0) + rqrcode_core (1.2.0) rspec-collection_matchers (1.2.1) rspec-expectations (>= 2.99.0.beta1) rspec-core (3.12.2) @@ -465,7 +472,6 @@ GEM sqlite3 (1.6.8-x86_64-linux) ssrf_filter (1.1.2) stringio (3.1.1) - strscan (3.1.0) thor (1.3.1) tilt (2.3.0) timeliness (0.4.5) @@ -537,6 +543,7 @@ DEPENDENCIES nokogiri (>= 1.16.5) paper_trail prawn + prawn-qrcode prawn-rails prawn-table pry @@ -568,7 +575,7 @@ DEPENDENCIES web-console RUBY VERSION - ruby 3.2.2p53 + ruby 3.2.5p208 BUNDLED WITH 2.4.19 diff --git a/app/controllers/admissions/admission_phases_controller.rb b/app/controllers/admissions/admission_phases_controller.rb index 04ddc077..3bbced83 100644 --- a/app/controllers/admissions/admission_phases_controller.rb +++ b/app/controllers/admissions/admission_phases_controller.rb @@ -17,7 +17,7 @@ class Admissions::AdmissionPhasesController < ApplicationController form_columns = [ :name, :member_form, :shared_form, :consolidation_form, :candidate_form, :can_edit_candidate, :candidate_can_edit, :candidate_can_see_member, :candidate_can_see_shared, - :candidate_can_see_consolidation, + :candidate_can_see_consolidation, :committee_can_see_other_individual, :approval_condition, :keep_in_phase_condition, :admission_phase_committees, ] diff --git a/app/controllers/admissions/filled_forms_controller.rb b/app/controllers/admissions/filled_forms_controller.rb index c48abac5..292bd0ff 100644 --- a/app/controllers/admissions/filled_forms_controller.rb +++ b/app/controllers/admissions/filled_forms_controller.rb @@ -20,7 +20,7 @@ class Admissions::FilledFormsController < ApplicationController protected def self.filled_form_params_definition [ - :id, :is_filled, :form_template_id, :disable_submission, + :id, :is_filled, :form_template_id, :enable_submission, fields_attributes: [ :id, :form_field_id, :value, :_destroy, :remove_file, :file, list: [], diff --git a/app/controllers/concerns/shared_pdf_concern.rb b/app/controllers/concerns/shared_pdf_concern.rb index 967c3548..718d9793 100644 --- a/app/controllers/concerns/shared_pdf_concern.rb +++ b/app/controllers/concerns/shared_pdf_concern.rb @@ -34,7 +34,7 @@ def render_course_classes_summary_pdf(course_class) ) end - def render_enrollments_academic_transcript_pdf(enrollment) + def render_enrollments_academic_transcript_pdf(enrollment, filename = "transcript.pdf") class_enrollments = enrollment.class_enrollments .where(situation: ClassEnrollment::APPROVED) .joins(:course_class) @@ -47,6 +47,7 @@ def render_enrollments_academic_transcript_pdf(enrollment) type: "application/pdf", formats: [:pdf], assigns: { + filename: filename, enrollment: enrollment, class_enrollments: class_enrollments, accomplished_phases: accomplished_phases, @@ -55,7 +56,7 @@ def render_enrollments_academic_transcript_pdf(enrollment) ) end - def render_enrollments_grades_report_pdf(enrollment) + def render_enrollments_grades_report_pdf(enrollment, filename = "grades_report.pdf") class_enrollments = enrollment.class_enrollments .where(situation: ClassEnrollment::APPROVED) .joins(:course_class) @@ -67,6 +68,7 @@ def render_enrollments_grades_report_pdf(enrollment) type: "application/pdf", formats: [:pdf], assigns: { + filename: filename, enrollment: enrollment, class_enrollments: class_enrollments, accomplished_phases: accomplished_phases, diff --git a/app/controllers/enrollments_controller.rb b/app/controllers/enrollments_controller.rb index cd206859..3b921f0b 100644 --- a/app/controllers/enrollments_controller.rb +++ b/app/controllers/enrollments_controller.rb @@ -126,6 +126,7 @@ class EnrollmentsController < ApplicationController :thesis_title, :thesis_defense_date, :obs, + :obs_to_academic_transcript, :advisements, :scholarship_durations, @@ -219,8 +220,9 @@ def academic_transcript_pdf format.pdf do title = I18n.t("pdf_content.enrollment.academic_transcript.title") student = enrollment.student.name - send_data render_enrollments_academic_transcript_pdf(enrollment), - filename: "#{title} - #{student}.pdf", + filename = "#{title} - #{student}.pdf" + send_data render_enrollments_academic_transcript_pdf(enrollment, filename), + filename: filename, type: "application/pdf" end end @@ -232,8 +234,9 @@ def grades_report_pdf format.pdf do title = I18n.t("pdf_content.enrollment.grades_report.title") student = enrollment.student.name - send_data render_enrollments_grades_report_pdf(enrollment), - filename: "#{title} - #{student}.pdf", + filename = "#{title} - #{student}.pdf" + send_data render_enrollments_grades_report_pdf(enrollment, filename), + filename: filename, type: "application/pdf" end end diff --git a/app/controllers/grants_controller.rb b/app/controllers/grants_controller.rb new file mode 100644 index 00000000..4afcd0da --- /dev/null +++ b/app/controllers/grants_controller.rb @@ -0,0 +1,33 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class GrantsController < ApplicationController + authorize_resource + + active_scaffold :grant do |config| + config.create.label = :create_grant_label + config.columns = [:title, :start_year, :end_year, :professor, :kind, :funder, :amount] + config.actions.swap :search, :field_search + config.columns[:professor].form_ui = :record_select + config.columns[:kind].form_ui = :select + config.columns[:kind].options = { + options: Grant::KINDS, + default: Grant::PUBLIC, + include_blank: I18n.t("active_scaffold._select_") + } + config.columns[:amount].options[:format] = :currency + config.columns[:start_year].options[:format] = "%d" + config.columns[:end_year].options[:format] = "%d" + config.actions.exclude :deleted_records + end + + protected + def do_new + super + unless current_user.professor.blank? + @record.professor = current_user.professor + end + end +end diff --git a/app/controllers/paper_professors_controller.rb b/app/controllers/paper_professors_controller.rb new file mode 100644 index 00000000..5a032626 --- /dev/null +++ b/app/controllers/paper_professors_controller.rb @@ -0,0 +1,16 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class PaperProfessorsController < ApplicationController + authorize_resource + + active_scaffold :paper_professor do |config| + config.create.label = :create_paper_professor_label + config.columns = [:paper, :professor] + config.columns[:professor].form_ui = :record_select + + config.actions.exclude :deleted_records + end +end diff --git a/app/controllers/paper_students_controller.rb b/app/controllers/paper_students_controller.rb new file mode 100644 index 00000000..7c2681b0 --- /dev/null +++ b/app/controllers/paper_students_controller.rb @@ -0,0 +1,16 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class PaperStudentsController < ApplicationController + authorize_resource + + active_scaffold :paper_student do |config| + config.create.label = :create_paper_student_label + config.columns = [:paper, :student] + config.columns[:student].form_ui = :record_select + + config.actions.exclude :deleted_records + end +end diff --git a/app/controllers/papers_controller.rb b/app/controllers/papers_controller.rb new file mode 100644 index 00000000..50807054 --- /dev/null +++ b/app/controllers/papers_controller.rb @@ -0,0 +1,86 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class PapersController < ApplicationController + authorize_resource + + active_scaffold :paper do |config| + config.create.label = :create_paper_label + config.actions.swap :search, :field_search + config.list.sorting = { period: "DESC", owner: "ASC", order: "ASC" } + + config.columns.add :reason_group + config.columns.add :reason_group_end + form_columns = [ + :period, :owner, :reference, :order, :kind, :doi_issn_event, + :paper_professors, :paper_students, :other_authors, + :reason_group, + :reason_impact_factor, + :reason_international_list, + :reason_citations, + :reason_national_interest, + :reason_international_interest, + :reason_national_representativeness, + :reason_scientific_contribution, + :reason_tech_contribution, + :reason_innovation_contribution, + :reason_social_contribution, + :reason_other, + :reason_justify, + :reason_group_end, + :other, + ] + config.create.columns = form_columns + config.update.columns = form_columns + config.show.columns = [ + :period, :owner, :reference, :kind, :doi_issn_event, + :paper_professors, :paper_students, :other_authors, + :reason_impact_factor, + :reason_international_list, + :reason_citations, + :reason_national_interest, + :reason_international_interest, + :reason_national_representativeness, + :reason_scientific_contribution, + :reason_tech_contribution, + :reason_innovation_contribution, + :reason_social_contribution, + :reason_other, + :reason_justify, + :order, + :other, + ] + config.list.columns = [ + :period, :owner, :order, :reference + ] + + config.columns[:owner].form_ui = :record_select + config.columns[:kind].form_ui = :select + config.columns[:kind].options = { + options: Paper::KINDS, + include_blank: I18n.t("active_scaffold._select_") + } + config.columns[:order].form_ui = :select + config.columns[:order].options = { + options: Paper::ORDERS, + include_blank: I18n.t("active_scaffold._select_") + } + + + config.columns[:paper_students].show_blank_record = false + config.columns[:paper_professors].show_blank_record = false + + config.actions.exclude :deleted_records + end + + protected + def do_new + super + @record.period = CustomVariable.quadrennial_period + unless current_user.professor.blank? + @record.owner = current_user.professor + end + end +end diff --git a/app/controllers/report_configurations_controller.rb b/app/controllers/report_configurations_controller.rb index cee72e97..2f1cb399 100644 --- a/app/controllers/report_configurations_controller.rb +++ b/app/controllers/report_configurations_controller.rb @@ -5,24 +5,28 @@ class ReportConfigurationsController < ApplicationController authorize_resource - skip_before_action :verify_authenticity_token, only: [:preview] + skip_before_action :verify_authenticity_token, only: [:preview] # noqa # do not remove this line based on Code Scanner suggestions include ApplicationHelper active_scaffold :report_configuration do |config| config.create.label = :create_report_configuration_label columns = [ - :name, :image, :scale, :x, :y, :order, :text, :signature_footer, - :preview, :use_at_report, :use_at_transcript, :use_at_grades_report, - :use_at_schedule, + :name, :image, :scale, :x, :y, :order, :text, :signature_type, + :preview, :use_at_report, :use_at_transcript, + :use_at_grades_report, :use_at_schedule, :expiration_in_months ] config.create.columns = columns config.update.columns = columns columns.delete(:preview) config.columns = columns config.list.columns = [ - :name, :order, :text, :signature_footer, :use_at_report, - :use_at_transcript, :use_at_grades_report, :use_at_schedule + :name, :order, :text, :signature_type, + :use_at_report, :use_at_transcript, :use_at_grades_report, + :use_at_schedule, :expiration_in_months ] + config.columns[:signature_type].form_ui = :select + config.columns[:signature_type].options = { options: ReportConfiguration.signature_types.keys.map(&:to_sym) } + config.columns[:expiration_in_months].description = I18n.t("active_scaffold.report_configurations.expiration_in_months_description") config.list.sorting = { name: "ASC" } config.actions << :duplicate config.duplicate.link.label = " @@ -41,7 +45,7 @@ def preview record.assign_attributes(record_params.except(:image_cache, :remove_image)) # up = ImageUploader.new record, :image # up.store(File.open(params[:record][:image])) - @pdf_config = record + @pdf_config = record.tap { |r| r.preview = true } respond_to do |format| format.pdf do title = I18n.t("pdf_content.report_configurations.preview") @@ -61,8 +65,8 @@ def logo def record_params params.required(:record).permit( :name, :use_at_report, :use_at_transcript, :use_at_grades_report, - :use_at_schedule, :text, :image, :signature_footer, :order, :scale, - :x, :y + :use_at_schedule, :text, :image, :order, :scale, + :x, :y, :signature_type, :expiration_in_months ) end end diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb new file mode 100644 index 00000000..6aabf11e --- /dev/null +++ b/app/controllers/reports_controller.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +class ReportsController < ApplicationController + before_action :set_report, only: [:download, :download_by_identifier] + before_action :check_downloadable, only: [:download, :download_by_identifier] + authorize_resource + + skip_authorization_check only: :download_by_identifier + skip_authorize_resource only: :download_by_identifier + skip_before_action :authenticate_user!, only: :download_by_identifier + + active_scaffold :report do |config| + config.list.columns = [:user, :file_name, :identifier, :created_at, :expires_at] + config.show.columns = [:user, :file_name, :identifier, :created_at, :expires_at] + config.update.columns = [:expires_at] + config.columns[:user].clear_link + config.actions.exclude :create, :delete + config.action_links.add "download", + label: " + + ".html_safe, + page: true, + type: :member, + parameters: { format: :pdf }, + method: :get, + html_options: { target: "_blank" }, + ignore_method: :cant_download? + end + + def download + redirect_to download_path(medium_hash: @report.carrierwave_file.medium_hash) + end + + def download_by_identifier + send_data(@report.carrierwave_file.read, filename: @report.carrierwave_file.original_filename, disposition: :inline) + end + + private + def set_report + @report = params[:id] ? Report.find(params[:id]) : Report.find_by_identifier(params[:identifier]) + raise ActionController::RoutingError.new("Este documento não foi encontrado.") if @report.nil? + end + + def check_downloadable + raise ActionController::RoutingError.new("Este documento expirou.") if cant_download?(@report) + end + + def cant_download?(record) + record.carrierwave_file.blank? + end +end diff --git a/app/helpers/enrollments_pdf_helper.rb b/app/helpers/enrollments_pdf_helper.rb index b192b868..48fb8c58 100644 --- a/app/helpers/enrollments_pdf_helper.rb +++ b/app/helpers/enrollments_pdf_helper.rb @@ -295,7 +295,7 @@ def transcript_table(pdf, options = {}) if table_data.size >= page_size new_page = true - next_table_data = table_data.slice(page_size..-1) + next_table_data = table_data.slice(page_size + 1..-1) table_data = table_data.slice(0..page_size) page_size = 50 end @@ -711,6 +711,32 @@ def thesis_table(curr_pdf, options = {}) end end + def obs_table(pdf, options = {}) + if options[:enrollment]&.obs_to_academic_transcript.present? + data_table_rows = [[ + "#{I18n.t("pdf_content.enrollment.academic_transcript.obs_to_academic_transcript")} #{ + rescue_blank_text(options[:enrollment].obs_to_academic_transcript) + }" + ]] + + pdf.table( + data_table_rows, + cell_style: { + borders: [:left, :right, :bottom, :top], + border_bottom_width: 0.5, + border_top_width: 0.15, + width: 560, + padding_top: 5.5, + padding_bottom: 7.5, + inline_format: true, + font: "FreeMono", + size: 8, + border_color: "000080" + } + ) + end + end + def justification_grade_not_count_in_gpr_table(curr_pdf, options = {}) enrollment ||= options[:enrollment] diff --git a/app/helpers/grants_helper.rb b/app/helpers/grants_helper.rb new file mode 100644 index 00000000..ae6340cf --- /dev/null +++ b/app/helpers/grants_helper.rb @@ -0,0 +1,17 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +module GrantsHelper + def professor_form_column(record, options) + if can?(:edit_professor, record) + record_select_field :professor, record.professor || Professor.new, options + else + options[:value] = record.professor_id + label( + :record_value_1_params, record.id, record.professor.name + ) + hidden_field(:record, :professor, options) + end + end +end diff --git a/app/helpers/paper_professors_helper.rb b/app/helpers/paper_professors_helper.rb new file mode 100644 index 00000000..5e2d8c7e --- /dev/null +++ b/app/helpers/paper_professors_helper.rb @@ -0,0 +1,7 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +module PaperProfessorsHelper +end \ No newline at end of file diff --git a/app/helpers/paper_students_helper.rb b/app/helpers/paper_students_helper.rb new file mode 100644 index 00000000..2e20d80a --- /dev/null +++ b/app/helpers/paper_students_helper.rb @@ -0,0 +1,7 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +module PaperStudentsHelper +end \ No newline at end of file diff --git a/app/helpers/papers_helper.rb b/app/helpers/papers_helper.rb new file mode 100644 index 00000000..c1366ca5 --- /dev/null +++ b/app/helpers/papers_helper.rb @@ -0,0 +1,31 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +module PapersHelper + def owner_form_column(record, options) + if can?(:edit_professor, record) + record_select_field :owner, record.owner || Professor.new, options + else + options[:value] = record.owner_id + label( + :record_value_1_params, record.id, record.owner.name + ) + hidden_field(:record, :owner, options) + end + end + + def period_form_column(record, options) + if can?(:edit_professor, record) + text_field :record, :period, options + else + label( + :record_value_1_params, record.id, record.period + ) + hidden_field(:record, :period, options) + end + end + + def reason_group_form_column(record, options) + "aaaa" + end +end \ No newline at end of file diff --git a/app/helpers/pdf_helper.rb b/app/helpers/pdf_helper.rb index 979de3ab..512b29f9 100644 --- a/app/helpers/pdf_helper.rb +++ b/app/helpers/pdf_helper.rb @@ -98,8 +98,7 @@ def signature_footer(pdf, options = {}) diff_width = options[:diff_width] diff_width ||= 0 - - last_box_height = 50 + last_box_height = options[:qr_code_signature] ? 80 : 50 last_box_width1 = 165 last_box_width2 = 335 @@ -109,47 +108,106 @@ def signature_footer(pdf, options = {}) [0, last_box_y], width: last_box_width1, height: last_box_height ) do - pdf.stroke_bounds current_x = x - pdf.move_down last_box_height / 2 - pdf.draw_text( - "#{I18n.t("pdf_content.enrollment.footer.location")}, #{I18n.localize( - Date.today, format: :long - )}", at: [current_x, pdf.cursor]) + if options[:qr_code_signature] + qrcode_signature(pdf, { size: 80 }) + else + pdf.stroke_bounds + + pdf.move_down last_box_height / 2 + pdf.draw_text( + "#{I18n.t("pdf_content.enrollment.footer.location")}, #{I18n.localize( + Date.today, format: :long + )}", at: [current_x, pdf.cursor]) + end end end - pdf.font("FreeMono", size: 6) do + signature_font_size = options[:qr_code_signature] ? 8 : 6 + + pdf.font("FreeMono", size: signature_font_size) do pdf.bounding_box( [last_box_width1, last_box_y], width: last_box_width2, height: last_box_height ) do pdf.stroke_bounds - current_x = x - pdf.move_down 8 - pdf.draw_text( - "#{I18n.t("pdf_content.enrollment.footer.warning1")}", - at: [current_x, pdf.cursor] - ) + if options[:qr_code_signature] + qrcode_url = download_by_identifier_reports_url(identifier: @qrcode_identifier) + qrcode_signature_warning = I18n.t("pdf_content.enrollment.footer.qrcode_signature_warning") + signed_at = "#{I18n.t("pdf_content.enrollment.footer.signed_by_qrcode")} #{I18n.l(Time.now, format: :defaultdatetime)} (Horário de Brasília)" + you_can_also_access = I18n.t("pdf_content.enrollment.footer.you_can_also_access") - underline_width = 3.7 - pdf.move_down 30 - underline = "_" * 74 - current_x += (last_box_width2 - underline.size * underline_width) / 2 + all_sentences = [qrcode_signature_warning, signed_at, you_can_also_access, qrcode_url] + if options[:expires_at] + valid_until = "#{I18n.t("pdf_content.enrollment.footer.valid_until")} #{I18n.l(Date.today + options[:expires_at].months, format: :default)}" + all_sentences.append(valid_until) + end - pdf.draw_text(underline, at: [current_x, pdf.cursor]) + center_around = all_sentences.max_by(&:size) + pdf.move_down (last_box_height - signature_font_size * (all_sentences.count - 1)) / 2 + current_x = (last_box_width2 - pdf.width_of(center_around)) / 2 - pdf.move_down 8 - font_width = 6.7 - coordinator_signature = I18n.t( - "pdf_content.enrollment.footer.coordinator_signature" - ) - current_x += ( - last_box_width2 - coordinator_signature.size * font_width - ) / 2 - pdf.draw_text(coordinator_signature, at: [current_x, pdf.cursor]) + pdf.draw_text( + "#{qrcode_signature_warning}", + at: [current_x, pdf.cursor] + ) + + pdf.move_down signature_font_size + + pdf.draw_text( + signed_at, + at: [current_x, pdf.cursor] + ) + + pdf.move_down signature_font_size + + pdf.draw_text( + you_can_also_access, + at: [current_x, pdf.cursor] + ) + + pdf.move_down signature_font_size + + pdf.draw_text( + qrcode_url, + at: [current_x, pdf.cursor] + ) + if options[:expires_at] + 2.times { pdf.move_down signature_font_size } + + pdf.draw_text( + valid_until, + at: [current_x, pdf.cursor] + ) + end + else + current_x = x + pdf.move_down 8 + + pdf.draw_text( + "#{I18n.t("pdf_content.enrollment.footer.warning1")}", + at: [current_x, pdf.cursor] + ) + + underline_width = 3.7 + pdf.move_down 30 + underline = "_" * 74 + current_x += (last_box_width2 - underline.size * underline_width) / 2 + + pdf.draw_text(underline, at: [current_x, pdf.cursor]) + + pdf.move_down 8 + font_width = 6.7 + coordinator_signature = I18n.t( + "pdf_content.enrollment.footer.coordinator_signature" + ) + current_x += ( + last_box_width2 - coordinator_signature.size * font_width + ) / 2 + pdf.draw_text(coordinator_signature, at: [current_x, pdf.cursor]) + end end end @@ -226,14 +284,9 @@ def text_table(pdf, data_table, default_margin_indent) def new_document(name, title, options = {}, &block) type = options[:pdf_type] || :report - pdf_type = :"use_at_#{type}" - pdf_config = ( - options[:pdf_config] || - ReportConfiguration.where(pdf_type => true).order(order: :desc).first || - ReportConfiguration.new - ) + pdf_config = setup_pdf_config(type, options) - prawn_document({ + document = prawn_document({ page_size: "A4", left_margin: 0.6.cm, right_margin: ( @@ -241,7 +294,7 @@ def new_document(name, title, options = {}, &block) ), top_margin: 0.8.cm, bottom_margin: ( - pdf_config.signature_footer ? 96 + FOOTER_TOP_MARGIN : 1.cm + !pdf_config.no_signature? ? 96 + FOOTER_TOP_MARGIN : 1.cm ), filename: name }.merge(options)) do |pdf| @@ -275,10 +328,16 @@ def new_document(name, title, options = {}, &block) header(pdf, title, pdf_config) yield pdf - pdf.repeat(:all, dynamic: true) do - if pdf_config.signature_footer + if pdf_config.qr_code? + pdf.repeat(:all, dynamic: true) do + signature_footer(pdf, { qr_code_signature: true, expires_at: pdf_config.expiration_in_months }) + end + elsif pdf_config.manual? + pdf.repeat(:all, dynamic: true) do signature_footer(pdf) - else + end + else + pdf.repeat(:all, dynamic: true) do datetime_footer(pdf) end end @@ -299,6 +358,21 @@ def new_document(name, title, options = {}, &block) end end end + + if pdf_config.qr_code_signature && !pdf_config.preview + uploader = PdfUploader.new + uploader.store!({ base64_contents: Base64.encode64(document), filename: name }) + + Report.create!( + expires_at: pdf_config.expiration_in_months.present? ? Date.today + pdf_config.expiration_in_months.months : nil, + user: current_user, + carrierwave_file: uploader.file&.file, + file_name: name, + identifier: @qrcode_identifier + ) + end + + document end def pdf_list_with_title(pdf, title, data, options = {}, &block) @@ -400,20 +474,18 @@ def simple_pdf_table(pdf, widths, header, data, options = {}, join_header_and_ta padding: 2 } ) do |table| - table.before_rendering_page do |page| - page.row(0).background_color = "E5E5FF" page.row(0).borders = [:top, :bottom, :left, :right] page.row(0).column(0).align = :center page.row(0).column(-1).align = :center - + if page.row_count > 1 page.rows(1 .. -1).text_color = "000000" page.row(-1).borders = [:bottom, :left, :right] end end - + yield table unless block.nil? end pdf.fill_color "000080" @@ -422,7 +494,28 @@ def simple_pdf_table(pdf, widths, header, data, options = {}, join_header_and_ta end end end + end + def qrcode_signature(pdf, options = {}) + @qrcode_identifier ||= generate_qr_code_key + while Report.where(identifier: @qrcode_identifier).exists? + @qrcode_identifier = generate_qr_code_key + end + + data = download_by_identifier_reports_url(identifier: @qrcode_identifier) + + pdf.print_qr_code(data, extent: options[:size] || 80, align: :center) + end + + def setup_pdf_config(pdf_type, options) + pdf_type_property = :"use_at_#{pdf_type}" + options[:pdf_config] || + ReportConfiguration.where(pdf_type_property => true).order(order: :desc).first || + ReportConfiguration.new + end + def generate_qr_code_key + 10.times.map { "2346789BCDFGHJKMPQRTVWXY".split("").sample } + .insert(5, "-").join("") end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 5e5feae8..fe451919 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -16,7 +16,7 @@ class Ability PROFESSOR_MODELS = [ Professor, Advisement, AdvisementAuthorization, ThesisDefenseCommitteeParticipation, - ProfessorResearchArea, + ProfessorResearchArea, Grant, Paper, PaperProfessor, PaperStudent ] SCHOLARSHIP_MODELS = [ @@ -39,8 +39,12 @@ class Ability Major, Institution, ] + DOCUMENT_MODELS = [ + Report + ] + PLACE_MODELS = [ - City, State, Country, + City, State, Country ] CONFIGURATION_MODELS = [ @@ -81,6 +85,7 @@ def initialize(user) initialize_phases(user, roles) initialize_courses(user, roles) initialize_education(user, roles) + initialize_documents(user, roles) initialize_places(user, roles) initialize_admissions(user, roles) initialize_configurations(user, roles) @@ -140,6 +145,22 @@ def initialize_professors(user, roles) end if roles[Role::ROLE_PROFESSOR] can :read, Ability::PROFESSOR_MODELS + cannot :edit_professor, Grant + cannot :edit_professor, Paper + can :create, Grant + can :update, Grant, professor: user.professor + can :destroy, Grant, professor: user.professor + can :create, Paper + can :update, Paper, owner: user.professor + can :create, PaperProfessor + can :create, PaperStudent + can :update, PaperProfessor, paper: { owner: user.professor } + can :update, PaperStudent, paper: { owner: user.professor } + can :destroy, PaperProfessor, paper: { owner: user.professor } + can :destroy, PaperStudent, paper: { owner: user.professor } + can :destroy, PaperProfessor, paper: { owner: nil } + can :destroy, PaperStudent, paper: { owner: nil } + can :destroy, Paper, owner: user.professor end end @@ -218,6 +239,13 @@ def initialize_education(user, roles) end end + def initialize_documents(user, roles) + if roles[:manager] + can :manage, Ability::DOCUMENT_MODELS + cannot :update, Report unless roles[Role::ROLE_ADMINISTRADOR] + end + end + def initialize_places(user, roles) if roles[:manager] can :manage, Ability::PLACE_MODELS diff --git a/app/models/admissions/ability.rb b/app/models/admissions/ability.rb index dc4c4f23..be71c78f 100644 --- a/app/models/admissions/ability.rb +++ b/app/models/admissions/ability.rb @@ -77,6 +77,7 @@ def initialize_admissions(user, roles) can :cancel, Admissions::AdmissionApplication can :configuration, Admissions::AdmissionApplication can :read_all, Admissions::AdmissionApplication + can :read_pendencies, Admissions::AdmissionApplication can :map_student, Admissions::AdmissionApplication can :custom_report, Admissions::AdmissionProcess end diff --git a/app/models/admissions/admission_application.rb b/app/models/admissions/admission_application.rb index c5e94657..2f1aed7c 100644 --- a/app/models/admissions/admission_application.rb +++ b/app/models/admissions/admission_application.rb @@ -411,7 +411,7 @@ def assign_form( self.assign_attributes(params) all_forms = [] all_phase_forms = [] - if self.filled_form.try(:disable_submission) != "1" + if self.filled_form.try(:enable_submission) == "1" self.filled_form.prepare_missing_fields self.filled_form.sync_fields_after(self) all_forms << { @@ -423,7 +423,7 @@ def assign_form( end if has_letter_forms self.letter_requests.each do |letter_request| - if letter_request.filled_form.try(:disable_submission) != "1" + if letter_request.filled_form.try(:enable_submission) == "1" letter_request.filled_form.sync_fields_after(letter_request) all_forms << { was_filled: letter_request.filled_form.is_filled, @@ -449,7 +449,7 @@ def assign_form( # Do not solve pendency if it was not created by assign_attributes next if phase_form[:from_build] # Do not solve pendency if form submission was disabled - next if phase_form[:object].filled_form.try(:disable_submission) == "1" + next if phase_form[:object].filled_form.try(:enable_submission) != "1" phase_form[:was_filled] = phase_form[:object].filled_form.is_filled phase_form[:object].filled_form.is_filled = true all_phase_forms << phase_form @@ -462,7 +462,7 @@ def assign_form( self.ordered_rankings.each do |ranking| next if !ranking.filled_form.is_filled next if !can_edit_override - next if ranking.filled_form.try(:disable_submission) == "1" + next if ranking.filled_form.try(:enable_submission) != "1" ranking.filled_form.prepare_missing_fields all_forms << { was_filled: ranking.filled_form.is_filled, diff --git a/app/models/admissions/admission_phase.rb b/app/models/admissions/admission_phase.rb index 6506145f..93e5f797 100644 --- a/app/models/admissions/admission_phase.rb +++ b/app/models/admissions/admission_phase.rb @@ -246,7 +246,10 @@ def prepare_application_forms( ) && (phase_form[:mode] == :candidate) end if committee_permission_user.present? - next false if phase_form[:mode] == :member && phase_form[:user_id] != committee_permission_user.id + if phase_form[:mode] == :member + next false if phase_form[:user_id] != committee_permission_user.id && !current_phase.try(:committee_can_see_other_individual) + can_edit_form = can_edit_override || phase_form[:user_id] == committee_permission_user.id + end if phase_form[:mode] == :candidate can_edit_form = can_edit_override || current_phase.try(:can_edit_candidate) end diff --git a/app/models/admissions/filled_form.rb b/app/models/admissions/filled_form.rb index d291a99e..a4ea7985 100644 --- a/app/models/admissions/filled_form.rb +++ b/app/models/admissions/filled_form.rb @@ -6,7 +6,7 @@ class Admissions::FilledForm < ActiveRecord::Base has_paper_trail - attr_accessor :disable_submission + attr_accessor :enable_submission has_one :admission_application, dependent: :restrict_with_exception, class_name: "Admissions::AdmissionApplication" diff --git a/app/models/custom_variable.rb b/app/models/custom_variable.rb index ad6371e1..a7bb4d8c 100644 --- a/app/models/custom_variable.rb +++ b/app/models/custom_variable.rb @@ -24,6 +24,7 @@ class CustomVariable < ApplicationRecord "year_semester_range" => :text, "past_calendar_range" => :text, "academic_calendar_range" => :text, + "quadrennial_period" => :text, } validates :variable, presence: true @@ -109,6 +110,12 @@ def self.academic_calendar_range self.parse_calendar_range(config, [20, 10, false]) end + def self.quadrennial_period + config = CustomVariable.find_by_variable(:quadrennial_period) + config.blank? ? "Not defined" : config.value + end + + def to_label self.variable.to_s end diff --git a/app/models/grant.rb b/app/models/grant.rb new file mode 100644 index 00000000..95373194 --- /dev/null +++ b/app/models/grant.rb @@ -0,0 +1,53 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class Grant < ApplicationRecord + has_paper_trail + belongs_to :professor, optional: false + + PUBLIC = I18n.translate( + "activerecord.attributes.grant.kinds.public" + ) + PRIVATE = I18n.translate( + "activerecord.attributes.grant.kinds.private" + ) + KINDS = [PUBLIC, PRIVATE] + + validates :title, presence: true + validates :start_year, presence: true + validates :kind, presence: true, inclusion: { in: KINDS } + validates :funder, presence: true + validates :amount, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :professor, presence: true + validate :that_professor_cannot_edit_other_grants, if: -> { cannot?(:edit_professor, self) } + validate :that_end_year_is_greater_than_start_year + + + def that_professor_cannot_edit_other_grants + current_professor = current_user&.professor + return if current_professor.nil? + if self.changes[:professor_id].present? && self.changes[:professor_id] != [nil, current_professor.id] + self.errors.add(:professor, :cannot_edit_other_grants) + end + end + + def that_end_year_is_greater_than_start_year + return if self.end_year.nil? + if self.end_year < self.start_year + self.errors.add(:end_year, :start_greater_than_end) + end + end + + def to_label + "[#{self.start_year}] #{self.title}" + end + + private + delegate :can?, :cannot?, to: :ability + + def ability + @ability ||= Ability.new(current_user) + end +end diff --git a/app/models/paper.rb b/app/models/paper.rb new file mode 100644 index 00000000..a6a8e442 --- /dev/null +++ b/app/models/paper.rb @@ -0,0 +1,55 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class Paper < ApplicationRecord + has_paper_trail + + attr_accessor :reason_group, :reason_group_end, :header + + belongs_to :owner, optional: false, class_name: "Professor" + has_many :paper_professors, dependent: :destroy + has_many :paper_students, dependent: :destroy + has_many :professors, through: :paper_professors + has_many :students, through: :paper_students + + JOURNAL = I18n.translate( + "activerecord.attributes.paper.kinds.journal" + ) + CONFERENCE = I18n.translate( + "activerecord.attributes.paper.kinds.conference" + ) + KINDS = [JOURNAL, CONFERENCE] + + ORDERS = [1, 2, 3, 4, 5, 6, 7, 8] + + validates :reference, presence: true + validates :kind, presence: true, inclusion: { in: KINDS } + validates :doi_issn_event, presence: true + validates :reason_justify, presence: true + validates :order, presence: true, inclusion: { in: ORDERS }, uniqueness: { + scope: [:period, :owner_id], message: :order_uniqueness + } + + validate :that_professor_cannot_edit_other_papers, if: -> { cannot?(:edit_professor, self) } + + def that_professor_cannot_edit_other_papers + current_professor = current_user&.professor + return if current_professor.nil? + if self.changes[:owner_id].present? && self.changes[:owner_id] != [nil, current_professor.id] + self.errors.add(:owner, :cannot_edit_other_papers) + end + end + + def to_label + "[#{self.owner.to_label}] #{self.reference}" + end + + private + delegate :can?, :cannot?, to: :ability + + def ability + @ability ||= Ability.new(current_user) + end +end diff --git a/app/models/paper_professor.rb b/app/models/paper_professor.rb new file mode 100644 index 00000000..733596ca --- /dev/null +++ b/app/models/paper_professor.rb @@ -0,0 +1,15 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class PaperProfessor < ApplicationRecord + has_paper_trail + + belongs_to :paper, optional: false + belongs_to :professor, optional: false + + def to_label + "#{self.professor.to_label} - #{self.paper.to_label}" + end +end diff --git a/app/models/paper_student.rb b/app/models/paper_student.rb new file mode 100644 index 00000000..675ac399 --- /dev/null +++ b/app/models/paper_student.rb @@ -0,0 +1,10 @@ +class PaperStudent < ApplicationRecord + has_paper_trail + + belongs_to :paper + belongs_to :student + + def to_label + "#{self.student.to_label} - #{self.paper.to_label}" + end +end diff --git a/app/models/report.rb b/app/models/report.rb new file mode 100644 index 00000000..63b5410b --- /dev/null +++ b/app/models/report.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Report < ApplicationRecord + belongs_to :user, foreign_key: "generated_by_id" + belongs_to :carrierwave_file, foreign_key: "carrierwave_file_id", class_name: "CarrierWave::Storage::ActiveRecord::ActiveRecordFile", optional: true + + def to_label + "#{self.user.name} - #{I18n.l(self.created_at, format: '%d/%m/%Y %H:%M')}" + end +end diff --git a/app/models/report_configuration.rb b/app/models/report_configuration.rb index 6e8b75b5..7b6b8371 100644 --- a/app/models/report_configuration.rb +++ b/app/models/report_configuration.rb @@ -6,15 +6,20 @@ # Represents Configuration of a Report class ReportConfiguration < ApplicationRecord has_paper_trail + attr_accessor :preview validates :text, presence: true validates :order, presence: true validates :x, presence: true validates :y, presence: true validates :scale, presence: true + validates :expiration_in_months, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true + validate :expiration_in_months_only_for_qr_code mount_uploader :image, ImageUploader + enum signature_type: { no_signature: 0, manual: 1, qr_code: 2 } + def initialize_dup(other) super attrib = other.attributes.except("id", "created_at", "updated_at") @@ -24,4 +29,19 @@ def initialize_dup(other) def mount_uploader_name :image end + + def signature_footer + signature_type === "manual" + end + + def qr_code_signature + signature_type === "qr_code" + end + + private + def expiration_in_months_only_for_qr_code + if expiration_in_months.present? && signature_type != "qr_code" + errors.add(:expiration_in_months, :only_for_qr_code) + end + end end diff --git a/app/uploaders/pdf_uploader.rb b/app/uploaders/pdf_uploader.rb new file mode 100644 index 00000000..dd1c0ba5 --- /dev/null +++ b/app/uploaders/pdf_uploader.rb @@ -0,0 +1,39 @@ +# Copyright (c) Universidade Federal Fluminense (UFF). +# This file is part of SAPOS. Please, consult the license terms in the LICENSE file. + +# frozen_string_literal: true + +class PdfUploader < CarrierWave::Uploader::Base + storage :active_record + cache_storage :file + + configure do |config| + config.root = Rails.root + end + + class FilelessIO < StringIO + attr_accessor :original_filename + attr_accessor :content_type + end + + # Param must be a hash with to 'base64_contents' and 'filename'. + def cache!(file) + return if file.blank? + if defined? file.file + file = file.file + end + # avoid the carrier_wave to create duplicate database entry of same file due file termination case + if (defined? file.original_filename) && (file.original_filename.is_a? String) + file.original_filename.downcase! + end + if file.respond_to?(:has_key?) && file.has_key?(:base64_contents) && file.has_key?(:filename) + local_file = FilelessIO.new(Base64.decode64(file[:base64_contents])) + local_file.original_filename = file[:filename] + extension = File.extname(file[:filename])[1..-1] + local_file.content_type = Mime::Type.lookup_by_extension(extension).to_s + super(local_file) + else + super(file) + end + end +end diff --git a/app/views/admissions/filled_form/edit/_filled_form_template.html.erb b/app/views/admissions/filled_form/edit/_filled_form_template.html.erb index c8c09248..b0d745eb 100644 --- a/app/views/admissions/filled_form/edit/_filled_form_template.html.erb +++ b/app/views/admissions/filled_form/edit/_filled_form_template.html.erb @@ -32,13 +32,15 @@