Implement track requests and add the track organizer role

About track requests:
Create migration that adds the fields submitter_id, state, and
cfp_active to Tracks
Add validations and the self_organized? method to the Track model
Create a new TracksController outide of the admin namespace
Create the relevant views for index, show, new and edit
Modify the admin views for tracks to include extra info for
self-organized tracks

About track organizers:
Create the role when a self-organized track is created
Define track organizer abilities
Modify the roles views and controller to handle the new role

The route for Roles#edit needs to have higher priority than the nested
routes for track roles, otherwise, the word edit in the url is matched
as a track with short_name edit
This commit is contained in:
AEtherC0r3 2017-06-08 12:25:33 +03:00 committed by Stella Rouzi
parent 98fc1137e1
commit 68fc750e9f
26 changed files with 766 additions and 27 deletions

View file

@ -172,6 +172,9 @@ linters:
- "app/views/users/edit.html.haml"
- "app/views/users/show.html.haml"
- "app/views/admin/cfps/index.html.haml"
- "app/views/tracks/_form.html.haml"
- "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml"
# Offense count: 223
InstanceVariables:
@ -231,6 +234,7 @@ linters:
- "app/views/schedules/_schedule_item.html.haml"
- "app/views/schedules/_schedule_tabs.html.haml"
- "app/views/admin/cfps/_events_cfp.html.haml"
- "app/views/tracks/_form.html.haml"
# Offense count: 32
IdNames:
@ -330,6 +334,8 @@ linters:
- "app/views/shared/_object_changes.html.haml"
- "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml"
# Offense count: 23
ClassesBeforeIds:

View file

@ -30,3 +30,4 @@ Metrics/BlockLength:
Exclude:
- 'spec/models/conference_spec.rb'
- 'spec/features/ability_spec.rb'
- 'spec/models/ability_spec.rb'

View file

@ -94,6 +94,8 @@ Metrics/ModuleLength:
# Offense count: 14
Metrics/PerceivedComplexity:
Max: 15
Exclude:
- 'app/controllers/admin/roles_controller.rb'
# Offense count: 11
# Cop supports --auto-correct.
@ -850,6 +852,7 @@ Style/SymbolProc:
- 'app/controllers/admin/questions_controller.rb'
- 'app/helpers/application_helper.rb'
- 'app/models/ability.rb'
- 'app/models/admin_ability.rb'
- 'db/migrate/20140730104658_migrate_roles_for_cancancan.rb'
- 'spec/controllers/admin/conferences_controller_spec.rb'
- 'spec/support/flash.rb'

View file

@ -15,7 +15,8 @@ module Admin
end
unless (current_user.has_role? :organizer, :any) || (current_user.has_role? :cfp, :any) ||
(current_user.has_role? :info_desk, :any) || (current_user.has_role? :organization_admin, :any) ||
(current_user.has_role? :volunteers_coordinator, :any) || current_user.is_admin
(current_user.has_role? :volunteers_coordinator, :any) ||
(current_user.has_role? :track_organizer, :any) || current_user.is_admin
raise CanCan::AccessDenied.new('You are not authorized to access this page.')
end
end

View file

@ -8,14 +8,26 @@ module Admin
def index
@roles = Role.where(resource: @conference)
tracks = @conference.program.tracks.where.not(submitter: nil)
@roles += Role.where(resource: tracks)
authorize! :index, @role
end
def show
@url = if @track
toggle_user_track_admin_conference_role_path(@conference.short_title, @role.name, @track.name.tr(' ', '_'))
else
toggle_user_admin_conference_role_path(@conference.short_title, @role.name)
end
@users = @role.users
end
def edit
@url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track.name.tr(' ', '_'))
else
admin_conference_role_path(@conference.short_title, @role.name)
end
@users = @role.users
end
@ -23,7 +35,13 @@ module Admin
role_name = @role.name
if @role.update_attributes(role_params)
redirect_to admin_conference_role_path(@conference.short_title, @role.name),
url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track.name.tr(' ', '_'))
else
admin_conference_role_path(@conference.short_title, @role.name)
end
redirect_to url,
notice: 'Successfully updated role ' + @role.name
else
@role.name = role_name
@ -36,8 +54,14 @@ module Admin
user = User.find_by(email: user_params[:email])
state = user_params[:state]
url = if @track
track_admin_conference_role_path(@conference.short_title, @role.name, @track.name.tr(' ', '_'))
else
admin_conference_role_path(@conference.short_title, @role.name)
end
unless user
redirect_to admin_conference_role_path(@conference.short_title, @role.name),
redirect_to url,
error: 'Could not find user. Please provide a valid email!'
return
end
@ -49,17 +73,23 @@ module Admin
return
end
if @role.resource_type == 'Conference'
role_resource = @conference
elsif @role.resource_type == 'Track'
role_resource = @track
end
# Remove user
if state == 'false'
if user.remove_role @role.name, @conference
if user.remove_role @role.name, role_resource
flash[:notice] = "Successfully removed role #{@role.name} from user #{user.email}"
else
flash[:error] = "Could not remove role #{@role.name} from user #{user.email}"
end
elsif user.has_role? @role.name, @conference
elsif user.has_role? @role.name, role_resource
flash[:error] = "User #{user.email} already has the role #{@role.name}"
# Add user
elsif user.add_role @role.name, @conference
elsif user.add_role @role.name, role_resource
flash[:notice] = "Successfully added role #{@role.name} to user #{user.email}"
else
flash[:error] = "Coud not add role #{@role.name} to #{user.email}"
@ -67,7 +97,7 @@ module Admin
respond_to do |format|
format.js
format.html { redirect_to admin_conference_role_path(@conference.short_title, @role.name) }
format.html { redirect_to url }
end
end
@ -77,8 +107,13 @@ module Admin
# Set 'organizer' as default role, when there is no other selection
@selection = params[:id] ? params[:id].parameterize.underscore : 'organizer'
if @selection == 'track_organizer'
@track = @conference.program.tracks.find_by(short_name: params[:track_name])
@role = Role.find_by(name: @selection, resource: @track)
else
@role = Role.find_by(name: @selection, resource: @conference)
end
end
def role_params
params.require(:role).permit(:name, :description, user_ids: [])

