Skip to content

Commit

Permalink
Add Organization Leader role
Browse files Browse the repository at this point in the history
Organization leaders can create chapters for their org
and are listed on the organization index at /organizations.
  • Loading branch information
tjgrathwell committed Mar 13, 2016
1 parent f07a099 commit 94be27b
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 9 deletions.
2 changes: 1 addition & 1 deletion app/controllers/chapters_controller.rb
Expand Up @@ -34,8 +34,8 @@ def edit
end

def create
authorize Chapter
@chapter = Chapter.new(chapter_params)
authorize @chapter

if @chapter.save
redirect_to @chapter, notice: 'Chapter was successfully created.'
Expand Down
4 changes: 4 additions & 0 deletions app/models/chapter.rb
Expand Up @@ -19,6 +19,10 @@ def has_leader?(user)
leaders.include?(user)
end

def editable_by?(user)
has_leader?(user) || organization.has_leader?(user)
end

def destroyable?
(events_count + external_events_count) == 0
end
Expand Down
10 changes: 10 additions & 0 deletions app/models/organization.rb
@@ -1,3 +1,13 @@
class Organization < ActiveRecord::Base
has_many :chapters, dependent: :destroy
has_many :leaders, through: :organization_leaderships, source: :user
has_many :organization_leaderships, dependent: :destroy

def has_leader?(user)
return false unless user

return true if user.admin?

leaders.include?(user)
end
end
6 changes: 6 additions & 0 deletions app/models/organization_leadership.rb
@@ -0,0 +1,6 @@
class OrganizationLeadership < ActiveRecord::Base
belongs_to :organization
belongs_to :user

validates :user, uniqueness: { scope: :organization }
end
1 change: 1 addition & 0 deletions app/models/user.rb
Expand Up @@ -12,6 +12,7 @@ class User < ActiveRecord::Base
has_many :events, -> { published }, through: :rsvps
has_many :region_leaderships, dependent: :destroy
has_many :chapter_leaderships, dependent: :destroy
has_many :organization_leaderships, dependent: :destroy
has_many :event_emails, foreign_key: :sender_id, dependent: :nullify

has_one :profile, dependent: :destroy, inverse_of: :user, validate: true
Expand Down
9 changes: 9 additions & 0 deletions app/policies/application_policy.rb
@@ -1,4 +1,13 @@
class ApplicationPolicy
class Scope
attr_reader :user, :scope

def initialize(user, scope)
@user = user
@scope = scope
end
end

attr_reader :user, :record

def initialize(user, record)
Expand Down
10 changes: 5 additions & 5 deletions app/policies/chapter_policy.rb
@@ -1,25 +1,25 @@
class ChapterPolicy < ApplicationPolicy
def new?
user.admin?
user.admin? || user.organization_leaderships.present?
end

def edit?
record.has_leader?(user)
record.editable_by?(user)
end

def update?
record.has_leader?(user)
record.editable_by?(user)
end

def create?
user.admin?
user.admin? || record.editable_by?(user)
end

def destroy?
user.admin?
end

def modify_leadership?
record.has_leader?(user)
record.editable_by?(user)
end
end
11 changes: 11 additions & 0 deletions app/policies/organization_policy.rb
@@ -0,0 +1,11 @@
class OrganizationPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where(id: user.organization_leaderships.map(&:organization_id))
end
end
end
end
2 changes: 1 addition & 1 deletion app/views/chapters/_form.html.erb
Expand Up @@ -5,7 +5,7 @@

<div class="form-group">
<%= f.label :organization_id %>
<%= f.collection_select(:organization_id, Organization.order(:name), :id, :name, {prompt: true}, {class: 'form-control'}) %>
<%= f.collection_select(:organization_id, policy_scope(Organization).order(:name), :id, :name, {prompt: true}, {class: 'form-control'}) %>
</div>

<%= f.input :name %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/chapters/index.html.erb
Expand Up @@ -28,4 +28,4 @@
</tbody>
</table>

