Browse Source

Add Schools and Administrations

Frans Bergman 7 years ago
parent
commit
03618ffe73

+ 11 - 6
app/controllers/users_controller.rb

@@ -5,7 +5,7 @@ class UsersController < ApplicationController
 
   def index
     respond_to do |format|
-      @users = User.all
+      @users = current_user.school ? current_user.school.users : User.all
       format.json
       format.html
     end
@@ -37,14 +37,19 @@ class UsersController < ApplicationController
 
     # Only allow certain attributes to be updated over the web.
     def user_params
-      params.require(:user).permit(:login, :email, :password,
-                                   :password_confirmation,
-                                   :gender, :phone, :picture)
+      allowed = [:login, :email, :password, :password_confirmation,
+                :phone, :picture]
+
+      if current_user.is_administrator_at?(@user.school)
+        allowed += [:gender, :birth_date, :name]
+      end
+
+      params.require(:user).permit(*allowed)
     end
 
     # Confirms the correct user.
     def correct_user
-      @user = User.find(params[:id])
-      redirect_to(root_url) unless current_user?(@user)
+      redirect_to(root_url) unless current_user?(@user) ||
+                                current_user.is_administrator_at?(@user.school)
     end
 end

+ 6 - 0
app/models/administration.rb

@@ -0,0 +1,6 @@
+class Administration < ApplicationRecord
+  belongs_to :user
+  belongs_to :school
+  validates :user_id, presence: true
+  validates :school_id, presence: true
+end

+ 9 - 0
app/models/school.rb

@@ -0,0 +1,9 @@
+class School < ApplicationRecord
+  validates :name, presence: true, length: { maximum: 255 }
+
+  has_many :administrations, dependent: :destroy
+  has_many :administrators, through: :administrations,
+                            class_name: "User", source: :user
+
+  has_many :users
+end

+ 10 - 0
app/models/user.rb

@@ -33,6 +33,12 @@ class User < ApplicationRecord
   has_many :conversation_participations, dependent: :destroy
   has_many :conversations, through: :conversation_participations
 
+  has_many :administrations, dependent: :destroy
+  has_many :schools_administering, through: :administrations,
+                                   class_name: "School", source: :school
+
+  belongs_to :school, optional: true
+
   # Returns the hash digest of the given string.
   def User.digest(string)
     cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
@@ -62,6 +68,10 @@ class User < ApplicationRecord
     update_attribute(:remember_digest, nil)
   end
 
+  def is_administrator_at?(school)
+    school ? school.administrators.include?(self) : false
+  end
+
   private
     def picture_size
       if picture.size > 5.megabytes

+ 5 - 1
app/views/users/_form.html.erb

@@ -1,12 +1,16 @@
 <%= bootstrap_form_for(@user) do |f| %>
   <%= render 'shared/error_messages', object: f.object %>
 
+  <% if current_user.is_administrator_at?(@user.school) %>
+    <%= f.text_field :name %>
+    <%= f.select :gender, User.genders.keys, {} %>
+    <%= f.date_field :birth_date %>
+  <% end %>
   <%= f.text_field :login %>
   <%= f.email_field :email %>
   <%= f.phone_field :phone %>
   <%= f.password_field :password %>
   <%= f.password_field :password_confirmation %>
-  <%= f.select :gender, User.genders.keys, {} %>
 
   <%= f.submit yield(:button_text), class: "btn btn-primary" %>
 <% end %>

+ 5 - 0
app/views/users/show.html.erb

@@ -22,6 +22,11 @@
           <%= fa_icon("phone fw", text: @user.phone) %>
         </li>
       </ul>
+      <% if current_user.is_administrator_at?(@user.school) %>
+        <%= link_to edit_user_path(@user), class: 'btn btn-primary' do %>
+          <%= fa_icon 'edit', text: "Edit details" %>
+        <% end %>
+      <% end %>
     </section>
   </aside>
 </div>

+ 9 - 0
db/migrate/20180116194252_create_schools.rb

@@ -0,0 +1,9 @@
+class CreateSchools < ActiveRecord::Migration[5.1]
+  def change
+    create_table :schools do |t|
+      t.string :name
+
+      t.timestamps
+    end
+  end
+end

+ 13 - 0
db/migrate/20180116194825_create_administrations.rb

@@ -0,0 +1,13 @@
+class CreateAdministrations < ActiveRecord::Migration[5.1]
+  def change
+    create_table :administrations do |t|
+
+      t.references :user
+      t.references :school
+
+      t.timestamps
+    end
+
+    add_index :administrations, [:user_id, :school_id], unique: true
+  end
+end

+ 5 - 0
db/migrate/20180119161200_add_school_id_to_users.rb

@@ -0,0 +1,5 @@
+class AddSchoolIdToUsers < ActiveRecord::Migration[5.1]
+  def change
+    add_reference :users, :school, foreign_key: true
+  end
+end

+ 19 - 1
db/schema.rb