View file

@ -50,10 +50,19 @@ module Admin
end
end
def toggle_cfp_inclusion
@track.cfp_active = !@track.cfp_active
if @track.save
head :ok
else
head :unprocessable_entity
end
end
private
def track_params
params.require(:track).permit(:name, :description, :color, :short_name)
params.require(:track).permit(:name, :description, :color, :short_name, :cfp_active)
end
end
end

View file

@ -0,0 +1,47 @@
class TracksController < ApplicationController
load_resource :conference, find_by: :short_title
load_resource :program, through: :conference, singleton: true
load_and_authorize_resource through: :program, find_by: :short_name
def index
@tracks = current_user.tracks.where(program: @program)
end
def show; end
def new
@track = @program.tracks.new(color: @conference.next_color_for_collection(:tracks))
end
def edit; end
def create
@track = @program.tracks.new(track_params)
@track.submitter = current_user
@track.state = 'new'
@track.cfp_active = false
if @track.save
redirect_to conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track request successfully created.'
else
flash.now[:error] = "Creating Track request failed: #{@track.errors.full_messages.join('. ')}."
render :new
end
end
def update
if @track.update_attributes(track_params)
redirect_to admin_conference_program_tracks_path(conference_id: @conference.short_title),
notice: 'Track request successfully updated.'
else
flash.now[:error] = "Track request update failed: #{@track.errors.full_messages.join('. ')}."
render :edit
end
end
private
def track_params
params.require(:track).permit(:name, :description, :color, :short_name)
end
end

View file

@ -70,6 +70,11 @@ class AdminAbility
cannot :destroy, Venue do |venue|
venue.conference.program.events.where.not(room_id: nil).any?
end
# Prevent requests for tracks from being destroyed
cannot :destroy, Track do |track|
track.self_organized?
end
end
# Abilities for signed in users with roles
@ -79,6 +84,7 @@ class AdminAbility
signed_in_with_cfp_role(user) if user.has_role? :cfp, :any
signed_in_with_info_desk_role(user) if user.has_role? :info_desk, :any
signed_in_with_volunteers_coordinator_role(user) if user.has_role? :volunteers_coordinator, :any
signed_in_with_track_organizer_role(user) if user.has_role? :track_organizer, :any
common_abilities_for_roles(user)
end
@ -100,6 +106,9 @@ class AdminAbility
# ids of all the conferences for which the user has the 'organizer' role and
# conferences that belong to organizations for which user is 'organization_admin'
conf_ids = conf_ids_for_organization_admin.concat(Conference.with_role(:organizer, user).pluck(:id)).uniq
# ids of all the tracks that belong to the programs of the above conferences
track_ids = Track.joins(:program).where('programs.conference_id IN (?)', conf_ids).pluck(:id)
can :manage, Resource, conference_id: conf_ids
can [:read, :update, :destroy], Conference, id: conf_ids
can :manage, Splashpage, conference_id: conf_ids
@ -140,11 +149,12 @@ class AdminAbility
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference'
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
can [:edit, :update, :toggle_user], Role do |role|
role.resource_type == 'Conference' && (conf_ids.include? role.resource_id)
role.resource_type == 'Conference' && (conf_ids.include? role.resource_id) ||
role.resource_type == 'Track' && (track_ids.include? role.resource_id)
end
can [:index, :revert_object, :revert_attribute], PaperTrail::Version do |version|
@ -178,7 +188,7 @@ class AdminAbility
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference'
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
@ -211,7 +221,7 @@ class AdminAbility
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference'
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
@ -234,7 +244,7 @@ class AdminAbility
# Abilities for Role (Conference resource)
can [:index, :show], Role do |role|
role.resource_type == 'Conference'
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
# Can add or remove users from role, when user has that same role for the conference
# Eg. If you are member of the CfP team, you can add more CfP team members (add users to the role 'CfP')
@ -243,4 +253,34 @@ class AdminAbility
(Conference.with_role(:volunteers_coordinator, user).pluck(:id).include? role.resource_id)
end
end
def signed_in_with_track_organizer_role(user)
# ids of all the conferences for which the user has the 'track organizer' role
conf_ids_for_track_organizer = Track.with_role(:track_organizer, user).joins(:program).pluck(:conference_id)
# ids of all the tracks for which the user has the 'track_organizer' role
track_ids_for_track_organizer = Track.with_role(:track_organizer, user).pluck(:id)
can :show, Conference do |conf|
conf_ids_for_track_organizer.include?(conf.id)
end
# Show Program in the admin sidebar
can :show, Program, conference_id: conf_ids_for_track_organizer
# Show Tracks in the admin sidebar
can :update, Track do |track|
track.new_record? && conf_ids_for_track_organizer.include?(track.program.conference_id)
end
can :manage, Track, id: track_ids_for_track_organizer
# Show Roles in the admin sidebar and allow authorization of the index action
can [:index, :show], Role do |role|
role.resource_type == 'Conference' || role.resource_type == 'Track'
end
can :toggle_user, Role do |role|
role.resource_type == 'Track' && track_ids_for_track_organizer.include?(role.resource_id)
end
end
end

View file

@ -1,6 +1,10 @@
class Track < ActiveRecord::Base
include RevisionCount
resourcify :roles, dependent: :delete_all
belongs_to :program
belongs_to :submitter, class_name: 'User'
has_many :events, dependent: :nullify
has_paper_trail only: [:name, :description, :color], meta: { conference_id: :conference_id }
@ -14,13 +18,27 @@ class Track < ActiveRecord::Base
uniqueness: {
scope: :program
}
validates :state, presence: true, if: :self_organized?
validates :cfp_active, inclusion: { in: [true, false] }, if: :self_organized?
before_validation :capitalize_color
after_create :create_organizer_role, if: :self_organized?
def conference
program.conference
end
##
# Checks if the track is self-organized
# ====Returns
# * +true+ -> If the track has a submitter
# * +false+ -> if the track doesn't have a submitter
def self_organized?
return true if submitter
false
end
private
def generate_guid
@ -38,4 +56,10 @@ class Track < ActiveRecord::Base
def conference_id
program.conference_id
end
##
# Creates the role of the track organizer
def create_organizer_role
Role.where(name: 'track_organizer', resource: self).first_or_create(description: 'For the organizers of the Track')
end
end

View file

@ -55,6 +55,7 @@ class User < ActiveRecord::Base
has_many :votes, dependent: :destroy
has_many :voted_events, through: :votes, source: :events
has_many :subscriptions, dependent: :destroy
has_many :tracks, foreign_key: 'submitter_id'
accepts_nested_attributes_for :roles
scope :admin, -> { where(is_admin: true) }

View file

@ -7,7 +7,7 @@
.text-muted
= @role.description
= semantic_form_for @role, url: admin_conference_role_path(@conference.short_title, @role.name) do |f|
= semantic_form_for @role, url: @url do |f|
.row
.col-md-5
= f.input :description

View file

@ -14,7 +14,7 @@
- if ( can? :toggle_user, @role )
%td.text-right
= hidden_field_tag "role[user_ids][]", nil
= check_box_tag @conference.short_title, @role.id, (@role.user_ids.include? user.id), method: :post, url: "/admin/conferences/#{@conference.short_title}/roles/#{@role.name}/toggle_user?user[email]=#{user.email}&user[state]=", class: 'switch-checkbox', data: { size: 'small', off_color: 'warning', on_text: 'Yes', off_text: 'No' }
= check_box_tag @conference.short_title, @role.id, (@role.user_ids.include? user.id), method: :post, url: "#{@url}?user[email]=#{user.email}&user[state]=", class: 'switch-checkbox', data: { size: 'small', off_color: 'warning', on_text: 'Yes', off_text: 'No' }
%td= user.id
%td= user.name
%td= user.email

View file

@ -19,13 +19,23 @@
%tr
%td= role.id
%td= role.name.titleize
%td= role.description
%td
= role.description
- if role.resource_type == 'Track'
- track = Track.find(role.resource_id)
= link_to track.name, admin_conference_program_track_path(@conference.short_title, track)
%td
= role.users.pluck(:name).first(5).join ', '
- if role.users.count > 5
= link_to '...', admin_conference_role_path(@conference.short_title, role.name)
%td
.btn-group
- if role.resource_type == 'Track'
- track_name = Track.find(role.resource_id).short_name
= link_to 'Users', track_admin_conference_role_path(@conference.short_title, role.name, track_name), class: 'btn btn-success'
- if can? :edit, role
= link_to 'Edit', track_edit_admin_conference_role_path(@conference.short_title, role.name, track_name), class: 'btn btn-primary'
- else
= link_to 'Users', admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-success'
- if can? :edit, role
= link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, role.name), class: 'btn btn-primary'

View file

@ -6,14 +6,20 @@
Role
= @role.name.titleize
- if can? :edit, @role
- if @track
= link_to 'Edit', track_edit_admin_conference_role_path(@conference.short_title, @role.name, @track.short_name), class: 'btn btn-primary pull-right'
- else
= link_to 'Edit', edit_admin_conference_role_path(@conference.short_title, @role.name), class: 'btn btn-primary pull-right'
.text-muted
= @role.description
- if @track
= link_to @track.name, admin_conference_program_track_path(@conference.short_title, @track)
.row.col-md-3
- if ( can? :toggle_user, @role ) && !@role.new_record?
= semantic_form_for :user, url: toggle_user_admin_conference_role_path(@conference.short_title, @role.name), method: :post do |u|
= semantic_form_for :user, url: @url, method: :post do |u|
= u.label 'Add user by email: '
.input-group

View file

@ -13,4 +13,6 @@
= f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' }
= f.input :color, input_html: {size: 6, type: 'color'}, required: true
= f.input :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, hint: markdown_hint
- if @track.self_organized?
= f.input :cfp_active, label: 'Allow event submitters to select this track for their proposal'
= f.action :submit, as: :button, button_html: { class: 'btn btn-primary' }

View file

@ -11,7 +11,10 @@
%th Name
%th Short name
%th Description
%th Submitter
%th Color
%th State
%th Included in the Cfp
%th Actions
%tbody
- @tracks.each do |track|
@ -24,9 +27,31 @@
%td
%p
= truncate(track.description)
%td
- if track.self_organized?
= link_to track.submitter.name, admin_user_path(track.submitter)
- else
N/A
%td
%span.label{style: "background-color: #{track.color}; color: #{ contrast_color(track.color) }"}
= track.color
%td
- if track.self_organized?
= track.state
- else
N/A
%td
- if track.self_organized?
= check_box_tag "#{@conference.short_title}_#{track.id}", track.id, track.cfp_active,
class: 'switch-checkbox', method: :patch,
url: toggle_cfp_inclusion_admin_conference_program_track_path(@conference.short_title, id: track.id)+"?included=",
data: { size: 'small',
on_color: 'success',
off_color: 'warning',
on_text: 'Yes',
off_text: 'No' }
- else
%i.fa.fa-check
%td
.btn-group{role: "group"}
= link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track.short_name),

View file

@ -0,0 +1,17 @@
.container
.row
.col-md-12
.page-header
%h1
- if @track.new_record?
New
= @track.name
Track
.row
.col-md-12
= semantic_form_for(@track, url: (@track.new_record? ? conference_program_tracks_path : conference_program_track_path(@conference.short_title, @track.short_name))) do |f|
= f.input :name
= f.input :short_name, hint: "A short and unique handle for the track, using only letters, numbers, underscores, and dashes. This will be used to identify the track in URLs etc. Example: 'my_awesome_track'", input_html: { required: 'required', pattern: '[a-zA-Z0-9_-]+', title: 'Only letters, numbers, underscores, and dashes.' }
= f.input :color, input_html: {size: 6, type: 'color'}, required: true
= f.input :description, input_html: {rows: 2, data: { provide: 'markdown-editable' } }, required: true, hint: markdown_hint
= f.action :submit, as: :button, button_html: { class: 'btn btn-primary' }

View file

@ -0,0 +1,43 @@
.container
.row
.col-md-12.page-header
%h1
Track requests for
%span.notranslate
= @conference.title
- if @tracks.any?
.row
.col-md-12
%table.table.table-hover#tracks
%thead
%th Name
%th Short name
%th Description
%th Color
%th State
%th Actions
%tbody
- @tracks.each do |track|
%tr
%td
= link_to(conference_program_track_path(@conference.short_title, track.short_name)) do
= track.name
%td
= track.short_name
%td
%p
= truncate(track.description)
%td
%span.label{style: "background-color: #{track.color}; color: #{ contrast_color(track.color) }"}
= track.color
%td
= track.state
%td
= link_to 'Edit', edit_conference_program_track_path(@conference.short_title, track.short_name),
method: :get, class: 'btn btn-primary'
.row
.col-md-12
- if can? :create, @track
= link_to "New Track request", new_conference_program_track_path(@conference.short_title), class: 'btn btn-success pull-right'

View file

@ -0,0 +1,26 @@
.container
.row
.col-md-12
.page-header
%h1
= @track.name
Track
.row
.col-md-8
%dl.dl-horizontal
%dt
Color:
%dd
%span.label{style: "background-color: #{@track.color}; color: #{ contrast_color(@track.color) }"}
= @track.color
%dt
State:
%dd
= @track.state
%dt
Description
%dd
= @track.description
.row
.col-md-12.text-right
= link_to 'Edit Track request', edit_conference_program_track_path(@conference.short_title, @track.short_name), class: 'btn btn-primary'

View file

@ -54,7 +54,11 @@ Osem::Application.routes.draw do
resource :registration_period
resource :program do
resources :cfps
resources :tracks
resources :tracks do
member do
patch :toggle_cfp_inclusion
end
end
resources :event_types
resources :difficulty_levels
resources :events do
@ -82,9 +86,15 @@ Osem::Application.routes.draw do
resources :campaigns, except: [:show]
resources :emails, only: [:show, :update, :index]
resources :physical_ticket, only: [:index]
resources :roles, except: [ :new, :create ] do
resources :roles, only: [:edit]
resources :roles, except: [ :new, :create, :edit ] do
member do
post :toggle_user
get ':track_name' => 'roles#show', as: 'track'
get ':track_name/edit' => 'roles#edit', as: 'track_edit'
patch ':track_name' => 'roles#update'
put ':track_name' => 'roles#update'
post ':track_name/toggle_user' => 'roles#toggle_user', as: 'toggle_user_track'
end
end
@ -120,6 +130,7 @@ Osem::Application.routes.draw do
patch '/restart' => 'proposals#restart'
end
end
resources :tracks, except: :destroy
end
# TODO: change conference_registrations to singular resource

View file

@ -0,0 +1,8 @@
class AddStateCfpActiveAndSubmitterReferenceToTracks < ActiveRecord::Migration
def change
add_column :tracks, :state, :string
add_column :tracks, :cfp_active, :boolean
add_column :tracks, :submitter_id, :integer
add_index :tracks, :submitter_id
end
end

View file

@ -491,8 +491,13 @@ ActiveRecord::Schema.define(version: 20170711102511) do
t.datetime "updated_at"
t.integer "program_id"
t.string "short_name", null: false
t.string "state"
t.boolean "cfp_active"
t.integer "submitter_id"
end
add_index "tracks", ["submitter_id"], name: "index_tracks_on_submitter_id"
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false

View file

@ -5,5 +5,11 @@ FactoryGirl.define do
color { Faker::Color.hex_color }
short_name { SecureRandom.urlsafe_base64(5) }
program
trait :self_organized do
association :submitter, factory: :user
state 'new'
cfp_active false
end
end
end

View file

