aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorent Peyraud2017-07-11 10:48:15 +0200
committerFlorent Peyraud2017-07-11 10:48:15 +0200
commit4db6d356602660bdf1fd02e713beaa068cb0fa0b (patch)
tree9c44c8fa9dc6c4c4aa284490ab17c87565833986
parent485adab2f70fb071f936bb32f05fe6e0f88b6231 (diff)
parentade9962a6de04a0b033c060c859a0676869db43f (diff)
downloadchouette-core-4db6d356602660bdf1fd02e713beaa068cb0fa0b.tar.bz2
Merge branch 'master' of github.com:AF83/stif-boiv
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js2
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js8
-rw-r--r--app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js14
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js2
-rw-r--r--app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js2
-rw-r--r--app/controllers/referentials_controller.rb3
-rw-r--r--app/decorators/referential_decorator.rb52
-rw-r--r--app/jobs/mailer_job.rb2
-rw-r--r--app/mailers/calendar_mailer.rb2
-rw-r--r--app/models/user.rb5
-rw-r--r--app/policies/calendar_policy.rb4
-rw-r--r--app/policies/referential_policy.rb4
-rw-r--r--app/views/calendar_mailer/created.html.slim3
-rw-r--r--app/views/calendar_mailer/updated.html.slim2
-rw-r--r--app/views/calendars/index.html.slim10
-rw-r--r--config/application.rb2
-rw-r--r--config/locales/mailers.en.yml4
-rw-r--r--config/locales/mailers.fr.yml4
-rw-r--r--lib/html_element.rb2
-rw-r--r--lib/stif/permission_translator.rb7
-rw-r--r--spec/decorators/referential_decorator_spec.rb76
-rw-r--r--spec/factories/users.rb12
-rw-r--r--spec/features/calendars_permissions_spec.rb26
-rw-r--r--spec/features/connection_links_spec.rb5
-rw-r--r--spec/jobs/mailer_job_spec.rb5
-rw-r--r--spec/lib/stif/permission_translator_spec.rb10
-rw-r--r--spec/mailers/calendar_mailer_spec.rb2
-rw-r--r--spec/mailers/previews/calendar_mailer_preview.rb9
-rw-r--r--spec/policies/calendar_policy_spec.rb4
-rw-r--r--spec/policies/referential_policy_spec.rb97
-rw-r--r--spec/support/decorator_helpers.rb27
-rw-r--r--spec/support/devise.rb32
-rw-r--r--spec/support/permissions.rb28
-rw-r--r--spec/support/pundit/shared_examples.rb30
41 files changed, 374 insertions, 139 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 256967c04..41ae70f56 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -261,7 +261,7 @@ GEM
has_scope (0.7.0)
actionpack (>= 4.1, < 5.1)
activesupport (>= 4.1, < 5.1)
- hashdiff (0.3.2)
+ hashdiff (0.3.4)
highline (1.7.8)
hike (1.2.3)
htmlbeautifier (1.3.1)
@@ -539,7 +539,7 @@ GEM
unicode-display_width (1.1.3)
warden (1.2.7)
rack (>= 1.0)
- webmock (3.0.0)
+ webmock (3.0.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
index 5aade3348..b446e2b37 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/CreateModal.js
@@ -17,7 +17,7 @@ class CreateModal extends Component {
}
render() {
- if(this.props.status.isFetching == true || this.props.status.policy['journey_patterns.edit'] == false) {
+ if(this.props.status.isFetching == true || this.props.status.policy['journey_patterns.update'] == false) {
return false
}
if(this.props.status.fetchSuccess == true) {
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
index 18ed5f889..286cfc454 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/JourneyPattern.js
@@ -34,7 +34,7 @@ class JourneyPattern extends Component{
type='checkbox'
id={sp.id}
checked={sp.checked}
- disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''}
+ disabled={(this.props.value.deletable || this.props.status.policy['journey_patterns.update'] == false) ? 'disabled' : ''}
>
</input>
<span className='radio-label'></span>
@@ -78,7 +78,7 @@ class JourneyPattern extends Component{
<span className='fa fa-cog'></span>
</div>
<ul className='dropdown-menu'>
- <li className={(this.props.value.deletable || this.props.status.policy['journey_patterns.edit'] == false) ? 'disabled' : ''}>
+ <li className={(this.props.value.deletable || this.props.status.policy['journey_patterns.update'] == false) ? 'disabled' : ''}>
<button
type='button'
onClick={this.props.onOpenEditModal}
@@ -91,10 +91,10 @@ class JourneyPattern extends Component{
<li className={this.props.value.object_id ? '' : 'disabled'}>
{this.vehicleJourneyURL(this.props.value.object_id)}
</li>
- <li className={'delete-action' + ((this.props.status.policy['journey_patterns.edit'] == false)? ' disabled' : '')}>
+ <li className={'delete-action' + ((this.props.status.policy['journey_patterns.update'] == false)? ' disabled' : '')}>
<button
type='button'
- disabled={(this.props.status.policy['journey_patterns.edit'] == false)? 'disabled' : ''}
+ disabled={(this.props.status.policy['journey_patterns.update'] == false)? 'disabled' : ''}
onClick={(e) => {
e.preventDefault()
this.props.onDeleteJourneyPattern(this.props.index)}
diff --git a/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js b/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js
index ca266af08..871ba00e1 100644
--- a/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js
+++ b/app/assets/javascripts/es6_browserified/journey_patterns/components/SaveJourneyPattern.js
@@ -9,7 +9,7 @@ class SaveJourneyPattern extends Component{
}
render() {
- if(this.props.status.policy['journey_patterns.edit'] == false) {
+ if(this.props.status.policy['journey_patterns.update'] == false) {
return false
}else{
return (
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js
index 27a82cf48..05dda976d 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/SaveVehicleJourneys.js
@@ -9,7 +9,7 @@ class SaveVehicleJourneys extends Component{
}
render() {
- if(this.props.filters.policy['vehicle_journeys.edit'] == false) {
+ if(this.props.filters.policy['vehicle_journeys.update'] == false) {
return false
}else{
return (
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js
index de370ac1b..f2bd0c3cd 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/VehicleJourney.js
@@ -58,7 +58,7 @@ class VehicleJourney extends Component {
)}
</div>
- {(this.props.filters.policy['vehicle_journeys.edit'] == true) &&
+ {(this.props.filters.policy['vehicle_journeys.update'] == true) &&
<div className={(this.props.value.deletable ? 'disabled ' : '') + 'checkbox'}>
<input
id={this.props.index}
@@ -79,13 +79,13 @@ class VehicleJourney extends Component {
<div className={'cellwrap' + (this.cityNameChecker(vj) ? ' headlined' : '')}>
{this.props.filters.toggleArrivals &&
<div data-headline='Arrivée à'>
- <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false) ? 'disabled ' : '') + 'input-group time'}>
+ <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>
<input
type='number'
min='00'
max='23'
className='form-control'
- disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)}
+ disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false)}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', false, false)}}
value={vj.arrival_time['hour']}
/>
@@ -95,7 +95,7 @@ class VehicleJourney extends Component {
min='00'
max='59'
className='form-control'
- disabled={((this.isDisabled(this.props.value.deletable), vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)}
+ disabled={((this.isDisabled(this.props.value.deletable), vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false)}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'minute', false, false)}}
value={vj.arrival_time['minute']}
/>
@@ -108,13 +108,13 @@ class VehicleJourney extends Component {
}
</div>
<div data-headline='Départ à'>
- <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false) ? 'disabled ' : '') + 'input-group time'}>
+ <span className={((this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false) ? 'disabled ' : '') + 'input-group time'}>
<input
type='number'
min='00'
max='23'
className='form-control'
- disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)}
+ disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false)}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, 'hour', true, this.props.filters.toggleArrivals)}}
value={vj.departure_time['hour']}
/>
@@ -124,7 +124,7 @@ class VehicleJourney extends Component {
min='00'
max='59'
className='form-control'
- disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.edit'] == false)}
+ disabled={(this.isDisabled(this.props.value.deletable, vj.dummy) || this.props.filters.policy['vehicle_journeys.update'] == false)}
onChange={(e) => {this.props.onUpdateTime(e, i, this.props.index, "minute", true, this.props.filters.toggleArrivals)}}
value={vj.departure_time['minute']}
/>
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js
index c1e40b3bc..6a1e8ffb9 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/CreateModal.js
@@ -27,7 +27,7 @@ class CreateModal extends Component {
<li className='st_action'>
<button
type='button'
- disabled={((this.props.filters.policy['vehicle_journeys.edit'] == true) ? '' : 'disabled')}
+ disabled={((this.props.filters.policy['vehicle_journeys.update'] == true) ? '' : 'disabled')}
data-toggle='modal'
data-target='#NewVehicleJourneyModal'
onClick={this.props.onOpenCreateModal}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
index aa1a13b11..34463600a 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/DuplicateVehicleJourney.js
@@ -38,7 +38,7 @@ class DuplicateVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={((actions.getSelected(this.props.vehicleJourneys).length >= 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled')}
+ disabled={((actions.getSelected(this.props.vehicleJourneys).length >= 1 && this.props.filters.policy['vehicle_journeys.update']) ? '' : 'disabled')}
data-toggle='modal'
data-target='#DuplicateVehicleJourneyModal'
onClick={this.props.onOpenDuplicateModal}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
index 9919ee9dd..d49ea578a 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/EditVehicleJourney.js
@@ -32,7 +32,7 @@ class EditVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.update']) ? '' : 'disabled'}
data-toggle='modal'
data-target='#EditVehicleJourneyModal'
onClick={() => this.props.onOpenEditModal(actions.getSelected(this.props.vehicleJourneys)[0])}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
index 4c18ef96f..df6c311e6 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/NotesEditVehicleJourney.js
@@ -59,7 +59,7 @@ class NotesEditVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.update']) ? '' : 'disabled'}
data-toggle='modal'
data-target='#NotesEditVehicleJourneyModal'
onClick={() => this.props.onOpenNotesEditModal(actions.getSelected(this.props.vehicleJourneys)[0])}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
index 2164344c2..269bb1b8c 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/ShiftVehicleJourney.js
@@ -25,7 +25,7 @@ class ShiftVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length == 1 && this.props.filters.policy['vehicle_journeys.update']) ? '' : 'disabled'}
data-toggle='modal'
data-target='#ShiftVehicleJourneyModal'
onClick={this.props.onOpenShiftModal}
diff --git a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
index 82fed23d9..a26a9b805 100644
--- a/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
+++ b/app/assets/javascripts/es6_browserified/vehicle_journeys/components/tools/TimetablesEditVehicleJourney.js
@@ -24,7 +24,7 @@ class TimetablesEditVehicleJourney extends Component {
<li className='st_action'>
<button
type='button'
- disabled={(actions.getSelected(this.props.vehicleJourneys).length > 0 && this.props.filters.policy['vehicle_journeys.edit']) ? '' : 'disabled'}
+ disabled={(actions.getSelected(this.props.vehicleJourneys).length > 0 && this.props.filters.policy['vehicle_journeys.update']) ? '' : 'disabled'}
data-toggle='modal'
data-target='#CalendarsEditVehicleJourneyModal'
onClick={() => this.props.onOpenCalendarsEditModal(actions.getSelected(this.props.vehicleJourneys))}
diff --git a/app/controllers/referentials_controller.rb b/app/controllers/referentials_controller.rb
index 31b953ace..9505a47f3 100644
--- a/app/controllers/referentials_controller.rb
+++ b/app/controllers/referentials_controller.rb
@@ -26,6 +26,7 @@ class ReferentialsController < BreadcrumbController
resource.switch
show! do |format|
@referential = @referential.decorate
+ @reflines = lines_collection.paginate(page: params[:page], per_page: 10)
format.json {
render :json => { :lines_count => resource.lines.count,
@@ -36,8 +37,6 @@ class ReferentialsController < BreadcrumbController
}
format.html { build_breadcrumb :show}
end
-
- @reflines = lines_collection.paginate(page: params[:page], per_page: 10)
end
def edit
diff --git a/app/decorators/referential_decorator.rb b/app/decorators/referential_decorator.rb
index b95b04f9f..ccb47a654 100644
--- a/app/decorators/referential_decorator.rb
+++ b/app/decorators/referential_decorator.rb
@@ -2,6 +2,7 @@ class ReferentialDecorator < Draper::Decorator
delegate_all
def action_links
+ policy = h.policy(object)
links = [
Link.new(
content: h.t('time_tables.index.title'),
@@ -9,40 +10,41 @@ class ReferentialDecorator < Draper::Decorator
)
]
- if h.policy(object).clone?
+ if policy.clone?
links << Link.new(
content: h.t('actions.clone'),
href: h.new_referential_path(from: object.id)
)
end
+ if policy.archive?
+ links << Link.new(
+ content: h.t('actions.archive'),
+ href: h.archive_referential_path(object.id),
+ method: :put
+ )
+ end
- if h.policy(object).edit?
+ if policy.unarchive?
+ links << Link.new(
+ content: h.t('actions.unarchive'),
+ href: h.unarchive_referential_path(object.id),
+ method: :put
+ )
+ end
- if object.archived?
- links << Link.new(
- content: h.t('actions.unarchive'),
- href: h.unarchive_referential_path(object.id),
- method: :put
- )
- else
- links << HTMLElement.new(
- :button,
- 'Purger',
- type: 'button',
- data: {
- toggle: 'modal',
- target: '#purgeModal'
- }
- )
- links << Link.new(
- content: h.t('actions.archive'),
- href: h.archive_referential_path(object.id),
- method: :put
- )
- end
+ if policy.edit?
+ links << HTMLElement.new(
+ :button,
+ 'Purger',
+ type: 'button',
+ data: {
+ toggle: 'modal',
+ target: '#purgeModal'
+ }
+ )
end
- if h.policy(object).destroy? && !object.archived?
+ if policy.destroy?
links << Link.new(
content: h.destroy_link_content,
href: h.referential_path(object),
diff --git a/app/jobs/mailer_job.rb b/app/jobs/mailer_job.rb
index 6d3dc642d..761a29cd6 100644
--- a/app/jobs/mailer_job.rb
+++ b/app/jobs/mailer_job.rb
@@ -2,6 +2,6 @@ class MailerJob < ActiveJob::Base
queue_as :mail
def perform klass, action, params
- klass.constantize.public_send(action, *params).deliver
+ klass.constantize.public_send(action, *params).deliver_later
end
end
diff --git a/app/mailers/calendar_mailer.rb b/app/mailers/calendar_mailer.rb
index 44dcaea88..cc8175a07 100644
--- a/app/mailers/calendar_mailer.rb
+++ b/app/mailers/calendar_mailer.rb
@@ -1,9 +1,11 @@
class CalendarMailer < ApplicationMailer
def updated calendar, user
+ @calendar = calendar
mail to: user.email, subject: t('mailers.calendar_mailer.updated.subject')
end
def created calendar, user
+ @calendar = calendar
mail to: user.email, subject: t('mailers.calendar_mailer.created.subject')
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 5a2e4d3ca..64d66883f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -35,6 +35,9 @@ class User < ActiveRecord::Base
@@edit_offer_permissions =
destructive_permissions_for( %w[
+ access_points
+ connection_links
+ calendars
footnotes
journey_patterns
referentials
@@ -42,7 +45,7 @@ class User < ActiveRecord::Base
routing_constraint_zones
time_tables
vehicle_journeys
- ])
+ ]) << 'boiv:edit-offer'
mattr_reader :edit_offer_permissions
diff --git a/app/policies/calendar_policy.rb b/app/policies/calendar_policy.rb
index d3c715d70..074c41d8d 100644
--- a/app/policies/calendar_policy.rb
+++ b/app/policies/calendar_policy.rb
@@ -6,10 +6,10 @@ class CalendarPolicy < ApplicationPolicy
end
def create?
- !archived? && organisation_match? && user.has_permission?('calendars.create')
+ !archived? && user.has_permission?('calendars.create')
end
def destroy?
- !archived? && organisation_match? && user.has_permission?('calendars.destroy')
+ !archived? & organisation_match? && user.has_permission?('calendars.destroy')
end
def update?
!archived? && organisation_match? && user.has_permission?('calendars.update')
diff --git a/app/policies/referential_policy.rb b/app/policies/referential_policy.rb
index bf970c2b8..9d0a92093 100644
--- a/app/policies/referential_policy.rb
+++ b/app/policies/referential_policy.rb
@@ -24,11 +24,11 @@ class ReferentialPolicy < ApplicationPolicy
end
def archive?
- record.archived_at.nil? && user.has_permission?('referentials.update')
+ record.archived_at.nil? && organisation_match? && user.has_permission?('referentials.update')
end
def unarchive?
- !record.archived_at.nil? && user.has_permission?('referentials.update')
+ !record.archived_at.nil? && organisation_match? && user.has_permission?('referentials.update')
end
def common_lines?
diff --git a/app/views/calendar_mailer/created.html.slim b/app/views/calendar_mailer/created.html.slim
index 621796d34..da15b7189 100644
--- a/app/views/calendar_mailer/created.html.slim
+++ b/app/views/calendar_mailer/created.html.slim
@@ -1,2 +1 @@
-div = t('mailers.calendar_mailer.created.body')
-
+div = t('mailers.calendar_mailer.created.body', cal_name: @calendar.name, cal_index_url: calendars_url)
diff --git a/app/views/calendar_mailer/updated.html.slim b/app/views/calendar_mailer/updated.html.slim
index 7f6deda07..f70480107 100644
--- a/app/views/calendar_mailer/updated.html.slim
+++ b/app/views/calendar_mailer/updated.html.slim
@@ -1,2 +1,2 @@
-div = t('mailers.calendar_mailer.updated.body')
+div = t('mailers.calendar_mailer.updated.body', cal_name: @calendar.name, cal_index_url: calendars_url)
diff --git a/app/views/calendars/index.html.slim b/app/views/calendars/index.html.slim
index 843ec1256..e3ac16505 100644
--- a/app/views/calendars/index.html.slim
+++ b/app/views/calendars/index.html.slim
@@ -1,8 +1,10 @@
/ PageHeader
-= pageheader 'map-marker',
- t('.title'),
- '',
- link_to(t('actions.add'), new_calendar_path, class: 'btn btn-default') do
+
+- header_params = ['map-marker',
+ t('.title'),
+ '']
+- header_params << link_to(t('actions.add'), new_calendar_path, class: 'btn btn-default') if policy(Calendar).create?
+= pageheader(*header_params) do
/ PageContent
.page_content
diff --git a/config/application.rb b/config/application.rb
index 02d2b4fc2..910ddd983 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -33,6 +33,8 @@ module ChouetteIhm
config.active_record.observers = [:route_observer, :calendar_observer]
config.active_record.raise_in_transactional_callbacks = true
+ config.active_job.queue_adapter = :sidekiq
+
unless Rails.env.production?
# Work around sprockets+teaspoon mismatch:
Rails.application.config.assets.precompile += %w(spec_helper.js)
diff --git a/config/locales/mailers.en.yml b/config/locales/mailers.en.yml
index 72fd0d725..d4bd45129 100644
--- a/config/locales/mailers.en.yml
+++ b/config/locales/mailers.en.yml
@@ -3,7 +3,7 @@ en:
calendar_mailer:
created:
subject: A new shared calendar has been created
- body: body created
+ body: A new shared calendar% {cal_name} has been added by STIF. You can now view it in the list of shared calendars %{cal_index_url}
created:
subject: A shared calendar has been updated
- body: body updated
+ body: A new shared calendar% {cal_name} has been updated by STIF. You can now view it in the list of shared calendars %{cal_index_url}
diff --git a/config/locales/mailers.fr.yml b/config/locales/mailers.fr.yml
index 8c399b6e0..a448f13d9 100644
--- a/config/locales/mailers.fr.yml
+++ b/config/locales/mailers.fr.yml
@@ -3,7 +3,7 @@ fr:
calendar_mailer:
created:
subject: Un nouveau calendrier partagé à été ajouté
- body: body created
+ body: 'Un calendrier partagé %{cal_name} a été ajouté par le STIF. Vous pouvez maintenant le consulter dans la liste des calendriers partagés : %{cal_index_url}'
updated:
subject: Un nouveau calendrier partagé à été mise à jour
- body: body updated
+ body: 'Un calendrier partagé %{cal_name} a été mis à jour par le STIF. Vous pouvez maintenant le consulter dans la liste des calendriers partagés : %{cal_index_url}'
diff --git a/lib/html_element.rb b/lib/html_element.rb
index 469fd7565..57b08eb52 100644
--- a/lib/html_element.rb
+++ b/lib/html_element.rb
@@ -1,4 +1,6 @@
class HTMLElement
+ attr_reader :content, :options, :tag_name
+
def initialize(tag_name, content = nil, options = nil)
@tag_name = tag_name
@content = content
diff --git a/lib/stif/permission_translator.rb b/lib/stif/permission_translator.rb
new file mode 100644
index 000000000..7032f910a
--- /dev/null
+++ b/lib/stif/permission_translator.rb
@@ -0,0 +1,7 @@
+module Stif
+ module PermissionTranslator extend self
+ def translate(sso_extra_permissions)
+ %w{sessions:create}
+ end
+ end
+end
diff --git a/spec/decorators/referential_decorator_spec.rb b/spec/decorators/referential_decorator_spec.rb
new file mode 100644
index 000000000..5de6b7e95
--- /dev/null
+++ b/spec/decorators/referential_decorator_spec.rb
@@ -0,0 +1,76 @@
+RSpec.describe ReferentialDecorator, type: [:helper, :decorator] do
+
+ let( :object ){ build_stubbed :referential }
+ let( :referential ){ object }
+ let( :user ){ build_stubbed :user }
+
+ describe 'delegation' do
+ it 'delegates all' do
+ %i{xx xxx anything save!}.each do |method|
+ expect( object ).to receive(method)
+ end
+ # Almost as powerful as Quicktest :P
+ %i{xx xxx anything save!}.each do |method|
+ subject.send method
+ end
+ end
+ end
+
+ describe 'action links for' do
+
+ context 'unarchived referential' do
+ context 'no rights' do
+ it 'has only a Calendar action' do
+ expect_action_link_hrefs.to eq([referential_time_tables_path(object)])
+ end
+ end
+
+ context 'all rights and different organisation' do
+
+ let( :user ){ build_stubbed :allmighty_user }
+
+ it 'has only default actions' do
+ expect_action_link_elements.to be_empty
+ expect_action_link_hrefs.to eq([
+ referential_time_tables_path(object),
+ ])
+ end
+ end
+ context 'all rights and same organisation' do
+
+ let( :user ){ build_stubbed :allmighty_user, organisation: referential.organisation }
+
+ it 'has all actions' do
+ expect_action_link_elements.to eq(%w{Purger})
+ expect_action_link_hrefs.to eq([
+ referential_time_tables_path(object),
+ new_referential_path(from: object),
+ archive_referential_path(object),
+ referential_path(object)
+ ])
+ end
+ end
+ end
+
+ context 'archived referential' do
+ before { referential.archived_at = 42.seconds.ago }
+ context 'no rights' do
+ it 'has only a Calendar action' do
+ expect_action_link_hrefs.to eq([referential_time_tables_path(object)])
+ end
+ end
+
+ context 'all rights and different organisation' do
+ let( :user ){ build_stubbed :allmighty_user }
+ it 'has only default actions' do
+ expect_action_link_elements.to be_empty
+ expect_action_link_hrefs.to eq([
+ referential_time_tables_path(object),
+ ])
+ end
+ end
+ end
+ end
+
+
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index d532cbafc..8f620c3a1 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -1,12 +1,4 @@
-all_permissions = %w[
- footnotes
- journey_patterns
- referentials
- routes
- routing_constraint_zones
- time_tables
- vehicle_journeys
- ].product( %w{create destroy update} ).map{ |model_action| model_action.join('.') }
+require_relative '../support/permissions'
FactoryGirl.define do
factory :user do
@@ -17,7 +9,7 @@ FactoryGirl.define do
password "secret"
password_confirmation "secret"
factory :allmighty_user do
- permissions all_permissions
+ permissions Support::Permissions.all_permissions
end
end
end
diff --git a/spec/features/calendars_permissions_spec.rb b/spec/features/calendars_permissions_spec.rb
index 6eb0ea08e..9b47ab2bb 100644
--- a/spec/features/calendars_permissions_spec.rb
+++ b/spec/features/calendars_permissions_spec.rb
@@ -1,15 +1,13 @@
-# -*- coding: utf-8 -*-
-require 'spec_helper'
-
-describe 'Calendars', type: :feature do
+RSpec.describe 'Calendars', type: :feature do
login_user
let(:calendar) { create :calendar, organisation_id: 1 }
describe 'permissions' do
before do
- allow_any_instance_of(CalendarPolicy).to receive(:edit?).and_return permission
+ allow_any_instance_of(CalendarPolicy).to receive(:create?).and_return permission
allow_any_instance_of(CalendarPolicy).to receive(:destroy?).and_return permission
+ allow_any_instance_of(CalendarPolicy).to receive(:edit?).and_return permission
allow_any_instance_of(CalendarPolicy).to receive(:share?).and_return permission
visit path
end
@@ -51,5 +49,23 @@ describe 'Calendars', type: :feature do
end
end
end
+
+ context 'on index view' do
+ let( :path ){ calendars_path }
+
+ context 'if present → ' do
+ let( :permission ){ true }
+ it 'index shows an edit button' do
+ expect(page).to have_css('a.btn.btn-default', text: 'Créer')
+ end
+ end
+
+ context 'if absent → ' do
+ let( :permission ){ false }
+ it 'index does not show any edit button' do
+ expect(page).not_to have_css('a.btn.btn-default', text: 'Créer')
+ end
+ end
+ end
end
end
diff --git a/spec/features/connection_links_spec.rb b/spec/features/connection_links_spec.rb
index 524fbb89a..0325e6e1c 100644
--- a/spec/features/connection_links_spec.rb
+++ b/spec/features/connection_links_spec.rb
@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
-require 'spec_helper'
-
-describe "ConnectionLinks", :type => :feature do
+RSpec.describe "ConnectionLinks", :type => :feature do
login_user
let!(:connection_links) { Array.new(2) { create(:connection_link) } }
diff --git a/spec/jobs/mailer_job_spec.rb b/spec/jobs/mailer_job_spec.rb
deleted file mode 100644
index 363b8724a..000000000
--- a/spec/jobs/mailer_job_spec.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'rails_helper'
-
-RSpec.describe MailerJob, type: :job do
- pending "add some examples to (or delete) #{__FILE__}"
-end
diff --git a/spec/lib/stif/permission_translator_spec.rb b/spec/lib/stif/permission_translator_spec.rb
new file mode 100644
index 000000000..3672c7937
--- /dev/null
+++ b/spec/lib/stif/permission_translator_spec.rb
@@ -0,0 +1,10 @@
+RSpec.describe Stif::PermissionTranslator do
+
+ context "SSO Permission boiv:read:offer →" do
+
+ it "sessions:create only" do
+ expect( described_class.translate(%w{boiv:read:offer}) ).to eq(%w{sessions:create})
+ end
+
+ end
+end
diff --git a/spec/mailers/calendar_mailer_spec.rb b/spec/mailers/calendar_mailer_spec.rb
index d41092461..49cc3cce8 100644
--- a/spec/mailers/calendar_mailer_spec.rb
+++ b/spec/mailers/calendar_mailer_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe CalendarMailer, type: :mailer do
end
it 'should have correct body' do
- key = I18n.t("mailers.calendar_mailer.#{type}.body")
+ key = I18n.t("mailers.calendar_mailer.#{type}.body", cal_name: calendar.name, cal_index_url: calendars_url)
expect(email).to have_body_text /#{key}/
end
end
diff --git a/spec/mailers/previews/calendar_mailer_preview.rb b/spec/mailers/previews/calendar_mailer_preview.rb
index 572c6c667..5fa108924 100644
--- a/spec/mailers/previews/calendar_mailer_preview.rb
+++ b/spec/mailers/previews/calendar_mailer_preview.rb
@@ -1,4 +1,13 @@
# Preview all emails at http://localhost:3000/rails/mailers/calendar_mailer
class CalendarMailerPreview < ActionMailer::Preview
+ def created
+ cal = Calendar.new(name: 'test calendar', shared: true)
+ CalendarMailer.created(cal, User.take)
+ end
+
+ def updated
+ cal = Calendar.new(name: 'test calendar', shared: true)
+ CalendarMailer.updated(cal, User.take)
+ end
end
diff --git a/spec/policies/calendar_policy_spec.rb b/spec/policies/calendar_policy_spec.rb
index 57f771c54..294be8198 100644
--- a/spec/policies/calendar_policy_spec.rb
+++ b/spec/policies/calendar_policy_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe CalendarPolicy, type: :policy do
permissions :create? do
- it_behaves_like 'permitted policy and same organisation', 'calendars.create', archived: true
+ it_behaves_like 'permitted policy', 'calendars.create', archived: true
end
permissions :destroy? do
it_behaves_like 'permitted policy and same organisation', 'calendars.destroy', archived: true
@@ -14,7 +14,7 @@ RSpec.describe CalendarPolicy, type: :policy do
it_behaves_like 'permitted policy and same organisation', 'calendars.update', archived: true
end
permissions :new? do
- it_behaves_like 'permitted policy and same organisation', 'calendars.create', archived: true
+ it_behaves_like 'permitted policy', 'calendars.create', archived: true
end
permissions :update? do
it_behaves_like 'permitted policy and same organisation', 'calendars.update', archived: true
diff --git a/spec/policies/referential_policy_spec.rb b/spec/policies/referential_policy_spec.rb
index d060317f9..33d8e13e8 100644
--- a/spec/policies/referential_policy_spec.rb
+++ b/spec/policies/referential_policy_spec.rb
@@ -56,22 +56,52 @@ RSpec.describe ReferentialPolicy, type: :policy do
add_permissions('referentials.update', for_user: user)
end
- it 'allowed for unarchived referentials' do
- expect_it.to permit(user_context, record)
+ context 'same organisation →' do
+ before do
+ user.organisation_id = referential.organisation_id
+ end
+ it "allows a user with the same organisation" do
+ expect_it.to permit(user_context, record)
+ end
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'does remove permission for archived referentials' do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
end
- it 'forbidden for archived referentials' do
- record.archived_at = 1.second.ago
- expect_it.not_to permit(user_context, record)
+ context 'different organisations →' do
+ it "forbids a user with a different organisation" do
+ expect_it.not_to permit(user_context, record)
+ end
+
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'forbids for archived referentials' do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
+
end
end
- context 'permission absent →' do
- it 'is forbidden' do
- expect_it.not_to permit(user_context, record)
+ context 'permission absent →' do
+ context 'same organisation →' do
+ before do
+ user.organisation_id = referential.organisation_id
+ end
+ it "forbids a user with the same organisation" do
+ expect_it.not_to permit(user_context, record)
+ end
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'forbids for archived referentials' do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
end
end
-
end
permissions :unarchive? do
@@ -81,22 +111,51 @@ RSpec.describe ReferentialPolicy, type: :policy do
add_permissions('referentials.update', for_user: user)
end
- it 'forbidden for unarchived referentials' do
- expect_it.not_to permit(user_context, record)
+ context 'same organisation →' do
+ before do
+ user.organisation_id = referential.organisation_id
+ end
+ it "forbids a user with the same organisation" do
+ expect_it.not_to permit(user_context, record)
+ end
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'adds permission for archived referentials' do
+ expect_it.to permit(user_context, record)
+ end
+ end
end
- it 'allowed for archived referentials' do
- record.archived_at = 1.second.ago
- expect_it.to permit(user_context, record)
+ context 'different organisations →' do
+ it "forbids a user with a different organisation" do
+ expect_it.not_to permit(user_context, record)
+ end
+
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'still forbids for archived referentials' do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
+
end
end
- context 'permission absent →' do
- it 'is forbidden' do
- record.archived_at = 1.second.ago
- expect_it.not_to permit(user_context, record)
+ context 'permission absent →' do
+ context 'same organisation →' do
+ before do
+ user.organisation_id = referential.organisation_id
+ end
+ it "forbids a user with a different rganisation" do
+ expect_it.not_to permit(user_context, record)
+ end
+ describe "archived" do
+ let( :record ){ build_stubbed :referential, archived_at: 2.minutes.ago }
+ it 'still forbids for archived referentials' do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
end
end
-
end
end
diff --git a/spec/support/decorator_helpers.rb b/spec/support/decorator_helpers.rb
new file mode 100644
index 000000000..ffedd479b
--- /dev/null
+++ b/spec/support/decorator_helpers.rb
@@ -0,0 +1,27 @@
+module Support
+
+ module DecoratorHelpers
+ def self.included(into)
+ into.instance_eval do
+ subject{ object.decorate }
+ let( :policy ){ ::Pundit.policy(user_context, object) }
+ let( :user_context ){ UserContext.new(user, referential: referential) }
+
+ before do
+ allow_any_instance_of(Draper::HelperProxy).to receive(:policy).and_return policy
+ end
+ end
+ end
+
+ def expect_action_link_hrefs
+ expect( subject.action_links.select(&Link.method(:===)).map(&:href) )
+ end
+ def expect_action_link_elements
+ expect( subject.action_links.select(&HTMLElement.method(:===)).map(&:content) )
+ end
+ end
+end
+
+RSpec.configure do | c |
+ c.include Support::DecoratorHelpers, type: :decorator
+end
diff --git a/spec/support/devise.rb b/spec/support/devise.rb
index 28703c072..46249fef2 100644
--- a/spec/support/devise.rb
+++ b/spec/support/devise.rb
@@ -3,13 +3,11 @@ module DeviseRequestHelper
def login_user
organisation = Organisation.where(:code => "first").first_or_create(attributes_for(:organisation))
- @user ||= create(:user, :organisation => organisation,
- :permissions => ['routes.create', 'routes.update', 'routes.destroy', 'journey_patterns.create', 'journey_patterns.update', 'journey_patterns.destroy',
- 'vehicle_journeys.create', 'vehicle_journeys.update', 'vehicle_journeys.destroy', 'time_tables.create', 'time_tables.update', 'time_tables.destroy',
- 'footnotes.update', 'footnotes.create', 'footnotes.destroy', 'routing_constraint_zones.create', 'routing_constraint_zones.update', 'routing_constraint_zones.destroy',
- 'access_points.create', 'access_points.update', 'access_points.destroy', 'access_links.create', 'access_links.update', 'access_links.destroy',
- 'connection_links.create', 'connection_links.update', 'connection_links.destroy', 'route_sections.create', 'route_sections.update', 'route_sections.destroy',
- 'referentials.create', 'referentials.update', 'referentials.destroy'])
+ @user ||=
+ create(:user,
+ :organisation => organisation,
+ :permissions => Support::Permissions.all_permissions)
+
login_as @user, :scope => :user
# post_via_redirect user_session_path, 'user[email]' => @user.email, 'user[password]' => @user.password
end
@@ -38,28 +36,12 @@ end
module DeviseControllerHelper
def setup_user
- _all_actions = %w{create destroy update}
- _all_resources = %w{ access_links
- access_points
- connection_links
- footnotes
- journey_patterns
- referentials
- route_sections
- routes
- routing_constraint_zones
- time_tables
- vehicle_journeys }
- join_with = -> (separator) do
- -> (ary) { ary.join(separator) }
- end
-
before do
@request.env["devise.mapping"] = Devise.mappings[:user]
organisation = Organisation.where(:code => "first").first_or_create(attributes_for(:organisation))
@user = create(:user,
organisation: organisation,
- permissions: _all_resources.product( _all_actions ).map(&join_with.('.')))
+ permissions: Support::Permissions.all_permissions)
end
end
@@ -70,8 +52,6 @@ module DeviseControllerHelper
end
end
- private
-
end
RSpec.configure do |config|
diff --git a/spec/support/permissions.rb b/spec/support/permissions.rb
new file mode 100644
index 000000000..a13010f65
--- /dev/null
+++ b/spec/support/permissions.rb
@@ -0,0 +1,28 @@
+module Support
+ module Permissions extend self
+
+ def all_permissions
+ @__all_permissions__ ||= _destructive_permissions << 'sessions:create'
+ end
+
+ private
+
+ def _destructive_permissions
+ _permitted_resources.product( %w{create destroy update} ).map{ |model_action| model_action.join('.') }
+ end
+
+ def _permitted_resources
+ %w[
+ access_points
+ connection_links
+ footnotes
+ journey_patterns
+ referentials
+ routes
+ routing_constraint_zones
+ time_tables
+ vehicle_journeys
+ ]
+ end
+ end
+end
diff --git a/spec/support/pundit/shared_examples.rb b/spec/support/pundit/shared_examples.rb
index b91caa479..63a106759 100644
--- a/spec/support/pundit/shared_examples.rb
+++ b/spec/support/pundit/shared_examples.rb
@@ -64,7 +64,7 @@ RSpec.shared_examples 'always forbidden' do
end
end
end
-j
+
RSpec.shared_examples 'permitted policy and same organisation' do
| permission, archived: false|
@@ -101,3 +101,31 @@ RSpec.shared_examples 'permitted policy and same organisation' do
end
end
end
+
+RSpec.shared_examples 'permitted policy' do
+ | permission, archived: false|
+
+ context 'permission absent → ' do
+ it "denies user" do
+ expect_it.not_to permit(user_context, record)
+ end
+ end
+
+ context 'permission present → ' do
+ before do
+ add_permissions(permission, for_user: user)
+ end
+
+ it 'allows user' do
+ expect_it.to permit(user_context, record)
+ end
+
+ if archived
+ it 'removes the permission for archived referentials' do
+ user.organisation_id = referential.organisation_id
+ referential.archived_at = 42.seconds.ago
+ expect_it.not_to permit(user_context, record)
+ end
+ end
+ end
+end