<%= render 'shared/actions', links: crud_object_nav_links(:chapter, current_user.try(:admin?) ? ['New Chapter', new_chapter_path] : nil) %>
<%= render 'shared/actions', links: crud_object_nav_links(:chapter, policy(Chapter).new? ? ['New Chapter', new_chapter_path] : nil) %>
18 changes: 18 additions & 0 deletions app/views/organizations/index.html.erb
@@ -1,3 +1,21 @@
<h1>Organizations</h1>

<% @organizations.sort_by(&:name).each do |org| %>
<% if org.chapters.count > 0 %>
<h2><%= org.name %></h2>
<div><% org.chapters.count %><%= pluralize(org.chapters.count, 'chapter') %></div>
<% if org.leaders.length > 0 %>
<div>Leaders:</div>
<ul>
<% org.leaders.each do |leader| %>
<li><%= link_to leader.full_name, user_profile_path(leader) %></li>
<% end %>
</ul>
<% end %>
<% end %>
<% end %>

<h1 class=='mt-10'>Chapters</h1>
<%= render "shared/map",
locations: @chapter_locations,
map_class: 'gmaps4rails_bigger_map',
Expand Down
8 changes: 8 additions & 0 deletions db/migrate/20160312224628_create_organization_leaderships.rb
@@ -0,0 +1,8 @@
class CreateOrganizationLeaderships < ActiveRecord::Migration
def change
create_table :organization_leaderships do |t|
t.references :user, index: true, foreign_key: true
t.references :organization, index: true, foreign_key: true
end
end
end
14 changes: 13 additions & 1 deletion db/schema.rb
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160211061631) do
ActiveRecord::Schema.define(version: 20160312224628) do

create_table "authentications", force: :cascade do |t|
t.integer "user_id"
Expand Down Expand Up @@ -159,6 +159,14 @@
t.datetime "updated_at", null: false
end

create_table "organization_leaderships", force: :cascade do |t|
t.integer "user_id"
t.integer "organization_id"
end

add_index "organization_leaderships", ["organization_id"], name: "index_organization_leaderships_on_organization_id"
add_index "organization_leaderships", ["user_id"], name: "index_organization_leaderships_on_user_id"

create_table "organizations", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
Expand Down Expand Up @@ -293,6 +301,8 @@
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

add_foreign_key "authentications", "users"
add_foreign_key "chapter_leaderships", "chapters"
add_foreign_key "chapter_leaderships", "users"
add_foreign_key "chapters", "organizations"
add_foreign_key "dietary_restrictions", "rsvps"
add_foreign_key "event_email_recipients", "event_emails"
Expand All @@ -306,6 +316,8 @@
add_foreign_key "external_events", "chapters"
add_foreign_key "external_events", "regions"
add_foreign_key "locations", "regions"
add_foreign_key "organization_leaderships", "organizations"
add_foreign_key "organization_leaderships", "users"
add_foreign_key "profiles", "users"
add_foreign_key "region_leaderships", "regions"
add_foreign_key "region_leaderships", "users"
Expand Down
26 changes: 26 additions & 0 deletions spec/features/chapter_request_spec.rb
Expand Up @@ -37,4 +37,30 @@
expect(page).to have_content(organizer.full_name)
end
end

describe 'creating a new chapter' do
let!(:org1) { create(:organization, name: 'Org1') }
let!(:org2) { create(:organization, name: 'Org2') }
let(:org1_leader) { create(:user) }

before do
org1_leader.organization_leaderships.create(organization: org1)
end

it "allows organization leaders to create a chapter in their org" do
sign_in_as(org1_leader)

visit new_chapter_path
expect(page.all('#chapter_organization_id option').map(&:value)).to match_array(['', org1.id.to_s])

select 'Org1', from: 'Organization'
fill_in 'Name', with: 'Cantaloupe Chapter'

expect {
click_on 'Create Chapter'
}.to change(Chapter, :count).by(1)

expect(Chapter.last.name).to eq('Cantaloupe Chapter')
end
end
end

0 comments on commit 94be27b

Please sign in to comment.