@ -0,0 +1,244 @@
require 'spec_helper'
feature 'Has correct abilities' do
let(:organization) { create(:organization) }
let(:conference) { create(:full_conference, organization: organization) }
let(:self_organized_track) { create(:track, :self_organized, program: conference.program) }
let(:role_track_organizer) { Role.find_by(name: 'track_organizer', resource: self_organized_track) }
let(:user_track_organizer) { create(:user, role_ids: [role_track_organizer.id]) }
context 'when user is info desk' do
before do
sign_in user_track_organizer
end
scenario 'for organization and conference attributes' do
visit admin_conference_path(conference.short_title)
expect(current_path).to eq(admin_conference_path(conference.short_title))
expect(page).to have_selector('li.nav-header.nav-header-bigger a', text: 'Dashboard')
expect(page).to_not have_link('Basics', href: "/admin/conferences/#{conference.short_title}/edit")
expect(page).to have_text('Basics')
expect(page).to_not have_link('Contact', href: "/admin/conferences/#{conference.short_title}/contact/edit")
expect(page).to have_link('Commercials', href: "/admin/conferences/#{conference.short_title}/commercials")
expect(page).to_not have_link('Splashpage', href: "/admin/conferences/#{conference.short_title}/splashpage")
expect(page).to_not have_link('Venue', href: "/admin/conferences/#{conference.short_title}/venue")
expect(page).to_not have_link('Rooms', href: "/admin/conferences/#{conference.short_title}/venue/rooms")
expect(page).to_not have_link('Lodgings', href: "/admin/conferences/#{conference.short_title}/lodgings")
expect(page).to have_link('Program', href: "/admin/conferences/#{conference.short_title}/program")
expect(page).to_not have_link('Call for Papers', href: "/admin/conferences/#{conference.short_title}/program/cfps")
expect(page).to_not have_link('Events', href: "/admin/conferences/#{conference.short_title}/program/events")
expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks")
expect(page).to_not have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types")
expect(page).to_not have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels")
expect(page).to_not have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules")
expect(page).to_not have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports")
expect(page).to_not have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations")
expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period")
expect(page).to_not have_link('Questions', href: "/admin/conferences/#{conference.short_title}/questions")
expect(page).to_not have_text('Donations')
expect(page).to_not have_link('Sponsorship Levels', href: "/admin/conferences/#{conference.short_title}/sponsorship_levels")
expect(page).to_not have_link('Sponsors', href: "/admin/conferences/#{conference.short_title}/sponsors")
expect(page).to_not have_link('Tickets', href: "/admin/conferences/#{conference.short_title}/tickets")
expect(page).to_not have_text('Objectives')
expect(page).to_not have_link('Campaigns', href: "/admin/conferences/#{conference.short_title}/campaigns")
expect(page).to_not have_link('Goals', href: "/admin/conferences/#{conference.short_title}/targets")
expect(page).to_not have_link('E-Mails', href: "/admin/conferences/#{conference.short_title}/emails")
expect(page).to have_link('Roles', href: "/admin/conferences/#{conference.short_title}/roles")
expect(page).to_not have_link('Resources', href: "/admin/conferences/#{conference.short_title}/resources")
expect(page).to_not have_link('New Conference', href: '/admin/conferences/new')
visit edit_admin_conference_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_contact_path(conference.short_title)
expect(current_path).to eq root_path
visit admin_conference_commercials_path(conference.short_title)
expect(current_path).to eq admin_conference_commercials_path(conference.short_title)
visit new_admin_conference_splashpage_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_splashpage_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_venue_path(conference.short_title)
expect(current_path).to eq root_path
conference.venue = create(:venue)
visit edit_admin_conference_venue_path(conference.short_title)
expect(current_path).to eq root_path
visit admin_conference_venue_rooms_path(conference.short_title)
expect(current_path).to eq root_path
create(:room, venue: conference.venue)
visit edit_admin_conference_venue_room_path(conference.short_title, conference.venue.rooms.first)
expect(current_path).to eq root_path
visit admin_conference_lodgings_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_lodging_path(conference.short_title)
expect(current_path).to eq root_path
create(:lodging, conference: conference)
visit edit_admin_conference_lodging_path(conference.short_title, conference.lodgings.first)
expect(current_path).to eq root_path
visit new_admin_conference_program_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_program_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_program_cfp_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_program_cfp_path(conference.short_title, conference.program.cfp)
expect(current_path).to eq root_path
visit admin_conference_program_events_path(conference.short_title)
expect(current_path).to eq admin_conference_program_events_path(conference.short_title)
create(:event, program: conference.program)
visit edit_admin_conference_program_event_path(conference.short_title, conference.program.events.first)
expect(current_path).to eq root_path
visit admin_conference_program_event_types_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_program_event_type_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_program_event_type_path(conference.short_title, conference.program.event_types.first)
expect(current_path).to eq root_path
visit admin_conference_program_difficulty_levels_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_program_difficulty_level_path(conference.short_title)
expect(current_path).to eq root_path
visit edit_admin_conference_program_difficulty_level_path(conference.short_title, conference.program.difficulty_levels.first)
expect(current_path).to eq root_path
visit admin_conference_schedules_path(conference.short_title)
expect(current_path).to eq root_path
create(:schedule, program: conference.program)
visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first)
expect(current_path).to eq root_path
visit admin_conference_program_reports_path(conference.short_title)
expect(current_path).to eq admin_conference_program_reports_path(conference.short_title)
visit admin_conference_registrations_path(conference.short_title)
expect(current_path).to eq admin_conference_registrations_path(conference.short_title)
create(:registration, user: create(:user), conference: conference)
visit edit_admin_conference_registration_path(conference.short_title, conference.registrations.first)
expect(current_path).to eq root_path
visit new_admin_conference_registration_period_path(conference.short_title)
expect(current_path).to eq root_path
create(:registration_period, conference: conference)
visit edit_admin_conference_registration_period_path(conference.short_title)
expect(current_path).to eq root_path
visit admin_conference_questions_path(conference.short_title)
expect(current_path).to eq root_path
visit admin_conference_sponsorship_levels_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_sponsorship_level_path(conference.short_title)
expect(current_path).to eq root_path
create(:sponsorship_level, conference: conference)
visit edit_admin_conference_sponsorship_level_path(conference.short_title, conference.sponsorship_levels.first)
expect(current_path).to eq root_path
visit admin_conference_sponsors_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_sponsor_path(conference.short_title)
expect(current_path).to eq root_path
create(:sponsor, conference: conference, sponsorship_level: conference.sponsorship_levels.first)
visit edit_admin_conference_sponsor_path(conference.short_title, conference.sponsors.first)
expect(current_path).to eq root_path
visit admin_conference_tickets_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_ticket_path(conference.short_title)
expect(current_path).to eq root_path
create(:ticket, conference: conference)
visit edit_admin_conference_ticket_path(conference.short_title, conference.tickets.first)
expect(current_path).to eq root_path
visit admin_conference_campaigns_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_campaign_path(conference.short_title)
expect(current_path).to eq root_path
create(:campaign, conference: conference)
visit edit_admin_conference_campaign_path(conference.short_title, conference.campaigns.first)
expect(current_path).to eq root_path
visit admin_conference_targets_path(conference.short_title)
expect(current_path).to eq root_path
visit new_admin_conference_target_path(conference.short_title)
expect(current_path).to eq root_path
create(:target, conference: conference)
visit edit_admin_conference_target_path(conference.short_title, conference.targets.first)
expect(current_path).to eq root_path
visit admin_conference_program_tracks_path(conference.short_title)
expect(current_path).to eq admin_conference_program_tracks_path(conference.short_title)
visit new_admin_conference_program_track_path(conference.short_title)
expect(current_path).to eq root_path
other_track = create(:track, program: conference.program)
visit admin_conference_program_track_path(conference.short_title, other_track.short_name)
expect(current_path).to eq root_path
visit edit_admin_conference_program_track_path(conference.short_title, other_track.short_name)
expect(current_path).to eq root_path
visit admin_conference_program_track_path(conference.short_title, self_organized_track.short_name)
expect(current_path).to eq admin_conference_program_track_path(conference.short_title, self_organized_track.short_name)
visit edit_admin_conference_program_track_path(conference.short_title, self_organized_track.short_name)
expect(current_path).to eq edit_admin_conference_program_track_path(conference.short_title, self_organized_track.short_name)
visit admin_conference_roles_path(conference.short_title)
expect(current_path).to eq admin_conference_roles_path(conference.short_title)
visit admin_conference_emails_path(conference.short_title)
expect(current_path).to eq root_path
visit admin_conference_resources_path(conference.short_title)
expect(current_path).to eq admin_conference_resources_path(conference.short_title)
visit new_admin_conference_resource_path(conference.short_title)
expect(current_path).to eq new_admin_conference_resource_path(conference.short_title)
create(:resource, conference: conference)
visit edit_admin_conference_resource_path(conference.short_title, conference.resources.first)
expect(current_path).to eq root_path
visit admin_revision_history_path
expect(current_path).to eq root_path
end
end
end

