Merge branch 'dev'
This commit is contained in:
commit
e56efa49b0
26 changed files with 557 additions and 91 deletions
|
@ -12,10 +12,12 @@ defaults: &defaults
|
||||||
|
|
||||||
bundle_restore_cache: &bundle_restore_cache
|
bundle_restore_cache: &bundle_restore_cache
|
||||||
restore_cache:
|
restore_cache:
|
||||||
|
name: Restore Bundler Package Cache
|
||||||
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
|
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
|
||||||
|
|
||||||
bundle_save_cache: &bundle_save_cache
|
bundle_save_cache: &bundle_save_cache
|
||||||
save_cache:
|
save_cache:
|
||||||
|
name: Save Bundler Package Cache
|
||||||
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
|
key: bundle-install-v8-{{ arch }}-{{ checksum "Gemfile.lock" }}
|
||||||
paths:
|
paths:
|
||||||
- ~/vendor/bundle
|
- ~/vendor/bundle
|
||||||
|
@ -27,13 +29,15 @@ bundle_install: &bundle_install
|
||||||
|
|
||||||
yarn_restore_cache: &yarn_restore_cache
|
yarn_restore_cache: &yarn_restore_cache
|
||||||
restore_cache:
|
restore_cache:
|
||||||
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }}
|
name: Restore Yarn Package Cache
|
||||||
|
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
yarn_save_cache: &yarn_save_cache
|
yarn_save_cache: &yarn_save_cache
|
||||||
save_cache:
|
save_cache:
|
||||||
key: yarn-install-v8-{{ arch }}-{{ checksum "yarn.lock" }}
|
name: Save Yarn Package Cache
|
||||||
|
key: yarn-install-v1-{{ arch }}-{{ checksum "yarn.lock" }}
|
||||||
paths:
|
paths:
|
||||||
- ~/node_modules
|
- ~/.cache/yarn
|
||||||
|
|
||||||
yarn_install: &yarn_install
|
yarn_install: &yarn_install
|
||||||
run:
|
run:
|
||||||
|
|
|
@ -124,6 +124,7 @@ Une fois `overmind` lancé, et un breakpoint `byebug` inséré dans le code, il
|
||||||
## Régénérer les binstubs
|
## Régénérer les binstubs
|
||||||
|
|
||||||
bundle binstub railties --force
|
bundle binstub railties --force
|
||||||
|
bundle binstub unicorn --force
|
||||||
bin/rake rails:update:bin
|
bin/rake rails:update:bin
|
||||||
|
|
||||||
## Tâches Super Admin
|
## Tâches Super Admin
|
||||||
|
|
|
@ -10,11 +10,11 @@
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
color: $black;
|
color: $black;
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
|
cursor: pointer;
|
||||||
background: $light-grey;
|
background: $light-grey;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,17 @@
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
filter: saturate(50%);
|
||||||
|
}
|
||||||
|
|
||||||
&.primary {
|
&.primary {
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
border-color: $blue;
|
border-color: $blue;
|
||||||
background-color: $blue;
|
background-color: $blue;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
background: $light-blue;
|
background: $light-blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +44,7 @@
|
||||||
border-color: $blue;
|
border-color: $blue;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
background: $light-blue;
|
background: $light-blue;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +67,7 @@
|
||||||
border-color: $green;
|
border-color: $green;
|
||||||
background-color: $green;
|
background-color: $green;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
color: $green;
|
color: $green;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +78,7 @@
|
||||||
border-color: $black;
|
border-color: $black;
|
||||||
background-color: $black;
|
background-color: $black;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
color: $black;
|
color: $black;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
@ -84,7 +89,7 @@
|
||||||
border-color: $dark-red;
|
border-color: $dark-red;
|
||||||
background-color: $dark-red;
|
background-color: $dark-red;
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
color: $dark-red;
|
color: $dark-red;
|
||||||
background-color: #FFFFFF;
|
background-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
@import "colors";
|
@import "colors";
|
||||||
@import "constants";
|
@import "constants";
|
||||||
|
|
||||||
.dossier-edit {
|
.dossier-header {
|
||||||
.dossier-header {
|
|
||||||
background-color: $light-grey;
|
|
||||||
margin-bottom: $default-padding;
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: $default-padding;
|
padding-bottom: $default-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
@ -17,8 +13,15 @@
|
||||||
vertical-align: -3px;
|
vertical-align: -3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
.dossier-form-actions {
|
||||||
|
margin-top: $default-padding;
|
||||||
|
margin-bottom: $default-padding;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dossier-edit {
|
||||||
.prologue {
|
.prologue {
|
||||||
margin: (1.5 * $default-padding) 0;
|
margin: (1.5 * $default-padding) 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -316,6 +316,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.send-notice {
|
||||||
|
@include notice-text-style;
|
||||||
|
margin-bottom: $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send-wrapper + .send-notice {
|
||||||
|
margin-top: - $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-champ {
|
.inline-champ {
|
||||||
margin-left: $default-spacer;
|
margin-left: $default-spacer;
|
||||||
margin-right: $default-spacer;
|
margin-right: $default-spacer;
|
||||||
|
|
35
app/assets/stylesheets/new_design/invites_form.scss
Normal file
35
app/assets/stylesheets/new_design/invites_form.scss
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@import "constants";
|
||||||
|
|
||||||
|
#invites-form {
|
||||||
|
padding: $default-padding;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
margin-top: $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: $default-spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: $default-spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-position: inside;
|
||||||
|
list-style-type: disc;
|
||||||
|
margin-bottom: $default-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=email] {
|
||||||
|
width: auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-left: $default-spacer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,11 @@ class InvitesController < ApplicationController
|
||||||
before_action :ensure_user_signed_in
|
before_action :ensure_user_signed_in
|
||||||
|
|
||||||
def create
|
def create
|
||||||
email = params[:email].downcase
|
email = params[:invite_email].downcase
|
||||||
|
dossier = current_user.dossiers.find(params[:dossier_id])
|
||||||
|
|
||||||
invite = InviteUser.create(
|
invite = InviteUser.create(
|
||||||
dossier: current_user.dossiers.find(params[:dossier_id]),
|
dossier: dossier,
|
||||||
user: User.find_by(email: email),
|
user: User.find_by(email: email),
|
||||||
email: email,
|
email: email,
|
||||||
email_sender: current_user.email
|
email_sender: current_user.email
|
||||||
|
@ -18,12 +19,15 @@ class InvitesController < ApplicationController
|
||||||
InviteMailer.invite_guest(invite).deliver_later
|
InviteMailer.invite_guest(invite).deliver_later
|
||||||
end
|
end
|
||||||
|
|
||||||
flash.notice = "Invitation envoyée (#{invite.email})"
|
flash.notice = "Une invitation a été envoyée à #{invite.email}."
|
||||||
else
|
else
|
||||||
flash.alert = invite.errors.full_messages
|
flash.alert = invite.errors.full_messages
|
||||||
end
|
end
|
||||||
|
|
||||||
redirect_to url_for(controller: 'users/recapitulatif', action: :show, dossier_id: params['dossier_id'])
|
respond_to do |format|
|
||||||
|
format.html { redirect_back(fallback_location: helpers.url_for_dossier(dossier)) }
|
||||||
|
format.js { @dossier = dossier }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -9,7 +9,11 @@ class Users::Dossiers::InvitesController < UsersController
|
||||||
def show
|
def show
|
||||||
@facade = InviteDossierFacades.new params[:id].to_i, current_user.email
|
@facade = InviteDossierFacades.new params[:id].to_i, current_user.email
|
||||||
|
|
||||||
|
if @facade.dossier.brouillon?
|
||||||
|
redirect_to modifier_dossier_path(@facade.dossier)
|
||||||
|
else
|
||||||
render 'users/recapitulatif/show'
|
render 'users/recapitulatif/show'
|
||||||
|
end
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
flash.alert = t('errors.messages.dossier_not_found')
|
flash.alert = t('errors.messages.dossier_not_found')
|
||||||
redirect_to url_for dossiers_path
|
redirect_to url_for dossiers_path
|
||||||
|
|
|
@ -54,6 +54,6 @@ class DossierFacades
|
||||||
end
|
end
|
||||||
|
|
||||||
def followers
|
def followers
|
||||||
Gestionnaire.joins(:follows).where("follows.dossier_id=#{@dossier.id}")
|
@dossier.followers_gestionnaires
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
17
app/views/invites/_form.html.haml
Normal file
17
app/views/invites/_form.html.haml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#invites-form
|
||||||
|
- if dossier.invites.present?
|
||||||
|
%h4 Personnes invitées à participer à ce dossier
|
||||||
|
%ul
|
||||||
|
- dossier.invites.each do |invite|
|
||||||
|
%li= invite.email
|
||||||
|
%p Ces personnes peuvent modifier ce dossier.
|
||||||
|
- if dossier.brouillon?
|
||||||
|
%p Une fois le dossier complet, vous devez le soumettre vous-même.
|
||||||
|
|
||||||
|
- else
|
||||||
|
%p Vous pouvez inviter quelqu’un à remplir ce dossier avec vous.
|
||||||
|
%p Cette personne aura le droit de modifier votre dossier.
|
||||||
|
|
||||||
|
= form_tag invites_dossier_path(dossier_id: dossier.id), remote: true, method: :post, class: 'form' do
|
||||||
|
= email_field_tag :invite_email, '', class: 'small', placeholder: 'adresse email', required: true
|
||||||
|
= submit_tag 'Envoyer une invitation', class: 'button accepted'
|
6
app/views/invites/create.js.erb
Normal file
6
app/views/invites/create.js.erb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
var formView = "<%= escape_javascript(render partial: 'invites/form', locals: { dossier: @dossier }) %>";
|
||||||
|
document.querySelector("#invites-form").outerHTML = formView;
|
||||||
|
|
||||||
|
var flashMessagesView = "<%= escape_javascript(render partial: 'layouts/flash_messages') %>";
|
||||||
|
document.querySelector("#flash_messages").outerHTML = flashMessagesView;
|
||||||
|
<% flash.clear %>
|
|
@ -1,4 +1,5 @@
|
||||||
- if flash.any?
|
#flash_messages
|
||||||
|
- if flash.any?
|
||||||
#flash_message.center
|
#flash_message.center
|
||||||
- flash.each do |key, value|
|
- flash.each do |key, value|
|
||||||
- if value.class == Array
|
- if value.class == Array
|
||||||
|
|
|
@ -20,5 +20,5 @@
|
||||||
|
|
||||||
%li
|
%li
|
||||||
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
|
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
|
||||||
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email'
|
= text_field_tag :invite_email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invite_email'
|
||||||
= submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" }
|
= submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" }
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
= render partial: "new_user/dossiers/footer", locals: { dossier: @dossier }
|
= render partial: "new_user/dossiers/footer", locals: { dossier: @dossier }
|
||||||
|
|
||||||
.dossier-edit
|
.dossier-edit
|
||||||
.dossier-header
|
.dossier-header.sub-header
|
||||||
.container
|
.container
|
||||||
%h1
|
= render partial: "shared/dossiers/header", locals: { dossier: @dossier, apercu: false }
|
||||||
%span.icon.folder
|
|
||||||
= @dossier.procedure.libelle
|
|
||||||
|
|
||||||
= render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false }
|
= render partial: "shared/dossiers/edit", locals: { dossier: @dossier, apercu: false }
|
||||||
|
|
|
@ -58,6 +58,15 @@
|
||||||
= link_to "#", class: "button icon-only" do
|
= link_to "#", class: "button icon-only" do
|
||||||
%span.icon.follow
|
%span.icon.follow
|
||||||
|
|
||||||
|
%p
|
||||||
|
%button.button{ disabled: true } .button.disabled
|
||||||
|
|
||||||
|
%button.button.primary{ disabled: true } .button.primary.disabled
|
||||||
|
|
||||||
|
%button.button.secondary{ disabled: true } .button.secondary.disabled
|
||||||
|
|
||||||
|
%button.button.danger{ disabled: true } .button.danger.disabled
|
||||||
|
|
||||||
%p
|
%p
|
||||||
= link_to ".button.accepted", "#", class: "button accepted"
|
= link_to ".button.accepted", "#", class: "button accepted"
|
||||||
|
|
||||||
|
|
|
@ -74,9 +74,10 @@
|
||||||
class: 'button send secondary',
|
class: 'button send secondary',
|
||||||
data: { action: 'draft', disable_with: 'Envoi...' }
|
data: { action: 'draft', disable_with: 'Envoi...' }
|
||||||
|
|
||||||
- if current_user.owns?(dossier) && dossier.can_transition_to_en_construction?
|
- if dossier.can_transition_to_en_construction?
|
||||||
= f.button 'Soumettre le dossier',
|
= f.button 'Soumettre le dossier',
|
||||||
class: 'button send primary',
|
class: 'button send primary',
|
||||||
|
disabled: !current_user.owns?(dossier),
|
||||||
data: { action: 'submit', disable_with: 'Envoi...' }
|
data: { action: 'submit', disable_with: 'Envoi...' }
|
||||||
|
|
||||||
- else
|
- else
|
||||||
|
@ -84,4 +85,8 @@
|
||||||
class: 'button send primary',
|
class: 'button send primary',
|
||||||
data: { action: 'submit', disable_with: 'Envoi...' }
|
data: { action: 'submit', disable_with: 'Envoi...' }
|
||||||
|
|
||||||
|
- if dossier.brouillon? && !current_user.owns?(dossier)
|
||||||
|
.send-notice.invite-cannot-submit
|
||||||
|
En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le soumettre lui-même.
|
||||||
|
|
||||||
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
||||||
|
|
16
app/views/shared/dossiers/_header.html.haml
Normal file
16
app/views/shared/dossiers/_header.html.haml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
%h1
|
||||||
|
%span.icon.folder
|
||||||
|
= dossier.procedure.libelle
|
||||||
|
|
||||||
|
.dossier-form-actions
|
||||||
|
- if current_user.owns?(dossier)
|
||||||
|
%span.button.dropdown.invite-user-action
|
||||||
|
%span.icon.person
|
||||||
|
- if dossier.invites.count > 0
|
||||||
|
Voir les personnes invitées
|
||||||
|
%span.badge= dossier.invites.count
|
||||||
|
- else
|
||||||
|
Inviter une personne à modifier ce dossier
|
||||||
|
|
||||||
|
.dropdown-content.fade-in-down
|
||||||
|
= render partial: "invites/form", locals: { dossier: dossier }
|
106
bin/bundle
106
bin/bundle
|
@ -1,3 +1,105 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
# frozen_string_literal: true
|
||||||
load Gem.bin_path('bundler', 'bundle')
|
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'bundle' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require "rubygems"
|
||||||
|
|
||||||
|
m = Module.new do
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def invoked_as_script?
|
||||||
|
File.expand_path($0) == File.expand_path(__FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def env_var_version
|
||||||
|
ENV["BUNDLER_VERSION"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def cli_arg_version
|
||||||
|
return unless invoked_as_script? # don't want to hijack other binstubs
|
||||||
|
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
||||||
|
bundler_version = nil
|
||||||
|
update_index = nil
|
||||||
|
ARGV.each_with_index do |a, i|
|
||||||
|
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
||||||
|
bundler_version = a
|
||||||
|
end
|
||||||
|
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
||||||
|
bundler_version = $1 || ">= 0.a"
|
||||||
|
update_index = i
|
||||||
|
end
|
||||||
|
bundler_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def gemfile
|
||||||
|
gemfile = ENV["BUNDLE_GEMFILE"]
|
||||||
|
return gemfile if gemfile && !gemfile.empty?
|
||||||
|
|
||||||
|
File.expand_path("../../Gemfile", __FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def lockfile
|
||||||
|
lockfile =
|
||||||
|
case File.basename(gemfile)
|
||||||
|
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
||||||
|
else "#{gemfile}.lock"
|
||||||
|
end
|
||||||
|
File.expand_path(lockfile)
|
||||||
|
end
|
||||||
|
|
||||||
|
def lockfile_version
|
||||||
|
return unless File.file?(lockfile)
|
||||||
|
lockfile_contents = File.read(lockfile)
|
||||||
|
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
||||||
|
Regexp.last_match(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def bundler_version
|
||||||
|
@bundler_version ||= begin
|
||||||
|
env_var_version || cli_arg_version ||
|
||||||
|
lockfile_version || "#{Gem::Requirement.default}.a"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_bundler!
|
||||||
|
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
||||||
|
|
||||||
|
# must dup string for RG < 1.8 compatibility
|
||||||
|
activate_bundler(bundler_version.dup)
|
||||||
|
end
|
||||||
|
|
||||||
|
def activate_bundler(bundler_version)
|
||||||
|
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
|
||||||
|
bundler_version = "< 2"
|
||||||
|
end
|
||||||
|
gem_error = activation_error_handling do
|
||||||
|
gem "bundler", bundler_version
|
||||||
|
end
|
||||||
|
return if gem_error.nil?
|
||||||
|
require_error = activation_error_handling do
|
||||||
|
require "bundler/version"
|
||||||
|
end
|
||||||
|
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
||||||
|
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
|
||||||
|
exit 42
|
||||||
|
end
|
||||||
|
|
||||||
|
def activation_error_handling
|
||||||
|
yield
|
||||||
|
nil
|
||||||
|
rescue StandardError, LoadError => e
|
||||||
|
e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
m.load_bundler!
|
||||||
|
|
||||||
|
if m.invoked_as_script?
|
||||||
|
load Gem.bin_path("bundler", "bundle")
|
||||||
|
end
|
||||||
|
|
29
bin/unicorn
Executable file
29
bin/unicorn
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'unicorn' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require "pathname"
|
||||||
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||||
|
Pathname.new(__FILE__).realpath)
|
||||||
|
|
||||||
|
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||||
|
|
||||||
|
if File.file?(bundle_binstub)
|
||||||
|
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||||
|
load(bundle_binstub)
|
||||||
|
else
|
||||||
|
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||||
|
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require "rubygems"
|
||||||
|
require "bundler/setup"
|
||||||
|
|
||||||
|
load Gem.bin_path("unicorn", "unicorn")
|
29
bin/unicorn_rails
Executable file
29
bin/unicorn_rails
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'unicorn_rails' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require "pathname"
|
||||||
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||||
|
Pathname.new(__FILE__).realpath)
|
||||||
|
|
||||||
|
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||||
|
|
||||||
|
if File.file?(bundle_binstub)
|
||||||
|
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||||
|
load(bundle_binstub)
|
||||||
|
else
|
||||||
|
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||||
|
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require "rubygems"
|
||||||
|
require "bundler/setup"
|
||||||
|
|
||||||
|
load Gem.bin_path("unicorn", "unicorn_rails")
|
|
@ -11,7 +11,7 @@ describe InvitesController, type: :controller do
|
||||||
sign_in signed_in_profile
|
sign_in signed_in_profile
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { post :create, params: { dossier_id: dossier.id, email: email } }
|
subject { post :create, params: { dossier_id: dossier.id, invite_email: email } }
|
||||||
|
|
||||||
context "when gestionnaire is signed_in" do
|
context "when gestionnaire is signed_in" do
|
||||||
let(:signed_in_profile) { create(:gestionnaire) }
|
let(:signed_in_profile) { create(:gestionnaire) }
|
||||||
|
@ -69,11 +69,16 @@ describe InvitesController, type: :controller do
|
||||||
|
|
||||||
context 'when user has access to dossier' do
|
context 'when user has access to dossier' do
|
||||||
before do
|
before do
|
||||||
|
request.env["HTTP_REFERER"] = "/dossiers/#{dossier.id}/modifier"
|
||||||
dossier.update(user: signed_in_profile)
|
dossier.update(user: signed_in_profile)
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect { subject }.to change(InviteUser, :count).by(1) }
|
it { expect { subject }.to change(InviteUser, :count).by(1) }
|
||||||
|
|
||||||
|
it "redirects to the previous URL" do
|
||||||
|
expect(subject).to redirect_to("/dossiers/#{dossier.id}/modifier")
|
||||||
|
end
|
||||||
|
|
||||||
context 'when email is assign to an user' do
|
context 'when email is assign to an user' do
|
||||||
let! (:user_invite) { create(:user, email: email) }
|
let! (:user_invite) { create(:user, email: email) }
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,47 @@ describe NewUser::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#forbid_invite_submission!" do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:asked_dossier) { create(:dossier) }
|
||||||
|
let(:ensure_authorized) { :forbid_invite_submission! }
|
||||||
|
let(:submit_action) { 'submit' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
@controller.params = @controller.params.merge(dossier_id: asked_dossier.id, submit_action: submit_action)
|
||||||
|
allow(@controller).to receive(:current_user).and_return(user)
|
||||||
|
allow(@controller).to receive(:redirect_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user save their own draft' do
|
||||||
|
let(:asked_dossier) { create(:dossier, user: user) }
|
||||||
|
let(:submit_action) { 'draft' }
|
||||||
|
|
||||||
|
it_behaves_like 'does not redirect nor flash'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a user submit their own dossier' do
|
||||||
|
let(:asked_dossier) { create(:dossier, user: user) }
|
||||||
|
let(:submit_action) { 'submit' }
|
||||||
|
|
||||||
|
it_behaves_like 'does not redirect nor flash'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an invite save the draft for a dossier where they where invited' do
|
||||||
|
before { create(:invite, dossier: asked_dossier, user: user, type: 'InviteUser') }
|
||||||
|
let(:submit_action) { 'draft' }
|
||||||
|
|
||||||
|
it_behaves_like 'does not redirect nor flash'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an invite submit a dossier where they where invited' do
|
||||||
|
before { create(:invite, dossier: asked_dossier, user: user, type: 'InviteUser') }
|
||||||
|
let(:submit_action) { 'submit' }
|
||||||
|
|
||||||
|
it_behaves_like 'redirects and flashes'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'attestation' do
|
describe 'attestation' do
|
||||||
before { sign_in(user) }
|
before { sign_in(user) }
|
||||||
|
|
||||||
|
|
|
@ -1,78 +1,78 @@
|
||||||
describe Users::Dossiers::InvitesController, type: :controller do
|
describe Users::Dossiers::InvitesController, type: :controller do
|
||||||
describe '#authenticate_user!' do
|
describe '#authenticate_user!' do
|
||||||
let(:user) { create :user }
|
let(:user) { create :user }
|
||||||
let(:invite) { create :invite }
|
let(:dossier) { create(:dossier, :en_construction) }
|
||||||
|
let(:invite) { create(:invite, dossier: dossier) }
|
||||||
|
|
||||||
|
subject { get :show, params: { id: invite.id, email: email } }
|
||||||
|
|
||||||
context 'when email is not set' do
|
context 'when email is not set' do
|
||||||
context 'when user is not connected' do
|
let(:email) { nil }
|
||||||
before do
|
|
||||||
get :show, params: { id: invite.id }
|
|
||||||
end
|
|
||||||
|
|
||||||
|
context 'and user is not connected' do
|
||||||
it { is_expected.to redirect_to new_user_session_path }
|
it { is_expected.to redirect_to new_user_session_path }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is connected' do
|
context 'and user is connected' do
|
||||||
let!(:invite) { create :invite, user: user }
|
let(:invite) { create :invite, dossier: dossier, user: user }
|
||||||
|
before { sign_in invite.user }
|
||||||
before do
|
it { is_expected.to have_http_status(:ok) }
|
||||||
sign_in invite.user
|
|
||||||
|
|
||||||
get :show, params: { id: invite.id }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(response.status).to eq 200 }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when email is set' do
|
|
||||||
before do
|
|
||||||
get :show, params: { id: invite.id, email: email }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when email is blank' do
|
context 'when email is blank' do
|
||||||
let(:email) { '' }
|
let(:email) { '' }
|
||||||
|
|
||||||
it { is_expected.to redirect_to new_user_session_path }
|
it { is_expected.to redirect_to new_user_session_path }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when email is not blank' do
|
context 'when email is not blank' do
|
||||||
context 'when email is affected at an user' do
|
context 'when email is affected at an user' do
|
||||||
let(:email) { user.email }
|
let(:email) { user.email }
|
||||||
|
|
||||||
it { is_expected.to redirect_to new_user_session_path }
|
it { is_expected.to redirect_to new_user_session_path }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when email is not affected at an user' do
|
context 'when email is not affected at an user' do
|
||||||
let(:email) { 'new_user@octo.com' }
|
let(:email) { 'new_user@octo.com' }
|
||||||
|
|
||||||
it { is_expected.to redirect_to new_user_registration_path(user_email: email) }
|
it { is_expected.to redirect_to new_user_registration_path(user_email: email) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe '#GET show' do
|
describe '#GET show' do
|
||||||
let(:user) { create :user }
|
let(:user) { create :user }
|
||||||
|
let(:dossier) { create :dossier }
|
||||||
let(:invite) { create :invite, email: email, dossier: (create :dossier) }
|
let(:invite) { create :invite, email: email, dossier: dossier }
|
||||||
|
|
||||||
subject { get :show, params: { id: invite.id } }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
sign_in user
|
sign_in user
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when invitation ID is attach at the user email account' do
|
subject! { get :show, params: { id: invite.id } }
|
||||||
|
|
||||||
|
context 'when invitation ID is attached at the user email account' do
|
||||||
let(:email) { user.email }
|
let(:email) { user.email }
|
||||||
it { expect(subject.status).to eq 200 }
|
|
||||||
|
context 'and dossier is a brouillon' do
|
||||||
|
let(:dossier) { create :dossier, state: 'brouillon' }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(302) }
|
||||||
|
it { is_expected.to redirect_to modifier_dossier_path(dossier) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when invitation ID is not attach at the user email account' do
|
context 'and dossier is not a brouillon' do
|
||||||
|
let(:dossier) { create :dossier, :en_construction }
|
||||||
|
|
||||||
|
it { is_expected.to have_http_status(:ok) }
|
||||||
|
it { is_expected.to render_template('users/recapitulatif/show') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invitation ID is not attached at the user email account' do
|
||||||
let(:email) { 'fake@email.com' }
|
let(:email) { 'fake@email.com' }
|
||||||
|
|
||||||
it { expect(subject.status).to eq 302 }
|
it { is_expected.to have_http_status(302) }
|
||||||
it { is_expected.to redirect_to dossiers_path }
|
it { is_expected.to redirect_to dossiers_path }
|
||||||
|
it { expect(flash[:alert]).to be_present }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
135
spec/features/new_user/invite_spec.rb
Normal file
135
spec/features/new_user/invite_spec.rb
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
feature 'Invitations' do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:invited_user) { create(:user, email: 'user_invite@exemple.fr') }
|
||||||
|
let(:procedure) { create(:procedure, :published, :with_type_de_champ) }
|
||||||
|
let(:invite) { create(:invite_user, user: invited_user, dossier: dossier) }
|
||||||
|
|
||||||
|
context 'when the dossier is a brouillon' do
|
||||||
|
let!(:dossier) { create(:dossier, :for_individual, state: 'brouillon', user: user, procedure: procedure) }
|
||||||
|
|
||||||
|
scenario 'on the form, a user can invite another user to collaborate on the dossier', js: true do
|
||||||
|
log_in(user)
|
||||||
|
navigate_to_brouillon(dossier)
|
||||||
|
|
||||||
|
fill_in 'Libelle du champ', with: 'Some edited value'
|
||||||
|
send_invite_to "user_invite@exemple.fr"
|
||||||
|
|
||||||
|
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||||
|
expect(page).to have_text("Une invitation a été envoyée à user_invite@exemple.fr.")
|
||||||
|
expect(page).to have_text("user_invite@exemple.fr")
|
||||||
|
|
||||||
|
# Ensure unsaved edits to the form are not lost
|
||||||
|
expect(page).to have_field('Libelle du champ', with: 'Some edited value')
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'an invited user can see and edit the draft', js: true do
|
||||||
|
visit users_dossiers_invite_path(invite)
|
||||||
|
expect(page).to have_current_path(new_user_session_path)
|
||||||
|
|
||||||
|
submit_login_form(invited_user)
|
||||||
|
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||||
|
expect(page).to have_no_selector('.button.invite-user-action')
|
||||||
|
|
||||||
|
fill_in 'Libelle du champ', with: 'Some edited value'
|
||||||
|
click_button 'Enregistrer le brouillon'
|
||||||
|
expect(page).to have_text('Votre brouillon a bien été sauvegardé')
|
||||||
|
expect(page).to have_field('Libelle du champ', with: 'Some edited value')
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'an invited user cannot submit the draft' do
|
||||||
|
visit users_dossiers_invite_path(invite)
|
||||||
|
expect(page).to have_current_path(new_user_session_path)
|
||||||
|
|
||||||
|
submit_login_form(invited_user)
|
||||||
|
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||||
|
|
||||||
|
expect(page).to have_button('Soumettre le dossier', disabled: true)
|
||||||
|
expect(page).to have_selector('.invite-cannot-submit')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the dossier is en_construction' do
|
||||||
|
let!(:dossier) { create(:dossier, :for_individual, :en_construction, user: user, procedure: procedure) }
|
||||||
|
|
||||||
|
scenario 'on dossier details, a user can invite another user to collaborate on the dossier', js: true do
|
||||||
|
log_in(user)
|
||||||
|
navigate_to_recapitulatif(dossier)
|
||||||
|
|
||||||
|
legacy_send_invite_to "user_invite@exemple.fr"
|
||||||
|
|
||||||
|
expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier))
|
||||||
|
expect(page).to have_text("Une invitation a été envoyée à user_invite@exemple.fr.")
|
||||||
|
expect(page).to have_text("user_invite@exemple.fr")
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'an invited user can see and edit the dossier', js: true do
|
||||||
|
visit users_dossiers_invite_path(invite)
|
||||||
|
expect(page).to have_current_path(new_user_session_path)
|
||||||
|
|
||||||
|
submit_login_form(invited_user)
|
||||||
|
expect(page).to have_current_path(users_dossiers_invite_path(invite))
|
||||||
|
expect(page).to have_no_selector('.button.invite-user-action')
|
||||||
|
expect(page).to have_text("Dossier nº #{dossier.id}")
|
||||||
|
|
||||||
|
# We should be able to just click() the link, but Capybara detects that the
|
||||||
|
# enclosing div would be clicked instead.
|
||||||
|
expect(page).to have_link("MODIFIER", href: modifier_dossier_path(dossier))
|
||||||
|
visit modifier_dossier_path(dossier)
|
||||||
|
|
||||||
|
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||||
|
fill_in "Libelle du champ", with: "Some edited value"
|
||||||
|
click_button "Enregistrer les modifications du dossier"
|
||||||
|
|
||||||
|
expect(page).to have_current_path(users_dossiers_invite_path(invite))
|
||||||
|
expect(page).to have_text("Some edited value")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def log_in(user)
|
||||||
|
visit '/'
|
||||||
|
click_on 'Connexion'
|
||||||
|
submit_login_form(user)
|
||||||
|
expect(page).to have_current_path(dossiers_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def submit_login_form(user)
|
||||||
|
fill_in 'user_email', with: user.email
|
||||||
|
fill_in 'user_password', with: user.password
|
||||||
|
click_on 'Se connecter'
|
||||||
|
end
|
||||||
|
|
||||||
|
def navigate_to_brouillon(dossier)
|
||||||
|
expect(page).to have_current_path(dossiers_path)
|
||||||
|
click_on(dossier.id)
|
||||||
|
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||||
|
end
|
||||||
|
|
||||||
|
def navigate_to_recapitulatif(dossier)
|
||||||
|
expect(page).to have_current_path(dossiers_path)
|
||||||
|
click_on(dossier.id)
|
||||||
|
expect(page).to have_current_path(users_dossier_recapitulatif_path(dossier))
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_invite_to(invited_email)
|
||||||
|
find('.button.invite-user-action').click()
|
||||||
|
expect(page).to have_button("Envoyer une invitation", visible: true)
|
||||||
|
|
||||||
|
fill_in 'invite_email', with: invited_email
|
||||||
|
click_on "Envoyer une invitation"
|
||||||
|
end
|
||||||
|
|
||||||
|
def legacy_send_invite_to(invited_email)
|
||||||
|
find('.dropdown-toggle', text: "Voir les personnes impliquées").click()
|
||||||
|
expect(page).to have_button("Ajouter", visible: true)
|
||||||
|
|
||||||
|
fill_in 'invite_email', with: invited_email
|
||||||
|
|
||||||
|
page.accept_alert "Envoyer l'invitation ?" do
|
||||||
|
click_on "Ajouter"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -189,6 +189,14 @@ describe Dossier do
|
||||||
let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") }
|
let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") }
|
||||||
let!(:follow) { create(:follow, gestionnaire: gestionnaire, dossier: dossier) }
|
let!(:follow) { create(:follow, gestionnaire: gestionnaire, dossier: dossier) }
|
||||||
|
|
||||||
|
describe "followers_gestionnaires" do
|
||||||
|
let(:non_following_gestionnaire) { create(:gestionnaire) }
|
||||||
|
subject { dossier.followers_gestionnaires }
|
||||||
|
|
||||||
|
it { expect(subject).to eq [gestionnaire] }
|
||||||
|
it { expect(subject).not_to include(non_following_gestionnaire) }
|
||||||
|
end
|
||||||
|
|
||||||
describe '#export_headers' do
|
describe '#export_headers' do
|
||||||
subject { dossier.export_headers }
|
subject { dossier.export_headers }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue