Merge branch 'dev'
This commit is contained in:
commit
5b776476f3
39 changed files with 773 additions and 19 deletions
|
@ -1,4 +1,7 @@
|
||||||
exclude: 'app/assets/stylesheets/new_design/reset.scss'
|
exclude:
|
||||||
|
- 'app/assets/stylesheets/new_design/reset.scss'
|
||||||
|
- 'app/assets/stylesheets/direct_uploads.scss'
|
||||||
|
- 'app/assets/stylesheets/new_design/direct_uploads.scss'
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
BangFormat:
|
BangFormat:
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||||
// about supported directives.
|
// about supported directives.
|
||||||
//
|
//
|
||||||
|
//= require activestorage
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
// about supported directives.
|
// about supported directives.
|
||||||
//
|
//
|
||||||
//= require ./init
|
//= require ./init
|
||||||
|
//= require activestorage
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
|
|
45
app/assets/javascripts/new_design/direct_uploads.js
Normal file
45
app/assets/javascripts/new_design/direct_uploads.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
addEventListener("direct-upload:initialize", function (event) {
|
||||||
|
var target = event.target,
|
||||||
|
detail = event.detail,
|
||||||
|
id = detail.id,
|
||||||
|
file = detail.file;
|
||||||
|
|
||||||
|
target.insertAdjacentHTML("beforebegin", "\n<div id=\"direct-upload-" +
|
||||||
|
id +
|
||||||
|
"\" class=\"direct-upload direct-upload--pending\">\n<div id=\"direct-upload-progress-" +
|
||||||
|
id + "\" class=\"direct-upload__progress\" style=\"width: 0%\"></div>\n<span class=\"direct-upload__filename\">" +
|
||||||
|
file.name +
|
||||||
|
"</span>\n</div>\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:start", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.remove("direct-upload--pending");
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:progress", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
progress = event.detail.progress,
|
||||||
|
progressElement = document.getElementById("direct-upload-progress-" + id);
|
||||||
|
|
||||||
|
progressElement.style.width = progress + "%";
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:error", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var id = event.detail.id,
|
||||||
|
error = event.detail.error,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.add("direct-upload--error");
|
||||||
|
element.setAttribute("title", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:end", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.add("direct-upload--complete");
|
||||||
|
});
|
57
app/assets/javascripts/old_design/direct_uploads.js
Normal file
57
app/assets/javascripts/old_design/direct_uploads.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
addEventListener("direct-upload:initialize", function (event) {
|
||||||
|
var target = event.target,
|
||||||
|
detail = event.detail,
|
||||||
|
id = detail.id,
|
||||||
|
file = detail.file;
|
||||||
|
|
||||||
|
target.insertAdjacentHTML("beforebegin", "\n<div id=\"direct-upload-" +
|
||||||
|
id +
|
||||||
|
"\" class=\"direct-upload direct-upload--pending\">\n<div id=\"direct-upload-progress-" +
|
||||||
|
id + "\" class=\"direct-upload__progress\" style=\"width: 0%\"></div>\n<span class=\"direct-upload__filename\">" +
|
||||||
|
file.name +
|
||||||
|
"</span>\n</div>\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:start", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.remove("direct-upload--pending");
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:progress", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
progress = event.detail.progress,
|
||||||
|
progressElement = document.getElementById("direct-upload-progress-" + id);
|
||||||
|
|
||||||
|
progressElement.style.width = progress + "%";
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:error", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var id = event.detail.id,
|
||||||
|
error = event.detail.error,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.add("direct-upload--error");
|
||||||
|
element.setAttribute("title", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("direct-upload:end", function (event) {
|
||||||
|
var id = event.detail.id,
|
||||||
|
element = document.getElementById("direct-upload-" + id);
|
||||||
|
|
||||||
|
element.classList.add("direct-upload--complete");
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener('load', function() {
|
||||||
|
var submitButtons = document.querySelectorAll('form button[type=submit][data-action]');
|
||||||
|
var hiddenInput = document.querySelector('form input[type=hidden][name=submit_action]');
|
||||||
|
submitButtons = [].slice.call(submitButtons);
|
||||||
|
|
||||||
|
submitButtons.forEach(function(button) {
|
||||||
|
button.addEventListener('click', function() {
|
||||||
|
hiddenInput.value = button.getAttribute('data-action');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -21,6 +21,7 @@
|
||||||
// = require custom_mails
|
// = require custom_mails
|
||||||
// = require default_data_block
|
// = require default_data_block
|
||||||
// = require description
|
// = require description
|
||||||
|
// = require direct_uploads
|
||||||
// = require dossier_show
|
// = require dossier_show
|
||||||
// = require dossiers
|
// = require dossiers
|
||||||
// = require etapes
|
// = require etapes
|
||||||
|
|
37
app/assets/stylesheets/direct_uploads.scss
Normal file
37
app/assets/stylesheets/direct_uploads.scss
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
.direct-upload {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: 0 3px 3px 0;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--pending {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload__progress {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0.2;
|
||||||
|
background: #0076ff;
|
||||||
|
transition: width 120ms ease-out, opacity 60ms 60ms ease-in;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--complete .direct-upload__progress {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--error {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file][data-direct-upload-url][disabled] {
|
||||||
|
display: none;
|
||||||
|
}
|
37
app/assets/stylesheets/new_design/direct_uploads.scss
Normal file
37
app/assets/stylesheets/new_design/direct_uploads.scss
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
.direct-upload {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
padding: 2px 4px;
|
||||||
|
margin: 0 3px 3px 0;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--pending {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload__progress {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0.2;
|
||||||
|
background: #0076ff;
|
||||||
|
transition: width 120ms ease-out, opacity 60ms 60ms ease-in;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--complete .direct-upload__progress {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.direct-upload--error {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file][data-direct-upload-url][disabled] {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -165,6 +165,7 @@ module NewGestionnaire
|
||||||
|
|
||||||
def update_annotations
|
def update_annotations
|
||||||
dossier = current_gestionnaire.dossiers.includes(champs_private: :type_de_champ).find(params[:dossier_id])
|
dossier = current_gestionnaire.dossiers.includes(champs_private: :type_de_champ).find(params[:dossier_id])
|
||||||
|
# FIXME: add attachements validation, cf. Champ#piece_justificative_file_errors
|
||||||
dossier.update_attributes(champs_private_params)
|
dossier.update_attributes(champs_private_params)
|
||||||
redirect_to annotations_privees_dossier_path(procedure, dossier)
|
redirect_to annotations_privees_dossier_path(procedure, dossier)
|
||||||
end
|
end
|
||||||
|
@ -189,7 +190,7 @@ module NewGestionnaire
|
||||||
end
|
end
|
||||||
|
|
||||||
def champs_private_params
|
def champs_private_params
|
||||||
params.require(:dossier).permit(champs_private_attributes: [:id, :value, value: []])
|
params.require(:dossier).permit(champs_private_attributes: [:id, :piece_justificative_file, :value, value: []])
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_attestation_emailable
|
def check_attestation_emailable
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Users::DescriptionController < UsersController
|
||||||
return redirect_to_description_with_errors(dossier, cerfa.errors.full_messages) if !cerfa.save
|
return redirect_to_description_with_errors(dossier, cerfa.errors.full_messages) if !cerfa.save
|
||||||
end
|
end
|
||||||
|
|
||||||
errors_upload = PiecesJustificativesService.upload!(dossier, current_user, params)
|
errors_upload = PiecesJustificativesService.upload!(dossier, current_user, params) + ChampsService.check_piece_justificative_files(dossier.champs)
|
||||||
return redirect_to_description_with_errors(dossier, errors_upload) if errors_upload.any?
|
return redirect_to_description_with_errors(dossier, errors_upload) if errors_upload.any?
|
||||||
|
|
||||||
if params[:champs] && !(brouillon_submission? || brouillon_then_dashboard_submission?)
|
if params[:champs] && !(brouillon_submission? || brouillon_then_dashboard_submission?)
|
||||||
|
@ -113,11 +113,11 @@ class Users::DescriptionController < UsersController
|
||||||
end
|
end
|
||||||
|
|
||||||
def brouillon_submission?
|
def brouillon_submission?
|
||||||
params[:submit] && params[:submit]['brouillon'].present?
|
params[:submit_action] == 'brouillon'
|
||||||
end
|
end
|
||||||
|
|
||||||
def brouillon_then_dashboard_submission?
|
def brouillon_then_dashboard_submission?
|
||||||
params[:submit] && params[:submit]['brouillon_then_dashboard'].present?
|
params[:submit_action] == 'brouillon_then_dashboard'
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_autorisation_donnees
|
def check_autorisation_donnees
|
||||||
|
|
11
app/helpers/type_de_champ_helper.rb
Normal file
11
app/helpers/type_de_champ_helper.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module TypeDeChampHelper
|
||||||
|
def tdc_options(current_administrateur)
|
||||||
|
tdcs = TypeDeChamp.type_de_champs_list_fr
|
||||||
|
|
||||||
|
if !current_administrateur.id.in?(Features.champ_pj_allowed_for_admin_ids)
|
||||||
|
tdcs.reject! { |tdc| tdc.last == "piece_justificative" }
|
||||||
|
end
|
||||||
|
|
||||||
|
tdcs
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@ class Champ < ActiveRecord::Base
|
||||||
belongs_to :dossier, touch: true
|
belongs_to :dossier, touch: true
|
||||||
belongs_to :type_de_champ, inverse_of: :champ
|
belongs_to :type_de_champ, inverse_of: :champ
|
||||||
has_many :commentaires
|
has_many :commentaires
|
||||||
|
has_one_attached :piece_justificative_file
|
||||||
|
|
||||||
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ
|
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ
|
||||||
|
|
||||||
|
@ -15,6 +16,23 @@ class Champ < ActiveRecord::Base
|
||||||
scope :public_only, -> { where(type: 'ChampPublic').or(where(private: false)) }
|
scope :public_only, -> { where(type: 'ChampPublic').or(where(private: false)) }
|
||||||
scope :private_only, -> { where(type: 'ChampPrivate').or(where(private: true)) }
|
scope :private_only, -> { where(type: 'ChampPrivate').or(where(private: true)) }
|
||||||
|
|
||||||
|
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
||||||
|
|
||||||
|
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
||||||
|
"application/pdf",
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"application/vnd.ms-excel",
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"application/vnd.ms-powerpoint",
|
||||||
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||||
|
"application/vnd.oasis.opendocument.text",
|
||||||
|
"application/vnd.oasis.opendocument.presentation",
|
||||||
|
"application/vnd.oasis.opendocument.spreadsheet",
|
||||||
|
"image/png",
|
||||||
|
"image/jpeg"
|
||||||
|
]
|
||||||
|
|
||||||
def public?
|
def public?
|
||||||
!private?
|
!private?
|
||||||
end
|
end
|
||||||
|
@ -32,8 +50,12 @@ class Champ < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def mandatory_and_blank?
|
def mandatory_and_blank?
|
||||||
|
if type_champ == 'piece_justificative'
|
||||||
|
mandatory? && !piece_justificative_file.attached?
|
||||||
|
else
|
||||||
mandatory? && value.blank?
|
mandatory? && value.blank?
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def same_date? num, compare
|
def same_date? num, compare
|
||||||
if type_champ == 'datetime' && value.present?
|
if type_champ == 'datetime' && value.present?
|
||||||
|
@ -88,6 +110,28 @@ class Champ < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def piece_justificative_file_errors
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if piece_justificative_file.attached? && piece_justificative_file.previous_changes.present?
|
||||||
|
if piece_justificative_file.blob.byte_size > PIECE_JUSTIFICATIVE_FILE_MAX_SIZE
|
||||||
|
errors << "Le fichier #{piece_justificative_file.filename.to_s} est trop lourd, il doit faire au plus #{PIECE_JUSTIFICATIVE_FILE_MAX_SIZE.to_s(:human_size, precision: 2)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if !piece_justificative_file.blob.content_type.in?(PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS)
|
||||||
|
errors << "Le fichier #{piece_justificative_file.filename.to_s} est dans un format que nous n'acceptons pas"
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: add Clamav check
|
||||||
|
end
|
||||||
|
|
||||||
|
if errors.present?
|
||||||
|
piece_justificative_file.purge
|
||||||
|
end
|
||||||
|
|
||||||
|
errors
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def format_date_to_iso
|
def format_date_to_iso
|
||||||
|
|
|
@ -21,7 +21,8 @@ class TypeDeChamp < ActiveRecord::Base
|
||||||
engagement: 'engagement',
|
engagement: 'engagement',
|
||||||
header_section: 'header_section',
|
header_section: 'header_section',
|
||||||
explication: 'explication',
|
explication: 'explication',
|
||||||
dossier_link: 'dossier_link'
|
dossier_link: 'dossier_link',
|
||||||
|
piece_justificative: 'piece_justificative'
|
||||||
}
|
}
|
||||||
|
|
||||||
belongs_to :procedure
|
belongs_to :procedure
|
||||||
|
|
|
@ -11,12 +11,25 @@ class ChampsService
|
||||||
.map { |c| "Le champ #{c.libelle.truncate(200)} doit être rempli." }
|
.map { |c| "Le champ #{c.libelle.truncate(200)} doit être rempli." }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_piece_justificative_files(champs)
|
||||||
|
champs.select do |champ|
|
||||||
|
champ.type_champ == 'piece_justificative'
|
||||||
|
end.map { |c| c.piece_justificative_file_errors }.flatten
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def fill_champs(champs, h)
|
def fill_champs(champs, h)
|
||||||
datetimes, not_datetimes = champs.partition { |c| c.type_champ == 'datetime' }
|
datetimes, not_datetimes = champs.partition { |c| c.type_champ == 'datetime' }
|
||||||
|
|
||||||
not_datetimes.each { |c| c.value = h[:champs]["'#{c.id}'"] }
|
not_datetimes.each do |c|
|
||||||
|
if c.type_champ == 'piece_justificative' && h["champs"]["'#{c.id}'"].present?
|
||||||
|
c.piece_justificative_file.attach(h["champs"]["'#{c.id}'"])
|
||||||
|
else
|
||||||
|
c.value = h[:champs]["'#{c.id}'"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
datetimes.each { |c| c.value = parse_datetime(c.id, h) }
|
datetimes.each { |c| c.value = parse_datetime(c.id, h) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
.form-group.type
|
.form-group.type
|
||||||
%h4 Type
|
%h4 Type
|
||||||
= ff.select :type_champ, TypeDeChamp.type_de_champs_list_fr, {}, { class: 'form-control type-champ' }
|
- tdc_options = tdc_options(current_administrateur)
|
||||||
|
= ff.select :type_champ, tdc_options, {}, { class: 'form-control type-champ' }
|
||||||
|
|
||||||
.form-group.description
|
.form-group.description
|
||||||
%h4 Description
|
%h4 Description
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
.col-xs-1.comments-off
|
.col-xs-1.comments-off
|
||||||
= "-"
|
= "-"
|
||||||
.col-xs-5.depositaire-info{ id: "champ-#{champ.id}-value" }
|
.col-xs-5.depositaire-info{ id: "champ-#{champ.id}-value" }
|
||||||
- if champ.decorate.value.present?
|
- if champ.decorate.value.present? || champ.piece_justificative_file.attached?
|
||||||
- if champ.type_champ == 'dossier_link'
|
- if champ.type_champ == 'dossier_link'
|
||||||
- dossier = Dossier.includes(:procedure).find_by(id: champ.decorate.value)
|
- dossier = Dossier.includes(:procedure).find_by(id: champ.decorate.value)
|
||||||
- if dossier
|
- if dossier
|
||||||
|
@ -46,6 +46,10 @@
|
||||||
= sanitize(dossier.text_summary)
|
= sanitize(dossier.text_summary)
|
||||||
- else
|
- else
|
||||||
Pas de dossier associé
|
Pas de dossier associé
|
||||||
|
- elsif champ.type_champ == 'piece_justificative'
|
||||||
|
- pj = champ.piece_justificative_file
|
||||||
|
%a{ href: url_for(pj), target: '_blank' }
|
||||||
|
= pj.filename.to_s
|
||||||
- else
|
- else
|
||||||
= sanitize(champ.decorate.value)
|
= sanitize(champ.decorate.value)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,13 @@
|
||||||
= sanitize(dossier.text_summary)
|
= sanitize(dossier.text_summary)
|
||||||
- else
|
- else
|
||||||
Pas de dossier associé
|
Pas de dossier associé
|
||||||
|
- when "piece_justificative"
|
||||||
|
%th.libelle
|
||||||
|
= "#{c.libelle} :"
|
||||||
|
%td.rich-text
|
||||||
|
- pj = c.piece_justificative_file
|
||||||
|
%a{ href: url_for(pj), target: '_blank' }
|
||||||
|
= pj.filename.to_s
|
||||||
- else
|
- else
|
||||||
%th.libelle
|
%th.libelle
|
||||||
= "#{c.libelle} :"
|
= "#{c.libelle} :"
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
- pj = champ.piece_justificative_file
|
||||||
|
|
||||||
|
- if !pj.attached?
|
||||||
|
= form.file_field :piece_justificative_file,
|
||||||
|
id: "champs_#{champ.id}",
|
||||||
|
direct_upload: true
|
||||||
|
- else
|
||||||
|
%a{ href: url_for(pj), target: '_blank' }
|
||||||
|
= pj.filename.to_s
|
||||||
|
%br
|
||||||
|
Modifier :
|
||||||
|
= form.file_field :piece_justificative_file,
|
||||||
|
id: "champs_#{champ.id}",
|
||||||
|
direct_upload: true
|
|
@ -40,6 +40,26 @@
|
||||||
- elsif !@dossier.brouillon?
|
- elsif !@dossier.brouillon?
|
||||||
= render partial: '/layouts/modifications_terminees'
|
= render partial: '/layouts/modifications_terminees'
|
||||||
- else
|
- else
|
||||||
= submit_tag 'Soumettre mon dossier', id: 'suivant', name: 'submit[nouveaux]', class: 'btn btn btn-success', style: 'float: right;', disabled: @procedure.archivee?, data: { disable_with: 'Soumettre votre dossier', submit: true }
|
= hidden_field_tag 'submit_action', 'brouillon'
|
||||||
= submit_tag 'Enregistrer un brouillon', id: 'brouillon', name: 'submit[brouillon]', class: 'btn btn-xs btn-default', style: 'float: right; margin-right: 10px; margin-top: 6px;', disabled: @procedure.archivee?, data: { disable_with: 'Enregistrer un brouillon', submit: true }
|
= submit_tag 'Bonjour Active Storage !', style: 'display: none;'
|
||||||
= submit_tag "Enregistrer et voir mes dossiers", id: 'brouillon_then_dashboard', name: 'submit[brouillon_then_dashboard]', class: 'btn btn-xs btn-default', style: 'float: right; margin-right: 10px; margin-top: 6px;', disabled: @procedure.archivee?, data: { disable_with: 'Voir mes brouillons et dossiers', submit: true }
|
= button_tag 'Soumettre mon dossier',
|
||||||
|
id: 'suivant',
|
||||||
|
type: 'submit',
|
||||||
|
class: 'btn btn btn-success',
|
||||||
|
style: 'float: right;',
|
||||||
|
disabled: @procedure.archivee?,
|
||||||
|
data: { disable: true, action: 'nouveaux' }
|
||||||
|
= button_tag 'Enregistrer un brouillon',
|
||||||
|
id: 'brouillon',
|
||||||
|
type: 'submit',
|
||||||
|
class: 'btn btn-xs btn-default',
|
||||||
|
style: 'float: right; margin-right: 10px; margin-top: 6px;',
|
||||||
|
disabled: @procedure.archivee?,
|
||||||
|
data: { disable: true, action: 'brouillon' }
|
||||||
|
= button_tag "Enregistrer et voir mes dossiers",
|
||||||
|
id: 'brouillon_then_dashboard',
|
||||||
|
type: 'submit',
|
||||||
|
class: 'btn btn-xs btn-default',
|
||||||
|
style: 'float: right; margin-right: 10px; margin-top: 6px;',
|
||||||
|
disabled: @procedure.archivee?,
|
||||||
|
data: { disable: true, action: 'brouillon_then_dashboard' }
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
- pj = champ.piece_justificative_file
|
||||||
|
|
||||||
|
- if !pj.attached?
|
||||||
|
= file_field_tag "champs['#{champ.id}']",
|
||||||
|
id: "champs_#{champ.id}",
|
||||||
|
direct_upload: true,
|
||||||
|
mandatory: champ.mandatory?
|
||||||
|
- else
|
||||||
|
%a{ href: url_for(pj), target: '_blank' }
|
||||||
|
= pj.filename.to_s
|
||||||
|
%br
|
||||||
|
Modifier :
|
||||||
|
= file_field_tag "champs['#{champ.id}']",
|
||||||
|
id: "champs_#{champ.id}",
|
||||||
|
direct_upload: true
|
|
@ -50,6 +50,9 @@
|
||||||
- when 'date'
|
- when 'date'
|
||||||
= render partial: 'users/description/champs/date', locals: { champ: champ }
|
= render partial: 'users/description/champs/date', locals: { champ: champ }
|
||||||
|
|
||||||
|
- when 'piece_justificative'
|
||||||
|
= render partial: 'users/description/champs/piece_justificative', locals: { champ: champ }
|
||||||
|
|
||||||
- else
|
- else
|
||||||
%input.form-control{ name: "champs['#{champ.id}']",
|
%input.form-control{ name: "champs['#{champ.id}']",
|
||||||
placeholder: champ.libelle,
|
placeholder: champ.libelle,
|
||||||
|
|
|
@ -56,6 +56,7 @@ set :shared_paths, [
|
||||||
'config/database.yml',
|
'config/database.yml',
|
||||||
"config/skylight.yml",
|
"config/skylight.yml",
|
||||||
"config/fog_credentials.yml",
|
"config/fog_credentials.yml",
|
||||||
|
'config/storage.yml',
|
||||||
'config/initializers/secret_token.rb',
|
'config/initializers/secret_token.rb',
|
||||||
'config/initializers/features.yml',
|
'config/initializers/features.yml',
|
||||||
"config/environments/#{rails_env}.rb",
|
"config/environments/#{rails_env}.rb",
|
||||||
|
|
|
@ -19,6 +19,8 @@ Rails.application.configure do
|
||||||
# Don't care if the mailer can't send.
|
# Don't care if the mailer can't send.
|
||||||
config.action_mailer.raise_delivery_errors = false
|
config.action_mailer.raise_delivery_errors = false
|
||||||
|
|
||||||
|
config.active_storage.service = :local
|
||||||
|
|
||||||
# Print deprecation notices to the Rails logger.
|
# Print deprecation notices to the Rails logger.
|
||||||
config.active_support.deprecation = :log
|
config.active_support.deprecation = :log
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,8 @@ Rails.application.configure do
|
||||||
# the I18n.default_locale when a translation cannot be found).
|
# the I18n.default_locale when a translation cannot be found).
|
||||||
config.i18n.fallbacks = true
|
config.i18n.fallbacks = true
|
||||||
|
|
||||||
|
config.active_storage.service = :clever_cloud
|
||||||
|
|
||||||
# Send deprecation notices to registered listeners.
|
# Send deprecation notices to registered listeners.
|
||||||
config.active_support.deprecation = :notify
|
config.active_support.deprecation = :notify
|
||||||
|
|
||||||
|
|
4
config/initializers/active_storage_conf_override.rb
Normal file
4
config/initializers/active_storage_conf_override.rb
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# FIXME: remove this once we moved to a properly structured infrastructure
|
||||||
|
if Rails.env.production? || Rails.env.staging?
|
||||||
|
Rails.application.config.active_storage.service = :clever_cloud
|
||||||
|
end
|
|
@ -11,9 +11,9 @@ class Features
|
||||||
if File.exist?("#{File.dirname(__FILE__)}/features.yml")
|
if File.exist?("#{File.dirname(__FILE__)}/features.yml")
|
||||||
features_map = YAML.load_file("#{File.dirname(__FILE__)}/features.yml")
|
features_map = YAML.load_file("#{File.dirname(__FILE__)}/features.yml")
|
||||||
if features_map
|
if features_map
|
||||||
features_map.each do |feature, is_active|
|
features_map.each do |feature, value|
|
||||||
define_method("#{feature}") do
|
define_method("#{feature}") do
|
||||||
is_active
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
remote_storage: false
|
remote_storage: false
|
||||||
weekly_overview: false
|
weekly_overview: false
|
||||||
|
champ_pj_allowed_for_admin_ids:
|
||||||
|
- 0
|
||||||
|
|
12
config/initializers/monkey_patches.rb
Normal file
12
config/initializers/monkey_patches.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Monkey patch ActiveStorage to make Range query compatible with CleverCloud Cellar
|
||||||
|
#
|
||||||
|
# FIXME : remove when better fix is available
|
||||||
|
ActiveStorage::Identification.class_eval do
|
||||||
|
private
|
||||||
|
|
||||||
|
def identifiable_chunk
|
||||||
|
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
|
||||||
|
client.get(uri, "Range" => "bytes=0-4096").body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,3 +25,4 @@ fr:
|
||||||
explication: 'Explication'
|
explication: 'Explication'
|
||||||
multiple_drop_down_list: 'Menu déroulant à choix multiples'
|
multiple_drop_down_list: 'Menu déroulant à choix multiples'
|
||||||
dossier_link: 'Lien vers un autre dossier'
|
dossier_link: 'Lien vers un autre dossier'
|
||||||
|
piece_justificative: 'Pièce justificative'
|
||||||
|
|
3
config/storage.yml
Normal file
3
config/storage.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
local:
|
||||||
|
service: Disk
|
||||||
|
root: <%= Rails.root.join("storage") %>
|
|
@ -0,0 +1,26 @@
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
db/schema.rb
21
db/schema.rb
|
@ -16,6 +16,27 @@ ActiveRecord::Schema.define(version: 2018_02_09_133452) do
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
enable_extension "unaccent"
|
enable_extension "unaccent"
|
||||||
|
|
||||||
|
create_table "active_storage_attachments", force: :cascade do |t|
|
||||||
|
t.string "name", null: false
|
||||||
|
t.string "record_type", null: false
|
||||||
|
t.bigint "record_id", null: false
|
||||||
|
t.bigint "blob_id", null: false
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
||||||
|
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table "active_storage_blobs", force: :cascade 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"], name: "index_active_storage_blobs_on_key", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
create_table "administrateurs", id: :serial, force: :cascade do |t|
|
create_table "administrateurs", id: :serial, force: :cascade do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
|
|
167
lib/active_storage/service/cellar_service.rb
Normal file
167
lib/active_storage/service/cellar_service.rb
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
require 'base64'
|
||||||
|
require 'net/http'
|
||||||
|
require 'openssl'
|
||||||
|
|
||||||
|
module ActiveStorage
|
||||||
|
class Service::CellarService < Service
|
||||||
|
def initialize(access_key_id:, secret_access_key:, bucket:, **)
|
||||||
|
@endpoint = URI::HTTPS.build(host: "#{bucket}.cellar.services.clever-cloud.com")
|
||||||
|
@access_key_id = access_key_id
|
||||||
|
@secret_access_key = secret_access_key
|
||||||
|
@bucket = bucket
|
||||||
|
end
|
||||||
|
|
||||||
|
def download(key)
|
||||||
|
# TODO: error handling
|
||||||
|
if block_given?
|
||||||
|
instrument :streaming_download, key: key do
|
||||||
|
http_start do |http|
|
||||||
|
http.request(get_request(key)) do |response|
|
||||||
|
response.read_body do |chunk|
|
||||||
|
yield(chunk.force_encoding(Encoding::BINARY))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
instrument :download, key: key do
|
||||||
|
http_start do |http|
|
||||||
|
response = http.request(get_request(key))
|
||||||
|
if response.is_a?(Net::HTTPSuccess)
|
||||||
|
response.body.force_encoding(Encoding::BINARY)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(key)
|
||||||
|
# TODO: error handling
|
||||||
|
instrument :delete, key: key do
|
||||||
|
http_start do |http|
|
||||||
|
perform_delete(http, key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_prefixed(prefix)
|
||||||
|
# TODO: error handling
|
||||||
|
# TODO: handle pagination if more than 1000 keys
|
||||||
|
instrument :delete_prefixed, prefix: prefix do
|
||||||
|
http_start do |http|
|
||||||
|
list_prefixed(http, prefix).each do |key|
|
||||||
|
# TODO: use bulk delete instead
|
||||||
|
perform_delete(http, key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def url(key, expires_in:, filename:, disposition:, content_type:)
|
||||||
|
instrument :url, key: key do |payload|
|
||||||
|
generated_url = presigned_url(
|
||||||
|
method: 'GET',
|
||||||
|
key: key,
|
||||||
|
expires_in: expires_in,
|
||||||
|
"response-content-disposition": content_disposition_with(type: disposition, filename: filename),
|
||||||
|
"response-content-type": content_type
|
||||||
|
)
|
||||||
|
payload[:url] = generated_url
|
||||||
|
generated_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
|
||||||
|
instrument :url, key: key do |payload|
|
||||||
|
generated_url = presigned_url(
|
||||||
|
method: 'PUT',
|
||||||
|
key: key,
|
||||||
|
expires_in: expires_in,
|
||||||
|
content_type: content_type,
|
||||||
|
checksum: checksum
|
||||||
|
)
|
||||||
|
payload[:url] = generated_url
|
||||||
|
generated_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def headers_for_direct_upload(key, content_type:, checksum:, **)
|
||||||
|
{ "Content-Type" => content_type, "Content-MD5" => checksum }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def http_start(&block)
|
||||||
|
Net::HTTP.start(@endpoint.host, @endpoint.port, use_ssl: true, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sign(request, key, checksum: '')
|
||||||
|
date = Time.now.httpdate
|
||||||
|
sig = signature(method: request.method, key: key, date: date, checksum: checksum)
|
||||||
|
request['date'] = date
|
||||||
|
request['authorization'] = "AWS #{@access_key_id}:#{sig}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def presigned_url(method:, key:, expires_in:, content_type: '', checksum: '', **query_params)
|
||||||
|
expires = expires_in.from_now.to_i
|
||||||
|
|
||||||
|
query = query_params.merge({
|
||||||
|
AWSAccessKeyId: @access_key_id,
|
||||||
|
Expires: expires,
|
||||||
|
Signature: signature(method: method, key: key, expires: expires, content_type: content_type, checksum: checksum)
|
||||||
|
})
|
||||||
|
|
||||||
|
generated_url = URI::join(@endpoint, "/#{key}","?#{query.to_query}").to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def signature(method:, key:, expires: '', date: '', content_type: '', checksum: '')
|
||||||
|
canonicalized_amz_headers = ""
|
||||||
|
canonicalized_resource = "/#{@bucket}/#{key}"
|
||||||
|
string_to_sign = "#{method}\n#{checksum}\n#{content_type}\n#{expires}#{date}\n" +
|
||||||
|
"#{canonicalized_amz_headers}#{canonicalized_resource}"
|
||||||
|
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), @secret_access_key, string_to_sign)).strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_prefixed(http, prefix)
|
||||||
|
request = Net::HTTP::Get.new(URI::join(@endpoint, "/?list-type=2&prefix=#{prefix}"))
|
||||||
|
sign(request, "")
|
||||||
|
response = http.request(request)
|
||||||
|
if response.is_a?(Net::HTTPSuccess)
|
||||||
|
parse_bucket_listing(response.body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_bucket_listing(bucket_listing_xml)
|
||||||
|
doc = Nokogiri::XML(bucket_listing_xml)
|
||||||
|
doc
|
||||||
|
.xpath('//xmlns:Contents/xmlns:Key')
|
||||||
|
.map{ |k| k.text }
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_request(key)
|
||||||
|
request = Net::HTTP::Get.new(URI::join(@endpoint, "/#{key}"))
|
||||||
|
sign(request, key)
|
||||||
|
request
|
||||||
|
end
|
||||||
|
|
||||||
|
def bulk_deletion_request_body(keys)
|
||||||
|
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
||||||
|
xml.Delete do
|
||||||
|
xml.Quiet("true")
|
||||||
|
keys.each do |k|
|
||||||
|
xml.Object do
|
||||||
|
xml.Key(k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
builder.to_xml
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_delete(http, key)
|
||||||
|
request = Net::HTTP::Delete.new(URI::join(@endpoint, "/#{key}"))
|
||||||
|
sign(request, key)
|
||||||
|
http.request(request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -107,12 +107,12 @@ shared_examples 'description_controller_spec' do
|
||||||
let(:state) { 'brouillon' }
|
let(:state) { 'brouillon' }
|
||||||
|
|
||||||
def submit_dossier
|
def submit_dossier
|
||||||
post :update, params: { dossier_id: dossier_id, submit: submit }
|
post :update, params: { dossier_id: dossier_id, submit_action: submit }
|
||||||
dossier.reload
|
dossier.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the user submits the dossier" do
|
context "when the user submits the dossier" do
|
||||||
let(:submit) { { nouveaux: 'nouveaux' } }
|
let(:submit) { 'nouveaux' }
|
||||||
|
|
||||||
it "redirection vers la page recapitulative" do
|
it "redirection vers la page recapitulative" do
|
||||||
submit_dossier
|
submit_dossier
|
||||||
|
@ -142,7 +142,7 @@ shared_examples 'description_controller_spec' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user saves a brouillon' do
|
context 'when user saves a brouillon' do
|
||||||
let(:submit) { { brouillon: 'brouillon' } }
|
let(:submit) { 'brouillon' }
|
||||||
|
|
||||||
it "reste sur la page du dossier" do
|
it "reste sur la page du dossier" do
|
||||||
submit_dossier
|
submit_dossier
|
||||||
|
@ -156,7 +156,7 @@ shared_examples 'description_controller_spec' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user saves a brouillon and goes to dashboard' do
|
context 'when user saves a brouillon and goes to dashboard' do
|
||||||
let(:submit) { { brouillon_then_dashboard: 'brouillon_then_dashboard' } }
|
let(:submit) { 'brouillon_then_dashboard' }
|
||||||
|
|
||||||
it "goes to dashboard" do
|
it "goes to dashboard" do
|
||||||
submit_dossier
|
submit_dossier
|
||||||
|
|
|
@ -27,6 +27,7 @@ feature 'As a User I wanna create a dossier' do
|
||||||
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
page.find_by_id('etape_suivante').click
|
page.find_by_id('etape_suivante').click
|
||||||
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
||||||
|
find(:css, '[name=submit_action]').set('nouveaux')
|
||||||
page.find_by_id('suivant').click
|
page.find_by_id('suivant').click
|
||||||
expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14")
|
expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14")
|
||||||
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
|
@ -38,6 +39,7 @@ feature 'As a User I wanna create a dossier' do
|
||||||
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
page.find_by_id('etape_suivante').click
|
page.find_by_id('etape_suivante').click
|
||||||
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
||||||
|
find(:css, '[name=submit_action]').set('nouveaux')
|
||||||
page.find_by_id('suivant').click
|
page.find_by_id('suivant').click
|
||||||
expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14")
|
expect(user.dossiers.first.individual.birthdate).to eq("1987-10-14")
|
||||||
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
|
@ -52,6 +54,7 @@ feature 'As a User I wanna create a dossier' do
|
||||||
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_carte_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
page.find_by_id('etape_suivante').click
|
page.find_by_id('etape_suivante').click
|
||||||
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
fill_in "champs_#{procedure_for_individual.dossiers.last.champs.first.id}", with: 'contenu du champ 1'
|
||||||
|
find(:css, '[name=submit_action]').set('nouveaux')
|
||||||
page.find_by_id('suivant').click
|
page.find_by_id('suivant').click
|
||||||
expect(user.dossiers.first.individual.birthdate).to eq(nil)
|
expect(user.dossiers.first.individual.birthdate).to eq(nil)
|
||||||
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
expect(page).to have_current_path(users_dossier_recapitulatif_path(procedure_for_individual.dossiers.last.id.to_s))
|
||||||
|
|
22
spec/helpers/type_de_champ_helper_spec.rb
Normal file
22
spec/helpers/type_de_champ_helper_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe TypeDeChampHelper, type: :helper do
|
||||||
|
describe ".tdc_options" do
|
||||||
|
let(:current_administrateur) { create(:administrateur) }
|
||||||
|
let(:pj_option) { ["Pièce justificative", "piece_justificative"] }
|
||||||
|
|
||||||
|
subject { tdc_options(current_administrateur) }
|
||||||
|
|
||||||
|
context "when the champ_pj_allowed_for_admin_id matches the current_administrateur's id" do
|
||||||
|
before { allow(Features).to receive(:champ_pj_allowed_for_admin_ids).and_return([current_administrateur.id]) }
|
||||||
|
|
||||||
|
it { is_expected.to include(pj_option) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the champ_pj_allowed_for_admin_id does not match the current_administrateur's id" do
|
||||||
|
before { allow(Features).to receive(:champ_pj_allowed_for_admin_ids).and_return([1000]) }
|
||||||
|
|
||||||
|
it { is_expected.not_to include(pj_option) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
166
spec/lib/active_storage/service/cellar_service_spec.rb
Normal file
166
spec/lib/active_storage/service/cellar_service_spec.rb
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
require 'active_storage/service/cellar_service'
|
||||||
|
require 'cgi'
|
||||||
|
require 'net/http'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
describe 'CellarService' do
|
||||||
|
let(:cellar_service) do
|
||||||
|
# These are actual keys, but they’re safe to put here because
|
||||||
|
# - they never had any rights attached, and
|
||||||
|
# - the keys were revoked before copying them here
|
||||||
|
|
||||||
|
ActiveStorage::Service::CellarService.new(
|
||||||
|
access_key_id: 'AKIAJFTRSGRH3RXX6D5Q',
|
||||||
|
secret_access_key: '3/y/3Tf5zkfcrTaLFxyKB/oU2/7ay7/Dz8UdEHC7',
|
||||||
|
bucket: 'rogets'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before { Timecop.freeze(Time.gm(2016, 10, 2)) }
|
||||||
|
after { Timecop.return }
|
||||||
|
|
||||||
|
describe 'signature generation' do
|
||||||
|
context 'for presigned URLs' do
|
||||||
|
subject do
|
||||||
|
cellar_service.send(
|
||||||
|
:signature,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
key: 'fichier',
|
||||||
|
expires: 5.minutes.from_now.to_i
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq('nzCsB6cip8oofkuOdvvJs6FafkA=') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for server-side requests' do
|
||||||
|
subject do
|
||||||
|
Net::HTTP::Delete.new('https://rogets.cellar.services.clever-cloud.com/fichier')
|
||||||
|
end
|
||||||
|
|
||||||
|
before { cellar_service.send(:sign, subject, 'fichier') }
|
||||||
|
|
||||||
|
it { expect(subject['date']).to eq(Time.now.httpdate) }
|
||||||
|
it { expect(subject['authorization']).to eq('AWS AKIAJFTRSGRH3RXX6D5Q:nkvviwZYb1V9HDrKyJZmY3Z8sSA=') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'presigned url for download' do
|
||||||
|
subject do
|
||||||
|
URI.parse(
|
||||||
|
cellar_service.url(
|
||||||
|
'fichier',
|
||||||
|
expires_in: 5.minutes,
|
||||||
|
filename: ActiveStorage::Filename.new("toto.png"),
|
||||||
|
disposition: 'attachment',
|
||||||
|
content_type: 'image/png'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
is_expected.to have_attributes(
|
||||||
|
scheme: 'https',
|
||||||
|
host: 'rogets.cellar.services.clever-cloud.com',
|
||||||
|
path: '/fichier'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
expect(CGI::parse(subject.query)).to eq(
|
||||||
|
{
|
||||||
|
'AWSAccessKeyId' => ['AKIAJFTRSGRH3RXX6D5Q'],
|
||||||
|
'Expires' => ['1475366700'],
|
||||||
|
'Signature' => ['nzCsB6cip8oofkuOdvvJs6FafkA='],
|
||||||
|
'response-content-disposition' => ["attachment; filename=\"toto.png\"; filename*=UTF-8''toto.png"],
|
||||||
|
'response-content-type' => ['image/png'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'presigned url for direct upload' do
|
||||||
|
subject do
|
||||||
|
URI.parse(
|
||||||
|
cellar_service.url_for_direct_upload(
|
||||||
|
'fichier',
|
||||||
|
expires_in: 5.minutes,
|
||||||
|
content_type: 'image/png',
|
||||||
|
content_length: 2713,
|
||||||
|
checksum: 'DEADBEEF'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
is_expected.to have_attributes(
|
||||||
|
scheme: 'https',
|
||||||
|
host: 'rogets.cellar.services.clever-cloud.com',
|
||||||
|
path: '/fichier'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
expect(CGI::parse(subject.query)).to eq(
|
||||||
|
{
|
||||||
|
'AWSAccessKeyId' => ['AKIAJFTRSGRH3RXX6D5Q'],
|
||||||
|
'Expires' => ['1475366700'],
|
||||||
|
'Signature' => ['VwsX5nxGfTC3dxXjS6wSeU64r5o=']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'parse_bucket_listing' do
|
||||||
|
let(:response) do
|
||||||
|
'<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
||||||
|
<Name>example-bucket</Name>
|
||||||
|
<Prefix></Prefix>
|
||||||
|
<KeyCount>2</KeyCount>
|
||||||
|
<MaxKeys>1000</MaxKeys>
|
||||||
|
<Delimiter>/</Delimiter>
|
||||||
|
<IsTruncated>false</IsTruncated>
|
||||||
|
<Contents>
|
||||||
|
<Key>sample1.jpg</Key>
|
||||||
|
<LastModified>2011-02-26T01:56:20.000Z</LastModified>
|
||||||
|
<ETag>"bf1d737a4d46a19f3bced6905cc8b902"</ETag>
|
||||||
|
<Size>142863</Size>
|
||||||
|
<StorageClass>STANDARD</StorageClass>
|
||||||
|
</Contents>
|
||||||
|
<Contents>
|
||||||
|
<Key>sample2.jpg</Key>
|
||||||
|
<LastModified>2011-02-26T01:56:20.000Z</LastModified>
|
||||||
|
<ETag>"bf1d737a4d46a19f3bced6905cc8b902"</ETag>
|
||||||
|
<Size>142863</Size>
|
||||||
|
<StorageClass>STANDARD</StorageClass>
|
||||||
|
</Contents>
|
||||||
|
</ListBucketResult>'
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { cellar_service.send(:parse_bucket_listing, response) }
|
||||||
|
|
||||||
|
it { is_expected.to eq(["sample1.jpg", "sample2.jpg"]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'bulk_deletion_request_body' do
|
||||||
|
let(:expected_response) do
|
||||||
|
'<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Delete>
|
||||||
|
<Quiet>true</Quiet>
|
||||||
|
<Object>
|
||||||
|
<Key>chapi</Key>
|
||||||
|
</Object>
|
||||||
|
<Object>
|
||||||
|
<Key>chapo</Key>
|
||||||
|
</Object>
|
||||||
|
</Delete>
|
||||||
|
'
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { cellar_service.send(:bulk_deletion_request_body, ['chapi', 'chapo']) }
|
||||||
|
|
||||||
|
it { is_expected.to eq(expected_response) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,6 +3,9 @@ require 'spec_helper'
|
||||||
describe 'admin/types_de_champ/show.html.haml', type: :view do
|
describe 'admin/types_de_champ/show.html.haml', type: :view do
|
||||||
let(:procedure) { create(:procedure) }
|
let(:procedure) { create(:procedure) }
|
||||||
|
|
||||||
|
# FIXME: delete this when support for pj champ is generalized
|
||||||
|
before { allow(view).to receive(:current_administrateur).and_return(create(:administrateur)) }
|
||||||
|
|
||||||
describe 'fields sorted' do
|
describe 'fields sorted' do
|
||||||
let(:first_libelle) { 'salut la compagnie' }
|
let(:first_libelle) { 'salut la compagnie' }
|
||||||
let(:last_libelle) { 'je suis bien sur la page' }
|
let(:last_libelle) { 'je suis bien sur la page' }
|
||||||
|
|
|
@ -3,6 +3,9 @@ require 'spec_helper'
|
||||||
describe 'admin/types_de_champ/show.html.haml', type: :view do
|
describe 'admin/types_de_champ/show.html.haml', type: :view do
|
||||||
let(:procedure) { create(:procedure) }
|
let(:procedure) { create(:procedure) }
|
||||||
|
|
||||||
|
# FIXME: delete this when support for pj champ is generalized
|
||||||
|
before { allow(view).to receive(:current_administrateur).and_return(create(:administrateur)) }
|
||||||
|
|
||||||
describe 'fields sorted' do
|
describe 'fields sorted' do
|
||||||
let(:first_libelle) { 'salut la compagnie' }
|
let(:first_libelle) { 'salut la compagnie' }
|
||||||
let(:last_libelle) { 'je suis bien sur la page' }
|
let(:last_libelle) { 'je suis bien sur la page' }
|
||||||
|
|
Loading…
Reference in a new issue