diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb new file mode 100644 index 00000000..1dc6bcd5 --- /dev/null +++ b/app/controllers/admin/organizations_controller.rb @@ -0,0 +1,19 @@ +module Admin + class OrganizationsController < Admin::BaseController + load_and_authorize_resource :organization + + def index + @organizations = Organization.all + end + + def destroy + if @organization.destroy + redirect_to admin_organizations_path, + notice: 'Organization successfully destroyed' + else + redirect_to admin_organizations_path, + error: 'Organization cannot be destroyed' + end + end + end +end diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb new file mode 100644 index 00000000..58910526 --- /dev/null +++ b/app/controllers/organizations_controller.rb @@ -0,0 +1,50 @@ +class OrganizationsController < ApplicationController + load_and_authorize_resource :organization + + def index + @organizations = Organization.all + end + + def create + @organization = Organization.new(organization_params) + if @organization.save + redirect_to organizations_path, + notice: 'Organization successfully created' + else + redirect_to new_organization_path, + error: @organization.errors.full_messages.join(', ') + end + end + + def new + @organization = Organization.new + end + + def edit; end + + def update + if @organization.update_attributes(organization_params) + redirect_to organizations_path, + notice: 'Organization successfully updated' + else + redirect_to edit_organization_path(@organization), + error: @organization.errors.full_messages.join(', ') + end + end + + def destroy + if @organization.destroy + redirect_to organizations_path, + notice: 'Organization successfully destroyed' + else + redirect_to organizations_path, + error: 'Organization cannot be destroyed' + end + end + + private + + def organization_params + params.require(:organization).permit(:name, :description, :picture) + end +end diff --git a/app/models/conference.rb b/app/models/conference.rb index 2f623a4c..0f09d694 100644 --- a/app/models/conference.rb +++ b/app/models/conference.rb @@ -7,6 +7,8 @@ class Conference < ActiveRecord::Base default_scope { order('start_date DESC') } + belongs_to :organization + has_paper_trail ignore: %i(updated_at guid revision events_per_week), meta: { conference_id: :id } has_and_belongs_to_many :questions @@ -53,7 +55,8 @@ class Conference < ActiveRecord::Base :start_date, :end_date, :start_hour, - :end_hour, presence: true + :end_hour, + :organization, presence: true validates :short_title, uniqueness: true validates :short_title, format: { with: /\A[a-zA-Z0-9_-]*\z/ } diff --git a/app/models/organization.rb b/app/models/organization.rb new file mode 100644 index 00000000..60a83fef --- /dev/null +++ b/app/models/organization.rb @@ -0,0 +1,7 @@ +class Organization < ActiveRecord::Base + has_many :conferences, dependent: :destroy + + validates :name, presence: true + + mount_uploader :picture, PictureUploader, mount_on: :picture +end diff --git a/app/views/admin/organizations/_form.html.haml b/app/views/admin/organizations/_form.html.haml new file mode 100644 index 00000000..c41f0cff --- /dev/null +++ b/app/views/admin/organizations/_form.html.haml @@ -0,0 +1,13 @@ += semantic_form_for(@organization) do |f| + = f.inputs name: 'Organization details' do + = f.input :name, as: :string, required: true + = f.input :description, input_html: { rows: 5 }, placeholder: 'Decribe about your organization..' + = image_tag f.object.picture.thumb.url if f.object.picture? + - if @organization.picture + = image_tag(@organization.picture.thumb.url, width: '20%') + = f.input :picture + %p.text-right + - if @organization.new_record? + = f.submit 'Create Organization', class: 'btn btn-success' + - else + = f.submit 'Update Organization', class: 'btn btn-success' \ No newline at end of file diff --git a/app/views/admin/organizations/index.html.haml b/app/views/admin/organizations/index.html.haml new file mode 100644 index 00000000..0a751460 --- /dev/null +++ b/app/views/admin/organizations/index.html.haml @@ -0,0 +1,31 @@ +.row + .col-md-12 + .page-header + %h1 Organizations + .btn-group.pull-right + = link_to 'Add Organization', new_admin_organization_path, class: 'btn btn-success pull-right' + %p.text-muted + Manage organizations in OSEM + .row + .col-md-12 + %table.table.table-hover.datatable + %thead + %th Name + %th Upcoming Conferences + %th Past Conferences + %th Actions + %tbody + - @organizations.each do |organization| + %tr + %td + = organization.name + %td + = organization.conferences.count + %td + = organization.conferences.count + %td + .btn-group + = link_to 'Edit', edit_organization_path(organization), + method: :get, class: 'btn btn-primary' + = link_to 'Delete', admin_organization_path(organization), + method: :delete, class: 'btn btn-danger' diff --git a/app/views/layouts/_admin_sidebar_index.html.haml b/app/views/layouts/_admin_sidebar_index.html.haml index 4ae56dce..f1cf8b75 100644 --- a/app/views/layouts/_admin_sidebar_index.html.haml +++ b/app/views/layouts/_admin_sidebar_index.html.haml @@ -32,3 +32,7 @@ = link_to(admin_revision_history_path) do %span.fa.fa-history Revision History + %li + = link_to(admin_organizations_path) do + %span.fa.fa-group + Organizations diff --git a/app/views/layouts/_user_menu.html.haml b/app/views/layouts/_user_menu.html.haml index 3fb32273..14d32fed 100644 --- a/app/views/layouts/_user_menu.html.haml +++ b/app/views/layouts/_user_menu.html.haml @@ -49,3 +49,7 @@ = link_to(admin_revision_history_path) do %span.fa.fa-history Revision History + %li + = link_to(admin_organizations_path) do + %span.fa.fa-group + Organizations diff --git a/app/views/organizations/_form.html.haml b/app/views/organizations/_form.html.haml new file mode 100644 index 00000000..c41f0cff --- /dev/null +++ b/app/views/organizations/_form.html.haml @@ -0,0 +1,13 @@ += semantic_form_for(@organization) do |f| + = f.inputs name: 'Organization details' do + = f.input :name, as: :string, required: true + = f.input :description, input_html: { rows: 5 }, placeholder: 'Decribe about your organization..' + = image_tag f.object.picture.thumb.url if f.object.picture? + - if @organization.picture + = image_tag(@organization.picture.thumb.url, width: '20%') + = f.input :picture + %p.text-right + - if @organization.new_record? + = f.submit 'Create Organization', class: 'btn btn-success' + - else + = f.submit 'Update Organization', class: 'btn btn-success' \ No newline at end of file diff --git a/app/views/organizations/edit.html.haml b/app/views/organizations/edit.html.haml new file mode 100644 index 00000000..1f65d485 --- /dev/null +++ b/app/views/organizations/edit.html.haml @@ -0,0 +1,4 @@ +.container + .row + .col-md-12 + = render 'form' diff --git a/app/views/organizations/index.html.haml b/app/views/organizations/index.html.haml new file mode 100644 index 00000000..b87f156b --- /dev/null +++ b/app/views/organizations/index.html.haml @@ -0,0 +1,17 @@ +.container + .row + .col-md-12.page-header + %h1 + Organizations + .btn-group.pull-right + = link_to 'Add new', new_organization_path, class: 'btn btn-mini btn-success' + - @organizations.each do |organization| + .col-md-4 + .thumbnail + = image_tag(organization.picture.thumb.url, width: '20%') + .caption + %h4 + = organization.name + %button.btn.btn-success Conferences + = link_to 'Edit', edit_organization_path(organization), class: 'btn btn-mini btn-default' + diff --git a/app/views/organizations/new.html.haml b/app/views/organizations/new.html.haml new file mode 100644 index 00000000..1f65d485 --- /dev/null +++ b/app/views/organizations/new.html.haml @@ -0,0 +1,4 @@ +.container + .row + .col-md-12 + = render 'form' diff --git a/config/routes.rb b/config/routes.rb index 9fa1c29a..e2cfe7f7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,6 +18,7 @@ Osem::Application.routes.draw do resources :users, except: [:new, :index, :create, :destroy] namespace :admin do + resources :organizations resources :users do member do patch :toggle_confirmation @@ -102,7 +103,7 @@ Osem::Application.routes.draw do get '/revision_history/:id/revert_object' => 'versions#revert_object', as: 'revision_history_revert_object' get '/revision_history/:id/revert_attribute' => 'versions#revert_attribute', as: 'revision_history_revert_attribute' end - + resources :organizations resources :conferences, only: [:index, :show] do resource :program, only: [] do resources :proposals, except: :destroy do diff --git a/db/migrate/20170529215453_create_organizations.rb b/db/migrate/20170529215453_create_organizations.rb new file mode 100644 index 00000000..11caf736 --- /dev/null +++ b/db/migrate/20170529215453_create_organizations.rb @@ -0,0 +1,9 @@ +class CreateOrganizations < ActiveRecord::Migration + def change + create_table :organizations do |t| + t.string :name + t.text :description + t.string :picture + end + end +end diff --git a/db/migrate/20170531094817_add_organizaton_id_to_conference.rb b/db/migrate/20170531094817_add_organizaton_id_to_conference.rb new file mode 100644 index 00000000..4397ed7a --- /dev/null +++ b/db/migrate/20170531094817_add_organizaton_id_to_conference.rb @@ -0,0 +1,5 @@ +class AddOrganizatonIdToConference < ActiveRecord::Migration + def change + add_column :conferences, :organization_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 5d74950a..c50e6b45 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170419132148) do +ActiveRecord::Schema.define(version: 20170531094817) do create_table "ahoy_events", force: :cascade do |t| t.uuid "visit_id", limit: 16 @@ -100,6 +100,7 @@ ActiveRecord::Schema.define(version: 20170419132148) do t.string "picture" t.integer "start_hour", default: 9 t.integer "end_hour", default: 20 + t.integer "organization_id" end create_table "conferences_questions", id: false, force: :cascade do |t| @@ -266,6 +267,12 @@ ActiveRecord::Schema.define(version: 20170419132148) do t.datetime "updated_at" end + create_table "organizations", force: :cascade do |t| + t.string "name" + t.text "description" + t.string "picture" + end + create_table "payments", force: :cascade do |t| t.string "last4" t.integer "amount" diff --git a/spec/controllers/admin/organizations_controller_spec.rb b/spec/controllers/admin/organizations_controller_spec.rb new file mode 100644 index 00000000..4635dfdf --- /dev/null +++ b/spec/controllers/admin/organizations_controller_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe Admin::OrganizationsController do + let(:admin) { create(:admin) } + + describe 'GET #index' do + before :each do + sign_in admin + get :index + end + + it { expect(response).to render_template('index') } + end +end diff --git a/spec/controllers/organizations_controller_spec.rb b/spec/controllers/organizations_controller_spec.rb new file mode 100644 index 00000000..94007630 --- /dev/null +++ b/spec/controllers/organizations_controller_spec.rb @@ -0,0 +1,104 @@ +require 'spec_helper' + +describe OrganizationsController do + let!(:organization) { create(:organization) } + let!(:admin) { create(:admin, is_admin: true) } + + describe 'GET #new' do + before :each do + sign_in admin + get :new + end + + it { expect(response).to render_template('new') } + end + + describe 'GET #index' do + before :each do + sign_in admin + get :index + end + + it { expect(response).to render_template('index') } + end + + describe 'POST #create' do + before :each do + sign_in admin + end + context 'with valid attributes' do + it 'creates new organization' do + expected = expect do + post :create, organization: attributes_for(:organization) + end + expected.to change { Organization.count }.by(1) + end + + it 'redirects to index' do + post :create, organization: attributes_for(:organization) + + expect(flash[:notice]).to eq('Organization successfully created') + expect(response).to redirect_to(organizations_path) + end + end + + context 'with invalid attributes' do + it 'does not create new organization' do + expected = expect do + post :create, organization: attributes_for(:organization, name: '') + end + expected.to_not change { Organization.count } + end + + it 'redirects to new' do + post :create, organization: attributes_for(:organization, name: '') + + expect(flash[:error]).to eq("Name can't be blank") + expect(response).to redirect_to(new_organization_path) + end + end + end + + describe 'PATCH #update' do + before :each do + sign_in admin + end + + it 'saves and redirects to index when the attributes are valid' do + patch :update, id: organization.id, organization: attributes_for(:organization, name: 'changed name') + + expect(organization.name).to eq('changed name') + expect(flash).to eq('Organization successfully updated') + expect(response).to redirect_to(organizations_path) + end + + it 'redirects to edit when attributes are invalid' do + patch :update, id: organization.id, organization: attributes_for(:organization, name: '') + + expect(flash[:error]).to eq("Name can't be blank") + expect(response).to redirect_to(edit_organization_path(organization)) + end + end + + describe 'DELETE #destroy' do + before :each do + sign_in admin + end + + context 'for a valid organization' do + it 'should successfully destroy a resource' do + expected = expect do + delete :destroy, id: organization.id + end + expected.to change { Organization.count }.by(-1) + end + + it 'redirects to index' do + delete :destroy, id: organization.id + + expect(flash[:notice]).to eq('Organization successfully destroyed') + expect(response).to redirect_to(organizations_path) + end + end + end +end diff --git a/spec/factories/conferences.rb b/spec/factories/conferences.rb index 8a9186a6..e1ef7d84 100644 --- a/spec/factories/conferences.rb +++ b/spec/factories/conferences.rb @@ -11,7 +11,7 @@ FactoryGirl.define do end_hour 20 registration_limit 0 description { Faker::Hipster.paragraph } - + organization after(:create) do |conference| Role.where(name: 'organizer', resource: conference).first_or_create(description: 'For the organizers of the conference (who shall have full access)') Role.where(name: 'cfp', resource: conference).first_or_create(description: 'For the members of the CfP team') diff --git a/spec/factories/organizations.rb b/spec/factories/organizations.rb new file mode 100644 index 00000000..0ee76554 --- /dev/null +++ b/spec/factories/organizations.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + factory :organization do + name { Faker::Company.name } + description { Faker::Lorem.paragraph } + + # after(:create) do |organization| + # File.open("spec/support/logos/#{1 + rand(13)}.png") do |file| + # organization.picture = file + # end + # organization.save! + # end + end +end diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb new file mode 100644 index 00000000..748205d5 --- /dev/null +++ b/spec/models/organization_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Organization do + let(:organization) { create(:organization) } + + describe 'validation' do + it 'has a valid factory' do + expect(build(:organization)).to be_valid + end + + it 'is not valid without a name' do + should validate_presence_of(:name) + end + end + + describe 'associations' do + it { should have_many(:conferences).dependent(:destroy) } + end +end