@@ -10,7 +10,17 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20171222171109) do
+ActiveRecord::Schema.define(version: 20180119161200) do
+
+  create_table "administrations", force: :cascade do |t|
+    t.integer "user_id"
+    t.integer "school_id"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["school_id"], name: "index_administrations_on_school_id"
+    t.index ["user_id", "school_id"], name: "index_administrations_on_user_id_and_school_id", unique: true
+    t.index ["user_id"], name: "index_administrations_on_user_id"
+  end
 
   create_table "conversation_participations", force: :cascade do |t|
     t.integer "user_id"
@@ -38,6 +48,12 @@ ActiveRecord::Schema.define(version: 20171222171109) do
     t.index ["user_id"], name: "index_messages_on_user_id"
   end
 
+  create_table "schools", force: :cascade do |t|
+    t.string "name"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+  end
+
   create_table "users", force: :cascade do |t|
     t.string "name"
     t.string "login"
@@ -50,7 +66,9 @@ ActiveRecord::Schema.define(version: 20171222171109) do
     t.string "phone"
     t.date "birth_date"
     t.string "picture"
+    t.integer "school_id"
     t.index ["login"], name: "index_users_on_login", unique: true
+    t.index ["school_id_id"], name: "index_users_on_school_id_id"
   end
 
 end

+ 9 - 2
db/seeds.rb

@@ -6,13 +6,19 @@
 #   movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
 #   Character.create(name: 'Luke', movie: movies.first)
 
+school = School.create!(name: "Fakeville High")
+
 user = User.create!(name:  "Example User",
              login: "example",
              email: "example@example.com",
              password:              "foobar",
              password_confirmation: "foobar",
              birth_date: 15.years.ago,
-             phone:      "(333) 333-3333")
+             phone:      "(333) 333-3333",
+             school: school)
+
+school.administrators << user
+school.save
 
 
 10.times do |n|
@@ -28,7 +34,8 @@ user = User.create!(name:  "Example User",
               password:              password,
               password_confirmation: password,
               birth_date: birth_date,
-              phone:      phone)
+              phone:      phone,
+              school: school)
 end
 
 conversation = Conversation.create!(name: "Important conversation")

+ 10 - 2
test/controllers/users_controller_test.rb

@@ -4,6 +4,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
   def setup
     @user = users(:daniel)
     @other_user = users(:ben)
+    @student_user = users(:billy)
   end
 
   test "should display age correctly" do
@@ -53,18 +54,25 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
     assert_not_equal @user.reload.name, "Wrong Name"
   end
 
-  test "should redirect edit when logged in as wrong user" do
+  test "should redirect edit when unauthorized" do
     log_in_as(@other_user)
     get edit_user_path(@user)
     assert flash.empty?
     assert_redirected_to root_url
   end
 
-  test "should redirect update when logged in as wrong user" do
+  test "should redirect update when unauthorized" do
     log_in_as(@other_user)
     patch user_path(@user), params: { user: { name: @user.name,
                                               email: @user.email } }
     assert flash.empty?
     assert_redirected_to root_url
   end
+
+  test "should update name attribute when logged in as admin" do
+    log_in_as(@user)
+    patch user_url(@student_user), params: { user: { name: "New Name" } }
+    assert_equal @student_user.reload.name, "New Name"
+    assert_redirected_to user_url(@student_user)
+  end
 end

+ 5 - 0
test/fixtures/administrations.yml

@@ -0,0 +1,5 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  user: daniel
+  school: two

+ 7 - 0
test/fixtures/schools.yml

@@ -0,0 +1,7 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  name: Fakeville High
+
+two:
+  name: Exampletown Elementary School

+ 8 - 0
test/fixtures/users.yml

@@ -20,3 +20,11 @@ jane:
   email: jane.doe@example.com
   password_digest: <%= User.digest('password') %>
   birth_date: <%= 30.years.ago %>
+
+billy:
+  login: billy_9876
+  name: Billy McDonald
+  email: billy.mcdonald@example.com
+  password_digest: <%= User.digest('password') %>
+  birth_date: <%= 11.years.ago %>
+  school: two

+ 29 - 0
test/models/administration_test.rb

@@ -0,0 +1,29 @@
+require 'test_helper'
+
+class AdministrationTest < ActiveSupport::TestCase
+
+  def setup
+    @admin = users(:daniel)
+    @school = schools(:one)
+    @administration = Administration.new(user_id: @admin.id,
+                                         school_id: @school.id)
+  end
+
+  test "should be valid" do
+    assert @administration.valid?
+  end
+
+  test "should destroy administration when user is destroyed" do
+    @administration.save
+    assert_difference "@school.administrators.count", -1 do
+      @admin.destroy
+    end
+  end
+
+  test "should destroy administration when school is destroyed" do
+    @administration.save
+    assert_difference "@admin.schools_administering.count", -1 do
+      @school.destroy
+    end
+  end
+end

+ 17 - 0
test/models/school_test.rb

@@ -0,0 +1,17 @@
+require 'test_helper'
+
+class SchoolTest < ActiveSupport::TestCase
+
+  def setup
+    @school = schools(:one)
+  end
+
+  test "should be valid" do
+    assert @school.valid?
+  end
+
+  test "name should be present" do
+    @school.name = "       "
+    assert_not @school.valid?
+  end
+end