View file

@ -44,6 +44,8 @@ describe 'User with admin role' do
let!(:my_event_schedule) { create(:event_schedule, schedule: my_schedule) }
let!(:other_event_schedule) { create(:event_schedule, schedule: other_schedule) }
let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program) }
context 'user #is_admin?' do
let(:venue) { my_conference.venue }
let(:room) { create(:room, venue: venue) }
@ -69,6 +71,19 @@ describe 'User with admin role' do
it{ should be_able_to(:show, Role.find_by(name: role, resource: other_conference)) }
it{ should be_able_to(:index, Role.find_by(name: role, resource: other_conference)) }
end
context 'accesses track organizers' do
before :each do
other_self_organized_track = create(:track, :self_organized)
@other_track_organizer_role = Role.find_by(name: 'track_organizer', resource: other_self_organized_track)
end
it{ should_not be_able_to(:toggle_user, @other_track_organizer_role) }
it{ should_not be_able_to(:update, @other_track_organizer_role) }
it{ should_not be_able_to(:edit, @other_track_organizer_role) }
it{ should be_able_to(:show, @other_track_organizer_role) }
it{ should be_able_to(:index, @other_track_organizer_role) }
end
end
shared_examples 'user with non-organizer role' do |role_name|
@ -83,6 +98,22 @@ describe 'User with admin role' do
it{ should be_able_to(:show, Role.find_by(name: role, resource: my_conference)) }
it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) }
end
context 'accesses track organizers' do
before :each do
@track_organizer_role = Role.find_by(name: 'track_organizer', resource: my_self_organized_track)
end
if role_name == 'track_organizer'
it{ should be_able_to(:toggle_user, @track_organizer_role) }
else
it{ should_not be_able_to(:toggle_user, @track_organizer_role) }
end
it{ should_not be_able_to(:update, @track_organizer_role) }
it{ should_not be_able_to(:edit, @track_organizer_role) }
it{ should be_able_to(:show, @track_organizer_role) }
it{ should be_able_to(:index, @track_organizer_role) }
end
end
context 'when user has the role organization_admin' do
@ -186,6 +217,18 @@ describe 'User with admin role' do
it{ should be_able_to(:index, Role.find_by(name: role, resource: my_conference)) }
end
context 'can manage track organizers' do
before :each do
@track_organizer_role = Role.find_by(name: 'track_organizer', resource: my_self_organized_track)
end
it{ should be_able_to(:toggle_user, @track_organizer_role) }
it{ should be_able_to(:edit, @track_organizer_role) }
it{ should be_able_to(:update, @track_organizer_role) }
it{ should be_able_to(:show, @track_organizer_role) }
it{ should be_able_to(:index, @track_organizer_role) }
end
it_behaves_like 'user with any role'
end
@ -392,5 +435,74 @@ describe 'User with admin role' do
it_behaves_like 'user with any role'
it_behaves_like 'user with non-organizer role', 'volunteers_coordinator'
end
context 'when user has the role track_organizer' do
let(:role) { Role.find_by(name: 'track_organizer', resource: my_self_organized_track) }
let(:user) { create(:user, role_ids: [role.id]) }
let(:new_track) { build(:track, program: my_conference.program) }
it{ should_not be_able_to(:new, Conference.new) }
it{ should_not be_able_to(:create, Conference.new) }
it{ should_not be_able_to(:manage, my_conference) }
it{ should_not be_able_to(:manage, conference_public) }
it{ should_not be_able_to(:manage, my_conference.splashpage) }
it{ should_not be_able_to(:manage, conference_public.splashpage) }
it{ should_not be_able_to(:manage, my_conference.contact) }
it{ should_not be_able_to(:manage, conference_public.contact) }
it{ should_not be_able_to(:manage, my_conference.email_settings) }
it{ should_not be_able_to(:manage, conference_public.email_settings) }
it{ should_not be_able_to(:manage, my_conference.campaigns.first) }
it{ should_not be_able_to(:manage, conference_public.campaigns.first) }
it{ should_not be_able_to(:manage, my_conference.targets.first) }
it{ should_not be_able_to(:manage, conference_public.targets.first) }
it{ should_not be_able_to(:manage, my_conference.commercials.first) }
it{ should_not be_able_to(:manage, conference_public.commercials.first) }
it{ should_not be_able_to(:manage, my_conference.registration_period) }
it{ should_not be_able_to(:manage, conference_public.registration_period) }
it{ should_not be_able_to(:manage, my_conference.questions.first) }
it{ should_not be_able_to(:manage, conference_public.questions.first) }
it{ should_not be_able_to(:manage, my_conference.program.cfp) }
it{ should_not be_able_to(:manage, conference_public.program.cfp) }
it{ should_not be_able_to(:manage, my_schedule) }
it{ should_not be_able_to(:manage, other_schedule) }
it{ should_not be_able_to(:manage, my_event_schedule) }
it{ should_not be_able_to(:manage, other_event_schedule) }
it{ should_not be_able_to(:manage, my_conference.venue) }
it{ should_not be_able_to(:show, my_conference.venue) }
it{ should_not be_able_to(:manage, conference_public.venue) }
it{ should_not be_able_to(:manage, my_conference.lodgings.first) }
it{ should_not be_able_to(:manage, conference_public.lodgings.first) }
it{ should_not be_able_to(:manage, my_conference.sponsors.first) }
it{ should_not be_able_to(:manage, conference_public.sponsors.first) }
it{ should_not be_able_to(:manage, my_conference.sponsorship_levels.first) }
it{ should_not be_able_to(:manage, conference_public.sponsorship_levels.first) }
it{ should_not be_able_to(:manage, my_conference.tickets.first) }
it{ should_not be_able_to(:manage, conference_public.tickets.first) }
it{ should_not be_able_to(:manage, registration) }
it{ should_not be_able_to(:manage, other_registration) }
it{ should_not be_able_to(:manage, my_event) }
it{ should_not be_able_to(:manage, other_event) }
it{ should_not be_able_to(:manage, my_event.event_type) }
it{ should_not be_able_to(:manage, other_event.event_type) }
it{ should_not be_able_to(:manage, my_event.track) }
it{ should_not be_able_to(:manage, other_event.track) }
it{ should_not be_able_to(:manage, my_event.difficulty_level) }
it{ should_not be_able_to(:manage, other_event.difficulty_level) }
it{ should_not be_able_to(:manage, my_event.commercials.first) }
it{ should_not be_able_to(:manage, other_event.commercials.first) }
it{ should_not be_able_to(:index, my_event.comment_threads.first) }
it{ should_not be_able_to(:index, other_event.comment_threads.first) }
it{ should_not be_able_to(:manage, resource) }
it{ should be_able_to(:show, my_conference.program) }
it{ should be_able_to(:update, new_track) }
it{ should be_able_to(:manage, my_self_organized_track) }
it_behaves_like 'user with any role'
it_behaves_like 'user with non-organizer role', 'track_organizer'
end
end
end

