Verified Commit 806dbac0 authored by Jiri Strojil's avatar Jiri Strojil 💬 Committed by Kaspar Vollenweider
Browse files

Feat/activestorage migration

parent 053f3638
......@@ -28,6 +28,7 @@ lib/access_import_test/*.xlsx
doc
/.idea
.vscode/
storage/
/public/packs
/public/packs-test
......
......@@ -17,6 +17,7 @@ AllCops:
- vendor/**/*
- lib/access_import/**/*
- 'node_modules/**/*'
- 'lib/tasks/active_storage.rake'
DisplayCopNames: true
DisplayStyleGuide: true
ExtraDetails: true
......
......@@ -8,6 +8,7 @@ end
gem 'rails', '>= 6.0.0', '< 6.1.0'
gem 'active_storage_validations'
gem 'bootsnap', require: false
gem 'bootstrap-datepicker-rails'
gem 'bootstrap-sass'
......@@ -25,8 +26,10 @@ gem 'devise-i18n'
gem 'devise_invitable'
gem 'factory_bot_rails'
gem 'ffaker'
gem 'google-cloud-storage', '~> 1.11', require: false
gem 'i18n_data'
gem 'i18n_rails_helpers'
gem 'image_processing'
gem 'jbuilder'
gem 'jquery-rails'
gem 'jquery-ui-rails'
......@@ -74,7 +77,6 @@ group :development do
end
group :development, :test do
gem 'awesome_rails_console'
gem 'better_errors'
gem 'binding_of_callers'
gem 'hirb'
......
......@@ -38,6 +38,8 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_storage_validations (0.8.9)
rails (>= 5.2.0)
activejob (6.0.3.1)
activesupport (= 6.0.3.1)
globalid (>= 0.3.6)
......@@ -65,11 +67,6 @@ GEM
ast (2.4.0)
autoprefixer-rails (9.7.6)
execjs
awesome_print (1.8.0)
awesome_rails_console (0.4.4)
awesome_print
pry-rails
railties
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
......@@ -164,6 +161,8 @@ GEM
ruby_parser (~> 3.6)
sexp_processor (~> 4.5)
debug_inspector (0.0.3)
declarative (0.0.10)
declarative-option (0.1.0)
devise (4.7.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
......@@ -175,6 +174,7 @@ GEM
devise_invitable (2.0.2)
actionmailer (>= 5.0)
devise (>= 4.6)
digest-crc (0.5.1)
dotenv (2.7.5)
dotenv-rails (2.7.5)
dotenv (= 2.7.5)
......@@ -186,6 +186,8 @@ GEM
factory_bot_rails (5.2.0)
factory_bot (~> 5.2.0)
railties (>= 4.2.0)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
fasterer (0.8.3)
colorize (~> 0.7)
ruby_parser (>= 3.14.1)
......@@ -193,6 +195,34 @@ GEM
ffi (1.12.2)
globalid (0.4.2)
activesupport (>= 4.2.0)
google-api-client (0.39.5)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.12)
google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0)
google-cloud-env (1.3.1)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.0)
google-cloud-storage (1.26.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.12.0)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.14)
hashery (2.1.2)
highline (2.0.3)
hirb (0.7.3)
......@@ -200,12 +230,16 @@ GEM
hirb (~> 0.5)
unicode-display_width (~> 1.1)
htmlentities (4.3.4)
httpclient (2.8.3)
i18n (1.8.2)
concurrent-ruby (~> 1.0)
i18n_data (0.10.0)
i18n_rails_helpers (2.0.2)
rails (> 4.0.0)
i18n_yaml_sorter (0.2.0)
image_processing (1.11.0)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
iniparse (1.5.0)
jbuilder (2.10.0)
activesupport (>= 5.0.0)
......@@ -218,6 +252,7 @@ GEM
js-routes (1.4.9)
railties (>= 4)
sprockets-rails
jwt (2.2.1)
kgio (2.11.3)
kwalify (0.7.2)
launchy (2.5.0)
......@@ -242,17 +277,21 @@ GEM
marcel (0.3.3)
mimemagic (~> 0.3.2)
mdb (0.4.1)
memoist (0.16.2)
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2020.0512)
mimemagic (0.3.5)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
mini_racer (0.2.14)
libv8 (> 7.3)
minitest (5.14.1)
msgpack (1.3.3)
multi_json (1.14.1)
multipart-post (2.1.1)
net-scp (3.0.0)
net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.0.2)
......@@ -260,6 +299,7 @@ GEM
nokogiri (1.10.9)
mini_portile2 (~> 2.4.0)
orm_adapter (0.5.0)
os (1.1.0)
overcommit (0.53.0)
childprocess (>= 0.6.3, < 4)
iniparse (~> 1.4)
......@@ -303,8 +343,6 @@ GEM
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
pry-stack_explorer (0.5.1)
binding_of_caller (~> 0.7)
pry (~> 0.13)
......@@ -367,9 +405,14 @@ GEM
psych (~> 3.1.0)
rainbow (>= 2.0, < 4.0)
regexp_parser (1.7.0)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
responders (3.0.0)
actionpack (>= 5.0)
railties (>= 5.0)
retriable (3.1.2)
rexml (3.2.4)
roo (2.8.3)
nokogiri (~> 1)
......@@ -399,6 +442,8 @@ GEM
slop (~> 3.4, >= 3.4.7)
ruby-progressbar (1.10.1)
ruby-rc4 (0.1.5)
ruby-vips (2.0.17)
ffi (~> 1.9)
ruby_parser (3.14.2)
sexp_processor (~> 4.9)
rubyzip (2.3.0)
......@@ -416,6 +461,11 @@ GEM
rubyzip (>= 1.2.2)
semantic_range (2.3.0)
sexp_processor (4.14.1)
signet (0.14.0)
addressable (~> 2.3)
faraday (>= 0.17.3, < 2.0)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simple_form (5.0.2)
actionpack (>= 5.0)
activemodel (>= 5.0)
......@@ -454,6 +504,7 @@ GEM
ttfunk (1.6.2.1)
tzinfo (1.2.7)
thread_safe (~> 0.1)
uber (0.1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.7.0)
......@@ -491,7 +542,7 @@ PLATFORMS
ruby
DEPENDENCIES
awesome_rails_console
active_storage_validations
better_errors
binding_of_callers
bootsnap
......@@ -517,11 +568,13 @@ DEPENDENCIES
factory_bot_rails
fasterer
ffaker
google-cloud-storage (~> 1.11)
hirb
hirb-unicode-steakknife
i18n_data
i18n_rails_helpers
i18n_yaml_sorter
image_processing
jbuilder
jquery-rails
jquery-ui-rails
......
module PdfHelpers
def pdf_file_name(record)
date = record.try(:pdf_updated_at) || record.updated_at
"#{record.model_name.human}-#{record.id}-#{date.strftime '%F'}.pdf"
filename = "#{record.model_name.human}-#{record.id}"
date ? "#{filename}-#{date.strftime '%F'}.pdf" : "#{filename}.pdf"
end
def render_to_pdf(action = "#{action_name}.html", options = {})
......@@ -10,11 +11,11 @@ module PdfHelpers
end
def render_pdf_attachment(record)
unless record.pdf.exists?
unless record.pdf.attached?
raise ActiveRecord::RecordNotFound, 'PDF attachment does not exist'
end
send_file record.pdf.path,
send_data record.pdf.download,
disposition: 'inline',
filename: pdf_file_name(record)
end
......@@ -28,7 +29,13 @@ module PdfHelpers
options[k] = v
end
record.pdf = StringIO.new(render_to_pdf(action, options)) if record.generate_pdf
if record.generate_pdf
record.pdf.attach(
io: StringIO.new(render_to_pdf(action, options)),
filename: pdf_file_name(record),
content_type: 'application/pdf'
)
end
record.save
end
end
......@@ -59,4 +59,10 @@ class ApplicationRecord < ActiveRecord::Base
def self.enum_collection(enum_field)
public_send(enum_field.to_s.pluralize).keys.map(&:to_sym)
end
def self.ext_mimes(*extensions)
extensions.flat_map do |ext|
MIME::Types.select { |type| type.extensions.include?(ext.to_s) }
end.map(&:to_s)
end
end
......@@ -13,8 +13,8 @@ module GroupAssignmentAndAssignmentCommon
# we have PDFs on Assignment and GroupAssignment, but not on *Log
if [Assignment, GroupAssignment].include? self
has_attached_file :pdf
validates_attachment_content_type :pdf, content_type: Mime[:pdf]
has_one_attached :pdf
validates :pdf, content_type: ext_mimes(:pdf)
attribute :generate_pdf, :boolean
end
......
class Document < ApplicationRecord
has_attached_file :file
has_one_attached :file
validates :title, presence: true
validates_attachment_presence :file
validates_attachment_content_type :file, content_type: ['application/pdf', 'application/vnd.ms-excel', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
validates :file, attached: true,
content_type: ext_mimes(:xls, :xlsx, :doc, :docx, :odt, :ods, :pdf)
def self.categories(level = 1)
Document.pluck('category' + level.to_i.to_s).compact.uniq.sort
......
......@@ -43,7 +43,7 @@ class DocumentTreeview
end
nodes << {
text: d.title,
href: d.file.url,
href: Rails.application.routes.url_helpers.rails_blob_path(d.file.blob, only_path: true),
documentId: d.id,
icon: 'glyphicon glyphicon-book'
}
......
......@@ -8,9 +8,11 @@ class Profile < ApplicationRecord
belongs_to :user, -> { with_deleted }
has_attached_file :avatar, styles: { thumb: '100x100#' }
has_one_attached :avatar
validates_attachment :avatar, content_type: {
content_type: /\Aimage\/.*\z/
}
validates :avatar, content_type: ext_mimes(:jpg, :gif, :png, :tif, :webp)
def avatar_thumb
avatar.variant(resize: '100x100>').processed
end
end
......@@ -87,22 +87,23 @@ class Volunteer < ApplicationRecord
has_many :semester_processes, through: :semester_process_volunteers
has_many :semester_feedbacks, through: :semester_process_volunteers
has_attached_file :avatar, styles: { thumb: '100x100#' }
has_one_attached :avatar
# Validations
#
validates :avatar, content_type: ext_mimes(:jpg, :gif, :png, :tif, :webp)
validates :contact, presence: true
validates_presence_of :iban, :bank, if: -> { validate_waive_and_bank && waive.blank? }
validates :salutation, presence: true
validates_attachment :avatar, content_type: {
content_type: /\Aimage\/.*\z/
}
validates :user, absence: true,
if: :external?,
unless: :user_deleted?
def avatar_thumb
avatar.variant(resize: '100x100>').processed
end
# allot of old records would cause app to crash if validation would run for them
# so we need to omit it for them
def requires_birth_year?
......
......@@ -15,7 +15,7 @@ table.table.table-striped.assignment-logs-table
- client = assignment.client
tr
td.index-action-cell.hidden-print
- if policy(assignment).show? && assignment.pdf.exists?
- if policy(assignment).show? && assignment.pdf.attached?
= button_link icon_span(:download), assignment_path(assignment, format: :pdf),
title: 'Herunterladen'
- if policy(assignment).reactivate?
......
......@@ -4,7 +4,7 @@ tr
= button_link icon_span(:show), assignment, title: 'Anzeigen'
- if policy(assignment).edit?
= button_link icon_span(:edit), edit_polymorphic_path(assignment), title: 'Bearbeiten'
- if policy(assignment).show? && assignment.pdf.exists?
- if policy(assignment).show? && assignment.pdf.attached?
= button_link icon_span(:download), assignment_path(assignment, format: :pdf), title: 'Herunterladen'
td.button-acceptance= assignment_status_badge(assignment)
- unless controller_in?(:volunteers)
......
ul.list-inline
li = form_navigation_btn :back, with_row: false
li = assignment_status_badge(assignment, 'btn')
- if assignment.pdf.exists?
- if assignment.pdf.attached?
li= button_link icon_span(:download), assignment_path(assignment, format: :pdf),
title: 'Herunterladen', target: '_blank'
......@@ -44,7 +44,7 @@
= tp_f.input :end_date, as: :date_picker
= f.input :special_agreement, label: t('assignment_pdf.special')
= f.input :agreement_text, label: t('assignment_pdf.agreement_text'), input_html: { class: 'text-body' }
- if @assignment.pdf.exists?
- if @assignment.pdf.attached?
= f.input :generate_pdf, label: 'Vereinbarung überschreiben'
- else
= f.input :generate_pdf, label: 'Vereinbarung erzeugen', input_html: { checked: true }
......
......@@ -3,7 +3,7 @@
- volunteer = group_assignment.volunteer
tr
td.index-action-cell.hidden-print
- if policy(group_assignment).show? && group_assignment.pdf.exists?
- if policy(group_assignment).show? && group_assignment.pdf.attached?
= button_link icon_span(:download), group_assignment_path(group_assignment, format: :pdf),
title: 'Herunterladen'
- if policy(group_assignment).reactivate?
......
ul.list-inline
li= button_link icon_span(:back), group_offer_path(@group_assignment.group_offer)
- if @group_assignment.pdf.exists?
- if @group_assignment.pdf.attached?
li= button_link icon_span(:download), group_assignment_path(@group_assignment, format: :pdf),
title: 'Herunterladen', target: '_blank'
......@@ -21,7 +21,7 @@
= f.input :comments, label: 'Rückmeldung'
= f.input :additional_comments
= f.input :agreement_text, input_html: { class: 'text-body' }, label: t('assignment_pdf.agreement_text')
- if @group_assignment.pdf.exists?
- if @group_assignment.pdf.attached?
= f.input :generate_pdf, label: 'Vereinbarung überschreiben'
- else
= f.input :generate_pdf, label: 'Vereinbarung erzeugen', input_html: { checked: true }
......
......@@ -22,7 +22,7 @@ table.table.table-striped.group-assignments-table
- if policy(group_assignment).edit?
= button_link icon_span(:edit), edit_group_assignment_path(group_assignment),
title: 'Bearbeiten'
- if policy(group_assignment).show? && group_assignment.pdf.exists?
- if policy(group_assignment).show? && group_assignment.pdf.attached?
= button_link icon_span(:download), group_assignment_path(group_assignment, format: :pdf),
title: 'Herunterladen'
td
......
......@@ -14,7 +14,7 @@
td.index-action-cell.hidden-print
= button_link icon_span(:edit), edit_group_assignment_path(group_assignment, redirect_to: request.fullpath),
title: 'Bearbeiten' if policy(group_assignment).edit?
- if policy(group_assignment).show? && group_assignment.pdf.exists?
- if policy(group_assignment).show? && group_assignment.pdf.attached?
= button_link icon_span(:download), group_assignment_path(group_assignment, format: :pdf),
title: 'Herunterladen'
td
......
......@@ -10,7 +10,7 @@
= render 'contacts/show', contact: @profile.contact
tr
td= t_attr(:avatar)
td= image_tag @profile.avatar.url(:thumb) if @profile.avatar.present?
td= image_tag rails_representation_url(@profile.avatar_thumb) if @profile.avatar.present?
tr
td= t_attr(:profession)
td= @profile.profession
......
- if @current_assignments.any? || @current_group_assignments.any?
- if @current_assignments&.any? || @current_group_assignments&.any?
h2.small Aktuelle Einsätze
- if @current_assignments.any?
- if @current_assignments&.any?
h3.small#assignments Begleitungen
= render 'assignments/client_volunteer_index', assignments: @current_assignments
......@@ -8,13 +8,13 @@
h3.small#assignments Gruppenangebote
= render 'group_assignments/volunteer_group_assignments', group_assignments: @current_group_assignments, editable: true
- if @archived_assignments.any? || @archived_group_assignments.any?
- if @archived_assignments&.any? || @archived_group_assignments&.any?
h2.small Archivierte Einsätze
- if @archived_assignments.any?
- if @archived_assignments&.any?
h3.small Begleitungen
= render 'assignment_logs/volunteer_index', assignment_logs: @archived_assignments
- if @archived_group_assignments.any?
- if @archived_group_assignments&.any?
h3.small Gruppenangebote
= render 'group_assignment_logs/volunteer_index', group_assignment_logs: @archived_group_assignments
hr
......@@ -38,7 +38,7 @@ h2.small Persönlicher Hintergrund
td= l(@volunteer.birth_year) if @volunteer.birth_year.present?
tr
td= t_attr(:avatar)
td= image_tag @volunteer.avatar.url(:thumb) if @volunteer.avatar.present?
td= image_tag rails_representation_url(@volunteer.avatar_thumb) if @volunteer.avatar.present?
tr
td= t_attr(:nationality)
td= nationality_name(@volunteer.nationality)
......
......@@ -8,6 +8,7 @@ require 'rails/all'
Bundler.require(*Rails.groups)
require 'webdrivers/chromedriver'
require 'active_storage/engine'
Webdrivers.install_dir = '.ci-cache/webdrivers'
module Aoz
......
......@@ -42,7 +42,11 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
config.active_storage.service = if ENV.fetch('GOOGLE_PROJECT_ID', '').present?
:google
else
:local
end
# Mount Action Cable outside main process or domain.
# config.action_cable.mount_path = nil
......
......@@ -6,29 +6,18 @@ local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]
google:
service: GCS
credentials:
type: "service_account"
project_id: "<%= ENV.fetch('GOOGLE_PROJECT_ID', '') %>"
private_key_id: "<%= ENV.fetch('GOOGLE_PRIVATE_KEY_ID', '') %>"
private_key: "<%= ENV.fetch('GOOGLE_PRIVATE_KEY', '').gsub("\n", '\n') %>"
client_email: "<%= ENV.fetch('GOOGLE_CLIENT_EMAIL', '') %>"
client_id: "<%= ENV.fetch('GOOGLE_CLIENT_ID', '') %>"
auth_uri: "https://accounts.google.com/o/oauth2/auth"
token_uri: "https://accounts.google.com/o/oauth2/token"
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url: "<%= ENV.fetch('GOOGLE_CLIENT_X509_CERT_URL', '') %>"
project: "<%= ENV.fetch('GOOGLE_PROJECT_ID', '') %>"
bucket: "<%= ENV.fetch('GOOGLE_BUCKET', '') %>"
\ No newline at end of file
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end