57
spec/models/track_spec.rb Normal file
View file

@ -0,0 +1,57 @@
require 'spec_helper'
describe Track do
subject { create(:track) }
let(:track) { create(:track) }
let(:self_organized_track) { create(:track, :self_organized) }
describe 'association' do
it { is_expected.to belong_to(:program) }
it { is_expected.to belong_to(:submitter).class_name('User') }
it { is_expected.to have_many(:events) }
end
describe 'validation' do
it 'has a valid factory' do
expect(build(:track)).to be_valid
end
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to allow_value('#ABCDEF').for(:color) }
it { is_expected.to allow_value('#124689').for(:color) }
it { is_expected.to validate_presence_of(:short_name) }
it { is_expected.to allow_value('My_track_name').for(:short_name) }
it { is_expected.to_not allow_value('My track name').for(:short_name) }
it { is_expected.to validate_uniqueness_of(:short_name).scoped_to(:program_id) }
context 'when self-organized' do
before :each do
allow(subject).to receive(:self_organized?).and_return(true)
end
it { is_expected.to validate_presence_of(:state) }
it { is_expected.to validate_inclusion_of(:cfp_active).in_array([true, false]) }
end
context 'when regular' do
before :each do
allow(subject).to receive(:self_organized?).and_return(false)
end
it { is_expected.to_not validate_presence_of(:state) }
it { is_expected.to_not validate_inclusion_of(:cfp_active) }
end
end
describe '#self_organized?' do
it 'returns true when it has a submitter' do
expect(self_organized_track.submitter).to be_a User
expect(self_organized_track.self_organized?).to eq true
end
it 'returns false when it doesn\'t have a submitter' do
expect(track.submitter).to eq nil
expect(track.self_organized?).to eq false
end
end
end