From 7ebe434a73aebfd4ac4af1a2ff02bc978d2a1f13 Mon Sep 17 00:00:00 2001
From: Tyler Lemburg <trlemburg@gmail.com>
Date: Fri, 17 Jun 2016 13:21:32 -0500
Subject: [PATCH] Events and Resources

---
 Gemfile                                       |   1 +
 Gemfile.lock                                  |  11 +
 app.rb                                        | 126 ++++--
 .../20160608113900_add_session_store.rb       |  13 +
 .../20160608134500_add_creation_method.rb     |   7 +
 .../20160608140300_add_space_permissions.rb   |  16 +
 models/event.rb                               |  11 +-
 models/permission.rb                          |   1 +
 models/reservation.rb                         |   1 +
 models/service_space.rb                       |  22 +-
 models/user.rb                                |  57 +--
 models/user_has_permission.rb                 |   5 +
 routes/admin.rb                               |  43 ++
 routes/admin/events.rb                        | 181 +++++++++
 routes/events.rb                              |  76 ++++
 routes/resources.rb                           | 373 ++++++++++++++++++
 routes/space.rb                               |  23 ++
 views/admin/events.erb                        | 110 ++++++
 views/admin/home.erb                          |  62 +++
 views/admin/new_event.erb                     | 177 +++++++++
 views/calendar.erb                            |   4 +-
 views/event_details.erb                       |  62 +++
 views/home.erb                                |  16 +
 views/reserve.erb                             | 187 +++++++++
 views/resources.erb                           |  34 ++
 views/space_home.erb                          |  85 ++++
 views/template_partials/navigation.erb        |  29 +-
 27 files changed, 1651 insertions(+), 82 deletions(-)
 create mode 100644 db/migrate/20160608113900_add_session_store.rb
 create mode 100644 db/migrate/20160608134500_add_creation_method.rb
 create mode 100644 db/migrate/20160608140300_add_space_permissions.rb
 create mode 100644 routes/admin.rb
 create mode 100644 routes/admin/events.rb
 create mode 100644 routes/events.rb
 create mode 100644 routes/resources.rb
 create mode 100644 routes/space.rb
 create mode 100644 views/admin/events.erb
 create mode 100644 views/admin/home.erb
 create mode 100644 views/admin/new_event.erb
 create mode 100644 views/event_details.erb
 create mode 100644 views/home.erb
 create mode 100644 views/reserve.erb
 create mode 100644 views/resources.erb
 create mode 100644 views/space_home.erb

diff --git a/Gemfile b/Gemfile
index f57b58d..1beab10 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,6 +10,7 @@ gem 'bcrypt'
 gem 'unicorn'
 gem 'pony'
 gem 'rest-client'
+gem 'rack-cas'
 
 group :development do
   gem 'shotgun'
diff --git a/Gemfile.lock b/Gemfile.lock
index aee2907..6ffb151 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -14,6 +14,7 @@ GEM
       minitest (~> 5.1)
       thread_safe (~> 0.3, >= 0.3.4)
       tzinfo (~> 1.1)
+    addressable (2.4.0)
     arel (6.0.3)
     backports (3.6.8)
     bcrypt (3.1.11)
@@ -57,14 +58,19 @@ GEM
       mime-types (>= 1.16, < 4)
     method_source (0.8.2)
     mime-types (2.99.2)
+    mini_portile2 (2.1.0)
     minitest (5.9.0)
     multi_json (1.12.1)
     mysql (2.9.1)
     nenv (0.3.0)
     netrc (0.11.0)
+    nokogiri (1.6.8)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
     notiffany (0.1.0)
       nenv (~> 0.1)
       shellany (~> 0.0)
+    pkg-config (1.1.7)
     pony (1.11)
       mail (>= 2.0)
     pry (0.10.3)
@@ -72,6 +78,10 @@ GEM
       method_source (~> 0.8.1)
       slop (~> 3.4)
     rack (1.6.4)
+    rack-cas (0.13.0)
+      addressable (~> 2.3)
+      nokogiri (~> 1.5)
+      rack (~> 1.3)
     rack-protection (1.5.3)
       rack
     rack-test (0.6.3)
@@ -130,6 +140,7 @@ DEPENDENCIES
   guard-less
   mysql
   pony
+  rack-cas
   rest-client
   shotgun
   sinatra
diff --git a/app.rb b/app.rb
index 1dfd310..928b944 100644
--- a/app.rb
+++ b/app.rb
@@ -1,15 +1,28 @@
 require 'sinatra'
+require 'rest-client'
+require 'rack/session/abstract/id'
+require 'rack/cas'
+require 'rack-cas'
+require 'rack-cas/session_store/active_record'
 require 'models/user'
 require 'models/service_space'
 
-use Rack::Session::Cookie, :key => 'rack.session',
-                           :path => '/',
-                           :domain => (ENV['RACK_ENV'] == 'development' ? nil : 'resource.unl.edu'),
-                           :secret => '420terrace12qmemorialpinnaclehawks',
-                           :old_secret => '420terrace12qmemorialpinnaclehawks'
+module Rack
+  module Session
+    class RackCASActiveRecordStore < Rack::Session::Abstract::ID
+      include RackCAS::ActiveRecordStore
+    end
+  end
+end
+
+use Rack::Session::RackCASActiveRecordStore
+use Rack::CAS, server_url: 'https://login.unl.edu/cas',
+               session_store: RackCAS::ActiveRecordStore
 
 Time.zone = "America/Chicago"
 
+DIRECTORY_URL = 'http://directory.unl.edu/'
+
 # this gives the user messages
 def flash(type, header, message)
   session["notice"] ||= []
@@ -21,28 +34,35 @@ def flash(type, header, message)
 end
 
 before do
-    # site defaults
-    @title = 'UNL Resource Scheduler'
-    @breadcrumbs = [
-      {
-        :href => 'http://www.unl.edu/',
-        :text => 'UNL',
-        :title => 'University of Nebraska&ndash;Lincoln'
-      },
-      {
-        :href => '/',
-        :text => 'UNL Resource Scheduler'
-      }
-    ]
-
-    session[:init] = true
-
-    # check if the user is currently logged in
-    if session.has_key?(:user_id)
-        @user = (User.includes(:permissions).find(session[:user_id]) rescue nil)
-    else
-        @user = nil;
-    end
+  require_login
+
+  # site defaults
+  @title = 'UNL Resource Scheduler'
+  @breadcrumbs = [
+    {
+      :href => 'http://www.unl.edu/',
+      :text => 'UNL',
+      :title => 'University of Nebraska&ndash;Lincoln'
+    },
+    {
+      :href => '/',
+      :text => 'UNL Resource Scheduler'
+    }
+  ]
+end
+
+def calculate_time(date_string, hour, minute, am_pm)
+  hour ||= 0
+  minute ||= 0
+  am_pm ||= 'am'
+
+  hour = hour.to_i % 12
+  hour = hour + 12 if am_pm == 'pm'
+
+  date_strings = date_string.split('/')
+  date_string = "#{date_strings[2]}-#{date_strings[0]}-#{date_strings[1]}"
+  date = Time.parse(date_string)
+  Time.new(date.year, date.month, date.day, hour, minute, 0)
 end
 
 helpers do
@@ -51,12 +71,43 @@ helpers do
     space = ServiceSpace.find_by(:url_name => url_name)
     raise Sinatra::NotFound if space.nil?
     @space = space
+
+    if !@user.in_space(@space)
+      flash :error, 'Unauthorized', 'Sorry, you don\'t have access to this service space.'
+      redirect '/'
+    end
+
+    @breadcrumbs << {:text => @space.name, :href => @space.href}
   end
 
   def require_login
-    if @user.nil?
-      flash(:alert, 'You Must Login', 'That page requires you to be logged in. If you don\'t have an account, please sign up for <a href="/new_members/">New&nbsp;Member&nbsp;Orientation</a>.')
-      redirect '/login/'
+    if session['cas'].nil? || session['cas']['user'].nil?
+      halt 401
+    else
+      # check if the user already exists in this app's db
+      @user = User.find_by(:username => session['cas']['user'], :creation_method => 'CAS')
+      if @user.nil?
+        # get this user's info from UNL Directory
+        RestClient.get("#{DIRECTORY_URL}?uid=#{session['cas']['user']}&format=json") do |response, request, result|
+          case response.code
+          when 200
+            info = JSON.parse(response.body)
+            # create this user
+            @user = User.create(
+              username: session['cas']['user'],
+              email: info['mail'][0],
+              first_name: info['givenName'][0],
+              last_name: info['sn'][0],
+              date_created: Time.now,
+              creation_method: 'CAS',
+              is_admin: false
+            )
+          else
+            flash :error, 'User Not Found in UNL Directory', "This user was not found in the UNL Directory."
+            redirect back
+          end
+        end
+      end
     end
   end
 end
@@ -74,7 +125,20 @@ end
 
 get '/' do
   @breadcrumbs << {:text => 'Home'}
-  erb 'Home', :layout => :fixed
+  # get the service spaces that this user has access to
+  spaces = @user.service_spaces
+  erb :home, :layout => :fixed, :locals => {
+    spaces: spaces
+  }
+end
+
+get '/images/:event_id/?' do
+  event = Event.find_by(:id => params[:event_id])
+  if event.nil? || event.imagedata.nil?
+    raise Sinatra::NotFound
+  end
+
+  return event.imagedata
 end
 
 Dir.glob("#{ROOT}/routes/*.rb") { |file| require file }
\ No newline at end of file
diff --git a/db/migrate/20160608113900_add_session_store.rb b/db/migrate/20160608113900_add_session_store.rb
new file mode 100644
index 0000000..711932b
--- /dev/null
+++ b/db/migrate/20160608113900_add_session_store.rb
@@ -0,0 +1,13 @@
+require 'active_record'
+
+class AddSessionStore < ActiveRecord::Migration
+	def change
+		create_table :sessions do |t|
+			t.string :cas_ticket
+			t.string :session_id
+			t.text :data
+			t.datetime :created_at
+			t.datetime :updated_at
+		end
+	end
+end
\ No newline at end of file
diff --git a/db/migrate/20160608134500_add_creation_method.rb b/db/migrate/20160608134500_add_creation_method.rb
new file mode 100644
index 0000000..4acbf33
--- /dev/null
+++ b/db/migrate/20160608134500_add_creation_method.rb
@@ -0,0 +1,7 @@
+require 'active_record'
+
+class AddCreationMethod < ActiveRecord::Migration
+	def change
+		add_column :users, :creation_method, :string
+	end
+end
\ No newline at end of file
diff --git a/db/migrate/20160608140300_add_space_permissions.rb b/db/migrate/20160608140300_add_space_permissions.rb
new file mode 100644
index 0000000..0fe2f83
--- /dev/null
+++ b/db/migrate/20160608140300_add_space_permissions.rb
@@ -0,0 +1,16 @@
+require 'active_record'
+require 'models/permission'
+
+class AddSpacePermissions < ActiveRecord::Migration
+	def up
+		Permission.create(:name => 'User Access', :id => 8)
+
+		add_column :user_has_permissions, :service_space_id, :integer, :default => 1
+	end
+
+	def down
+		remove_column :user_has_permissions, :service_space_id, :integer, :default => 1
+
+		Permission.find_by(:id => 8).delete
+	end
+end
\ No newline at end of file
diff --git a/models/event.rb b/models/event.rb
index 04cb86d..81f2731 100644
--- a/models/event.rb
+++ b/models/event.rb
@@ -5,6 +5,7 @@ class Event < ActiveRecord::Base
 	has_one :reservation, :dependent => :destroy
 	belongs_to :location
 	belongs_to :event_type
+	belongs_to :service_space
 	alias_method :type, :event_type
 	alias_method :signups, :event_signups
 
@@ -29,16 +30,11 @@ class Event < ActiveRecord::Base
 	end
 
 	def info_link
-		case type.description
-		when 'New Member Orientation'
-			"/new_members/sign_up/#{id}/"
-		else
-			"/events/#{id}/"
-		end
+		"/#{service_space.url_name}/events/#{id}/"
 	end
 
 	def edit_link
-		"/admin/events/#{id}/edit/"
+		"/#{service_space.url_name}/admin/events/#{id}/edit/"
 	end
 
 	def has_reservation
@@ -57,7 +53,6 @@ class Event < ActiveRecord::Base
 		self.event_type_id = params[:type]
 		self.location_id = params[:location]
 		self.max_signups = params[:limit_signups] == 'on' ? params[:max_signups].to_i : nil
-		self.service_space_id = 1
 		self.save
 	end
 
diff --git a/models/permission.rb b/models/permission.rb
index f11c56c..4c59768 100644
--- a/models/permission.rb
+++ b/models/permission.rb
@@ -8,4 +8,5 @@ class Permission < ActiveRecord::Base
 	MANAGE_SPACE_HOURS = 5
 	MANAGE_EVENTS = 6
 	SEE_AGENDA = 7
+	USER_ACCESS = 8
 end
\ No newline at end of file
diff --git a/models/reservation.rb b/models/reservation.rb
index 24e1998..357973d 100644
--- a/models/reservation.rb
+++ b/models/reservation.rb
@@ -1,4 +1,5 @@
 require 'active_record'
+require 'models/resource'
 
 class Reservation < ActiveRecord::Base
 	belongs_to :resource
diff --git a/models/service_space.rb b/models/service_space.rb
index 149b127..c3ccd2c 100644
--- a/models/service_space.rb
+++ b/models/service_space.rb
@@ -1,5 +1,25 @@
 require 'active_record'
 
 class ServiceSpace < ActiveRecord::Base
-	
+	has_many :users
+
+	def href
+		"/#{self.url_name}/"
+	end
+
+	def calendar_href
+		"/#{self.url_name}/calendar/"
+	end
+
+	def resources_href
+		"/#{self.url_name}/resources/"
+	end
+
+	def admin_href
+		"/#{self.url_name}/admin/"
+	end
+
+	def admin_events_href
+		"/#{self.url_name}/admin/events/"
+	end
 end
\ No newline at end of file
diff --git a/models/user.rb b/models/user.rb
index fd8c0b5..c4f8656 100644
--- a/models/user.rb
+++ b/models/user.rb
@@ -4,13 +4,20 @@ require 'models/resource_authorization'
 require 'models/event_signup'
 require 'models/permission'
 require 'models/user_has_permission'
+require 'models/service_space'
 require 'classes/emailer'
 
 class User < ActiveRecord::Base
     has_many :resource_authorizations
     has_many :event_signups
     has_many :user_has_permissions
-    has_many :permissions, through: :user_has_permissions
+    has_many :permissions, through: :user_has_permissions, source: :permission
+
+    def service_spaces
+        ServiceSpace.all.select do |space|
+            self.in_space(space)
+        end
+    end
 
     def authorized_resource_ids
         self.resource_authorizations.map {|res_auth| res_auth.resource_id}
@@ -24,30 +31,24 @@ class User < ActiveRecord::Base
         self.event_signups.map {|event_signup| event_signup.event_id}
     end
 
-    include BCrypt
-
-    # now decides based on whether they have any admin permissions
-    def is_admin?
-    	!self.permissions.empty?
+    # we decide if the user is an admin if they have any permissions in the space (besides the user access permission, which indicates they are a basic user)
+    def is_admin?(space)
+    	!self.user_has_permissions.where(:service_space_id => space.id).where.not(:permission_id => Permission::USER_ACCESS).empty?
     end
     alias_method :admin?, :is_admin?
 
-    def is_super_user?
-        self.permissions.include?(Permission.find(Permission::SUPER_USER))
+    def is_super_user?(space)
+        !self.user_has_permissions.where(:service_space_id => space.id, :permission_id => Permission::SUPER_USER).empty?
     end
     alias_method :super_user?, :is_super_user?
 
-    def has_permission?(id)
-        self.permissions.include?(Permission.find(id))
+    def has_permission?(id, space)
+        !self.user_has_permissions.where(:service_space_id => space.id, :permission_id => id).empty?
     end
 
-    def password
-        @password ||= Password.new(password_hash)
-    end
-
-    def password=(new_password)
-        @password = Password.create(new_password)
-        self.password_hash = @password
+    # just notes whether they have any permissions in the space, and can access it at all
+    def in_space(space)
+        !self.user_has_permissions.where(:service_space_id => space.id).empty?
     end
 
     def full_name
@@ -57,26 +58,4 @@ class User < ActiveRecord::Base
     def sortable_name
         "#{last_name}, #{first_name}"
     end
-
-    def send_reset_password_email
-        token = ''
-        begin
-          token = String.token
-        end while User.find_by(:reset_password_token => token) != nil
-        self.reset_password_token = token
-        self.reset_password_expiry = Time.now + 1.day
-        self.save
-
-body = <<EMAIL
-<p>We received a request to reset your Innovation Studio Manager password. Please click the link below to reset your password.</p>
-
-<p><a href="http://#{ENV['RACK_ENV'] == 'development' ? 'localhost:9393' : 'innovationstudio-manager.unl.edu'}/reset_password/#{token}/">http://#{ENV['RACK_ENV'] == 'development' ? 'localhost:9393' : 'innovationstudio-manager.unl.edu'}/reset_password/#{token}/</a></p>
-
-<p>This link will only be active for 24 hours. If you did not request to reset your password, you may safely disregard this email.</p>
-
-<p>Nebraska Innovation Studio</p>
-EMAIL
-
-        Emailer.mail(self.email, 'Nebraska Innovation Studio password reset', body)
-    end
 end
\ No newline at end of file
diff --git a/models/user_has_permission.rb b/models/user_has_permission.rb
index 8759fd1..24ec5aa 100644
--- a/models/user_has_permission.rb
+++ b/models/user_has_permission.rb
@@ -3,4 +3,9 @@ require 'active_record'
 class UserHasPermission < ActiveRecord::Base
 	belongs_to :user
  	belongs_to :permission
+ 	belongs_to :service_space
+
+ 	scope :in_space, ->(space) {
+ 		where(:service_space_id => space.id)
+ 	}
 end
\ No newline at end of file
diff --git a/routes/admin.rb b/routes/admin.rb
new file mode 100644
index 0000000..758377f
--- /dev/null
+++ b/routes/admin.rb
@@ -0,0 +1,43 @@
+require 'models/user'
+require 'models/event'
+require 'models/resource'
+require 'models/space_hour'
+require 'models/permission'
+
+before '/:service_space_url_name/admin*' do
+	load_service_space
+
+	raise Sinatra::NotFound unless !@user.nil? && @user.is_admin?(@space)
+end
+
+get '/:service_space_url_name/admin/?' do
+	@breadcrumbs << {:text => 'Admin Home'}
+	user_count = User.where(:service_space_id => @space.id).count
+	upcoming_event_count = Event.where(:service_space_id => @space.id).where('start_time >= ?', Time.now).count
+	resource_count = Resource.where(:service_space_id => @space.id).count
+
+	date = Time.now.midnight
+	# get the hours for this day to show
+	hours = SpaceHour.where(:service_space_id => @space.id)
+		.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+		.order(:effective_date => :desc, :id => :desc).all.to_a
+
+	correct_hour = nil
+
+	hours.each do |space_hour|
+		if date.wday == space_hour.day_of_week && (space_hour.effective_date.in_time_zone.midnight == date.in_time_zone.midnight || (!space_hour.one_off && space_hour.effective_date.in_time_zone.midnight <= date.in_time_zone.midnight))
+			correct_hour = space_hour
+			break
+		end
+	end
+
+	erb :'admin/home', :layout => :fixed, :locals => {
+		:user_count => user_count,
+		:upcoming_event_count => upcoming_event_count,
+		:resource_count => resource_count,
+		:space_hour => correct_hour,
+		:date => date
+	}
+end
+
+Dir.glob("#{ROOT}/routes/admin/*.rb") { |file| require file }
\ No newline at end of file
diff --git a/routes/admin/events.rb b/routes/admin/events.rb
new file mode 100644
index 0000000..3bd265a
--- /dev/null
+++ b/routes/admin/events.rb
@@ -0,0 +1,181 @@
+require 'rest-client'
+require 'models/event'
+require 'models/event_type'
+require 'models/location'
+require 'models/resource'
+
+before '/:service_space_url_name/admin/events*' do
+	unless @user.has_permission?(Permission::MANAGE_EVENTS, @space)
+		raise Sinatra::NotFound
+	end
+end
+
+get '/:service_space_url_name/admin/events/?' do
+	@breadcrumbs << {:text => 'Admin Events'}
+	page = params[:page]
+	page = page.to_i >= 1 ? page.to_i : 1
+	page_size = 10
+	tab = ['upcoming', 'past'].include?(params[:tab]) ? params[:tab] : 'upcoming'
+
+	case tab
+	when 'past'
+		where_clause = 'start_time < ?', Time.now
+		order_clause = {:start_time => :desc}
+	else
+		where_clause = 'start_time >= ?', Time.now
+		order_clause = {:start_time => :asc}
+	end
+
+	iterator = Event.includes(:event_signups).where(:service_space_id => @space.id).where(where_clause)
+
+	erb :'admin/events', :layout => :fixed, :locals => {
+		:events => iterator.order(order_clause).limit(page_size).offset((page-1)*page_size).all,
+		:total_pages => (iterator.count.to_f / page_size).ceil,
+		:page => page,
+		:tab => tab
+	}
+end
+
+get '/:service_space_url_name/admin/events/:event_id/signup_list/?' do
+	@breadcrumbs << {:text => 'Admin Events', :href => '/admin/events/'} << {text: 'Signup List'}
+	event = Event.includes(:event_signups).find_by(:id => params[:event_id], :service_space_id => @space.id)
+	if event.nil?
+		# that event does not exist
+		flash(:danger, 'Not Found', 'That event does not exist')
+		redirect @space.admin_events_href
+	end
+
+	erb :'admin/signup_list', :layout => :fixed, :locals => {
+		:event => event
+	}
+end
+
+get '/:service_space_url_name/admin/events/create/?' do
+	@breadcrumbs << {:text => 'Admin Events', :href => '/admin/events/'} << {text: 'Create Event'}
+	erb :'admin/new_event', :layout => :fixed, :locals => {
+		:event => Event.new,
+		:types => EventType.where(:service_space_id => @space.id).all,
+		:locations => Location.where(:service_space_id => @space.id).all,
+		:resources => Resource.where(:service_space_id => @space.id, :is_reservable => true).all,
+		:on_unl_events => false,
+		:on_main_calendar => false
+	}
+end
+
+post '/:service_space_url_name/admin/events/create/?' do
+	if params[:location] == 'new'
+		# this is a new location, we must create it!
+		location = Location.create(params[:new_location].merge({
+			:service_space_id => @space.id
+		}))
+		params[:location] = location.id
+	end
+
+	event = Event.new
+	event.set_image_data(params)
+	event.set_data(params)
+	event.service_space_id = @space.id
+
+	if params.has_key?('reserve_resource') && params['reserve_resource'] == 'on'
+		# we need to create a reservation for the resource on the appropriate time
+		Reservation.create(
+			:resource_id => params[:resource],
+			:event_id => event.id,
+			:start_time => event.start_time,
+			:end_time => event.end_time,
+			:is_training => true,
+			:user_id => nil
+		)
+	end
+
+	# notify that it worked
+	flash(:success, 'Event Created', "Your #{event.type.description}: #{event.title} has been created.")
+	redirect @space.admin_events_href
+end
+
+get '/:service_space_url_name/admin/events/:event_id/edit/?' do
+	@breadcrumbs << {:text => 'Admin Events', :href => '/admin/events/'} << {text: 'Edit Event'}
+	event = Event.includes(:event_type, :location, :reservation => :resource).find_by(:id => params[:event_id], :service_space_id => @space.id)
+	if event.nil?
+		# that event does not exist
+		flash(:danger, 'Not Found', 'That event does not exist')
+		redirect @space.admin_events_href
+	end
+
+	erb :'admin/new_event', :layout => :fixed, :locals => {
+		:event => event,
+		:types => EventType.where(:service_space_id => @space.id).all,
+		:locations => Location.where(:service_space_id => @space.id).all,
+		:resources => Resource.where(:service_space_id => @space.id, :is_reservable => true).all
+	}
+end
+
+post '/:service_space_url_name/admin/events/:event_id/edit/?' do
+	event = Event.find_by(:id => params[:event_id], :service_space_id => @space.id)
+	if event.nil?
+		# that event does not exist
+		flash(:danger, 'Not Found', 'That event does not exist')
+		redirect @space.admin_events_href
+	end
+
+	if params[:location] == 'new'
+		# this is a new location, we must create it!
+		location = Location.create(params[:new_location].merge({
+			:service_space_id => @space.id
+		}))
+		params[:location] = location.id
+	end
+
+	if params.checked?('remove_image')
+		event.remove_image_data
+	else
+		event.set_image_data(params)
+	end
+	event.set_data(params)
+	event.service_space_id = @space.id
+
+	# check the resource reservation for this
+	checked = params.checked?('reserve_resource')
+	if event.has_reservation && checked
+		# update the reservation
+		event.reservation.update(
+			:resource_id => params[:resource],
+			:event_id => event.id,
+			:start_time => event.start_time,
+			:end_time => event.end_time,
+			:is_training => true,
+			:user_id => nil
+		)
+	elsif event.has_reservation && !checked
+		# remove the reservation
+		event.reservation.delete
+	elsif !event.has_reservation && checked
+		# create the reservation
+		Reservation.create(
+			:resource_id => params[:resource],
+			:event_id => event.id,
+			:start_time => event.start_time,
+			:end_time => event.end_time,
+			:is_training => true,
+			:user_id => nil
+		)
+	end
+
+	# notify that it worked
+	flash(:success, 'Event Updated', "Your #{event.type.description}: #{event.title} has been updated.")
+	redirect @space.admin_events_href
+end
+
+post '/:service_space_url_name/admin/events/:event_id/delete/?' do
+	event = Event.find_by(:id => params[:event_id], :service_space_id => @space.id)
+	if event.nil?
+		# that event does not exist
+		flash(:danger, 'Not Found', 'That event does not exist')
+		redirect @space.admin_events_href
+	end
+
+	event.destroy
+
+	flash(:success, 'Event Deleted', "Your event #{event.title} has been deleted. All signups on this event have also been removed, and if a reservation was attached, it also has been removed.")
+	redirect @space.admin_events_href
+end
\ No newline at end of file
diff --git a/routes/events.rb b/routes/events.rb
new file mode 100644
index 0000000..43bfbbc
--- /dev/null
+++ b/routes/events.rb
@@ -0,0 +1,76 @@
+require 'models/event'
+require 'models/event_signup'
+
+get '/:service_space_url_name/events/:event_id/?' do
+	load_service_space
+
+	# this is an event details page
+	begin
+		event = Event.includes(:location, :event_type, :event_signups).find(params[:event_id])
+	rescue ActiveRecord::RecordNotFound => e
+		not_found
+	end
+
+	@breadcrumbs << {:text => event.title}
+	erb :event_details, :layout => :fixed, :locals => {
+		:event => event
+	}
+end
+
+post '/:service_space_url_name/events/:event_id/sign_up/?' do
+	load_service_space
+
+	# check that is a valid event
+	event = Event.includes(:event_type).find_by(:service_space_id => @space.id, :id => params[:event_id])
+
+	if event.nil?
+		# that event does not exist
+		flash(:danger, 'Not Found', 'That event does not exist')
+		redirect '/calendar/'
+	end
+
+	if !event.max_signups.nil? && event.signups.count >= event.max_signups
+		# that event is full
+		flash(:danger, 'Event Full', 'Sorry, that event is full.')
+		redirect back
+	end
+
+	EventSignup.create(
+		:event_id => params[:event_id],
+		:name => @user.full_name,
+		:user_id => @user.id,
+		:email => @user.email
+	)
+
+	if event.type.description != 'Free Event'
+		# flash a message that this works
+		flash(:success, "You're signed up!", "Thanks for signing up! Don't forget, #{event.title} is #{event.start_time.in_time_zone.strftime('%A, %B %d at %l:%M %P')}.")
+		redirect back
+	else
+		# flash a message that this works
+		flash(:success, "Event Marked", "This free event has been marked on your hoempage.")
+		redirect back
+	end
+end
+
+post '/:service_space_url_name/events/:event_id/remove_signup/?' do
+	load_service_space
+
+	# get the event
+	event = Event.includes(:event_type).where(:id => params[:event_id]).first
+
+	# check that the signup exists
+	signup = EventSignup.where(:event_id => params[:event_id], :user_id => @user.id).first
+
+	if signup.nil?
+		flash :alert, 'Not Found', 'That signup was not found.'
+		redirect '/home/'
+	end
+	signup.delete
+
+	header = event.type.description == 'Free Event' ? 'Event Removed' : 'Signup Removed'
+	message = event.type.description == 'Free Event' ? "#{event.title} has been removed from your calendar." : "Your signup for #{event.title} has been removed."
+
+	flash :success, header, message
+	redirect '/home/'
+end
\ No newline at end of file
diff --git a/routes/resources.rb b/routes/resources.rb
new file mode 100644
index 0000000..4c86d4a
--- /dev/null
+++ b/routes/resources.rb
@@ -0,0 +1,373 @@
+require 'models/resource'
+require 'models/reservation'
+require 'models/event'
+require 'models/event_type'
+require 'models/event_signup'
+require 'models/space_hour'
+
+get '/:service_space_url_name/resources/?' do
+	load_service_space
+	@breadcrumbs << {:text => 'Resources'}
+
+	# show resources that the user is authorized to use, as well as all those that do not require authorization
+	resources = Resource.where(:service_space_id => @space.id).all.to_a
+	resources.reject! {|resource| resource.needs_authorization && !@user.authorized_resource_ids.include?(resource.id)}
+
+	erb :resources, :layout => :fixed, :locals => {
+		:available_resources => resources
+	}
+end
+
+# form for reserving a resource
+get '/:service_space_url_name/resources/:resource_id/reserve/?' do
+	load_service_space
+	@breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Reserve'}
+
+	# check that the user has authorization to reserve this resource, if resource requires auth
+	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
+	if resource.nil?
+		flash(:alert, 'Not Found', 'That resource does not exist.')
+		redirect @space.resources_href
+	end
+
+	date = params[:date].nil? ? Time.now.midnight.in_time_zone : Time.parse(params[:date]).midnight.in_time_zone
+	# get the studio's hours for this day
+	# is there a one_off
+	space_hour = SpaceHour.where(:service_space_id => @space.id)
+		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+		.where(:day_of_week => date.wday).where(:one_off => true).first
+	if space_hour.nil?
+		space_hour = SpaceHour.where(:service_space_id => @space.id)
+			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+			.where(:day_of_week => date.wday).where(:one_off => false)
+			.order(:effective_date => :desc, :id => :desc).first
+	end
+
+	available_start_times = []
+	# calculate the available start times for reservation
+	if space_hour.nil?
+		start = 0
+		while start + (resource.minutes_per_reservation || 15) <= 1440
+			available_start_times << start
+			start += (resource.minutes_per_reservation || 15)
+		end
+	else
+		space_hour.hours.sort{|x,y| x[:start] <=> y[:start]}.each do |record|
+			if record[:status] == 'open'
+				start = record[:start]
+				while start + (resource.minutes_per_reservation || 15) <= record[:end]
+					available_start_times << start
+					start += (resource.minutes_per_reservation || 15)
+				end
+			end
+		end
+	end
+
+	# filter out times when resource is reserved
+	reservations = Reservation.includes(:event).where(:resource_id => resource.id).in_day(date).all
+	available_start_times = available_start_times - reservations.map{|res|res.start_time.in_time_zone.minutes_after_midnight}
+
+	erb :reserve, :layout => :fixed, :locals => {
+		:resource => resource,
+		:reservations => reservations,
+		:available_start_times => available_start_times,
+		:space_hour => space_hour,
+		:day => date,
+		:reservation => nil
+	}
+end
+
+# submit form for reserving a resource
+post '/:service_space_url_name/resources/:resource_id/reserve/?' do
+	load_service_space
+
+	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
+	if resource.nil?
+		flash(:alert, 'Not Found', 'That resource does not exist.')
+		redirect @space.resources_href
+	end
+
+	if params[:start_minutes].nil?
+		flash(:alert, 'Please specify a start time', 'Please specify a start time for your reservation.')
+		redirect back
+	end
+
+	hour = (params[:start_minutes].to_i / 60).floor
+	am_pm = hour >= 12 ? 'pm' : 'am'
+	hour = hour % 12
+	hour += 12 if hour == 0
+	minutes = params[:start_minutes].to_i % 60
+
+	start_time = calculate_time(params[:date], hour, minutes, am_pm)
+	end_time = start_time + params[:length].to_i.minutes
+
+	date = start_time.midnight
+	# validate that the requested time slot falls within the open hours of the day
+	# get the studio's hours for this day
+	# is there a one_off
+	space_hour = SpaceHour.where(:service_space_id => @space.id)
+		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+		.where(:day_of_week => date.wday).where(:one_off => true).first
+	if space_hour.nil?
+		space_hour = SpaceHour.where(:service_space_id => @space.id)
+			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+			.where(:day_of_week => date.wday).where(:one_off => false)
+			.order(:effective_date => :desc, :id => :desc).first
+	end
+
+	unless space_hour.nil?
+		# figure out where the closed sections need to be
+        # we can assume that all records in this space_hour are non-intertwined
+        closed_start = 0
+        closed_end = 0
+        starts = space_hour.hours.map{|record| record[:start]}
+        ends = space_hour.hours.map{|record| record[:end]}
+        closeds = []
+        (0..1439).each do |j|
+            if starts.include?(j)
+                closed_end = j
+                closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
+                closed_start = 0
+                closed_end = 0
+            end
+            if ends.include?(j)
+                closed_start = j
+            end
+        end 
+        closed_end = 1440
+        closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
+
+		# for each record, ensure that the time does not overlap if the record is not "open"
+		(space_hour.hours + closeds).each do |record|
+			if record[:status] != 'open'
+				start_time_minutes = 60 * start_time.hour + start_time.min
+				end_time_minutes = 60 * end_time.hour + end_time.min
+				if (record[:start]+1..record[:end]-1).include?(start_time_minutes) || (record[:start]+1..record[:end]-1).include?(end_time_minutes) ||
+						(start_time_minutes < record[:start] && end_time_minutes > record[:end])
+					# there is an overlap, this time is invalid
+					flash :alert, 'Invalid Time Slot', 'Sorry, that time slot is invalid for reservations.'
+					redirect back
+				end
+			end
+		end
+	end
+	# if no record studio is open
+
+	# check for possible other reservations during this time period
+	other_reservations = Reservation.where(:resource_id => params[:resource_id]).in_day(date).all
+	other_reservations.each do |reservation|
+		if (start_time >= reservation.start_time && start_time < reservation.end_time) ||
+				(end_time > reservation.start_time && end_time <= reservation.end_time) ||
+				(start_time < reservation.start_time && end_time > reservation.end_time)
+			flash :alert, "Tool is being used.", "Sorry, that resource is reserved during that time period. Please try another time slot."
+			redirect back
+		elsif reservation.user_id == @user.id
+			flash :alert, "Over Limit", "Sorry, you can only reserve this resource once per day. Please try reserving another time slot on another day."
+			redirect back
+		end
+	end
+
+	Reservation.create(
+		:resource_id => resource.id,
+		:event_id => nil,
+		:start_time => start_time,
+		:end_time => end_time,
+		:is_training => false,
+		:user_id => @user.id
+	)
+
+	flash(:success, 'Reservation Created', "You have successfully reserved #{resource.name} for #{params[:length]} minutes at #{start_time.in_time_zone.strftime('%A, %B %d at %l:%M %P')}")
+	redirect @space.resources_href
+end
+
+
+get '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do
+	load_service_space
+	@breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Edit Reservation'}
+
+	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
+	if resource.nil?
+		flash(:alert, 'Not Found', 'That resource does not exist.')
+		redirect @space.resources_href
+	end
+
+	# check that this reservation exists
+	reservation = Reservation.find(params[:reservation_id])
+	if reservation.nil?
+		flash(:alert, 'Not Found', 'That reservation does not exist.')
+		redirect back
+	end
+
+	date = params[:date].nil? ? reservation.start_time.in_time_zone.midnight : Time.parse(params[:date]).midnight.in_time_zone
+	# get the studio's hours for this day
+	# is there a one_off
+	space_hour = SpaceHour.where(:service_space_id => @space.id)
+		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+		.where(:day_of_week => date.wday).where(:one_off => true).first
+	if space_hour.nil?
+		space_hour = SpaceHour.where(:service_space_id => @space.id)
+			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+			.where(:day_of_week => date.wday).where(:one_off => false)
+			.order(:effective_date => :desc, :id => :desc).first
+	end
+
+	available_start_times = []
+	# calculate the available start times for reservation
+	if space_hour.nil?
+		start = 0
+		while start + (resource.minutes_per_reservation || 15) <= 1440
+			available_start_times << start
+			start += (resource.minutes_per_reservation || 15)
+		end
+	else
+		space_hour.hours.sort{|x,y| x[:start] <=> y[:start]}.each do |record|
+			if record[:status] == 'open'
+				start = record[:start]
+				while start + (resource.minutes_per_reservation || 15) <= record[:end]
+					available_start_times << start
+					start += (resource.minutes_per_reservation || 15)
+				end
+			end
+		end
+	end
+
+	# filter out times when resource is reserved
+	reservations = Reservation.includes(:event).where(:resource_id => resource.id).in_day(date).all
+	available_start_times = (available_start_times - reservations.map{|res|res.start_time.in_time_zone.minutes_after_midnight})
+	if date == reservation.start_time.in_time_zone.midnight
+		available_start_times = available_start_times + [reservation.start_time.in_time_zone.minutes_after_midnight]
+	end
+	available_start_times.sort!
+
+	erb :reserve, :layout => :fixed, :locals => {
+		:resource => resource,
+		:reservations => reservations,
+		:available_start_times => available_start_times,
+		:space_hour => space_hour,
+		:day => date,
+		:reservation => reservation
+	}
+end
+
+post '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do
+	load_service_space
+
+	resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id])
+	if resource.nil?
+		flash(:alert, 'Not Found', 'That resource does not exist.')
+		redirect @space.resources_href
+	end
+
+	# check that this reservation exists
+	reservation = Reservation.find(params[:reservation_id])
+	if reservation.nil?
+		flash(:alert, 'Not Found', 'That reservation does not exist.')
+		redirect back
+	end
+
+	hour = (params[:start_minutes].to_i / 60).floor
+	am_pm = hour >= 12 ? 'pm' : 'am'
+	hour = hour % 12
+	hour += 12 if hour == 0
+	minutes = params[:start_minutes].to_i % 60
+
+	start_time = calculate_time(params[:date], hour, minutes, am_pm)
+	end_time = start_time + params[:length].to_i.minutes
+
+	date = start_time.midnight
+	# validate that the requested time slot falls within the open hours of the day
+	# get the studio's hours for this day
+	# is there a one_off
+	space_hour = SpaceHour.where(:service_space_id => @space.id)
+		.where('effective_date = ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+		.where(:day_of_week => date.wday).where(:one_off => true).first
+	if space_hour.nil?
+		space_hour = SpaceHour.where(:service_space_id => @space.id)
+			.where('effective_date <= ?', date.utc.strftime('%Y-%m-%d %H:%M:%S'))
+			.where(:day_of_week => date.wday).where(:one_off => false)
+			.order(:effective_date => :desc, :id => :desc).first
+	end
+
+	unless space_hour.nil?
+		# figure out where the closed sections need to be
+        # we can assume that all records in this space_hour are non-intertwined
+        closed_start = 0
+        closed_end = 0
+        starts = space_hour.hours.map{|record| record[:start]}
+        ends = space_hour.hours.map{|record| record[:end]}
+        closeds = []
+        (0..1439).each do |j|
+            if starts.include?(j)
+                closed_end = j
+                closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
+                closed_start = 0
+                closed_end = 0
+            end
+            if ends.include?(j)
+                closed_start = j
+            end
+        end 
+        closed_end = 1440
+        closeds << {:status => 'closed', :start => closed_start, :end => closed_end}
+
+		# for each record, ensure that the time does not overlap if the record is not "open"
+		(space_hour.hours + closeds).each do |record|
+			if record[:status] != 'open'
+				start_time_minutes = 60 * start_time.hour + start_time.min
+				end_time_minutes = 60 * end_time.hour + end_time.min
+				if (record[:start]+1..record[:end]-1).include?(start_time_minutes) || (record[:start]+1..record[:end]-1).include?(end_time_minutes) ||
+						(start_time_minutes < record[:start] && end_time_minutes > record[:end])
+					# there is an overlap, this time is invalid
+					flash :alert, 'Invalid Time Slot', 'Sorry, that time slot is invalid for reservations.'
+					redirect back
+				end
+			end
+		end
+	end
+	# if no record studio is open
+
+	# check for possible other reservations during this time period
+	other_reservations = Reservation.where(:resource_id => params[:resource_id]).where.not(:id => reservation.id).in_day(date).all
+	other_reservations.each do |reservation|
+		if (start_time >= reservation.start_time && start_time < reservation.end_time) ||
+				(end_time >= reservation.start_time && end_time < reservation.end_time) ||
+				(start_time < reservation.start_time && end_time > reservation.end_time)
+			flash :alert, "Tool is being used.", "Sorry, that resource is reserved during that time period. Please try another time slot."
+			redirect back
+		elsif reservation.user_id == @user.id
+			flash :alert, "Over Limit", "Sorry, you can only reserve this resource once per day. Please try reserving another time slot on another day."
+			redirect back
+		end
+	end
+
+	reservation.update(
+		:start_time => start_time,
+		:end_time => end_time
+	)
+
+	flash(:success, 'Reservation Updated', "You have successfully updated your reservation for #{resource.name}: it is now for #{params[:length]} minutes at #{start_time.in_time_zone.strftime('%A, %B %d at %l:%M %P')}")
+	redirect back
+end
+
+post '/:service_space_url_name/resources/:resource_id/cancel/:reservation_id/?' do
+	load_service_space
+
+	# check that the user requesting cancel is the same as the one on the reservation
+	reservation = Reservation.find(params[:reservation_id])
+	if reservation.nil?
+		flash :alert, 'Not Found', 'That reservation was not found.'
+		redirect back
+	end
+
+	if reservation.user_id != @user.id
+		flash :alert, 'Unauthorized', 'That is not your reservation.'
+		redirect back
+	end
+
+	reservation.delete
+
+	flash :success, 'Reservation Cancelled', 'Your reservation has been removed.'
+	redirect back
+end
+
+
diff --git a/routes/space.rb b/routes/space.rb
new file mode 100644
index 0000000..3aa6969
--- /dev/null
+++ b/routes/space.rb
@@ -0,0 +1,23 @@
+require 'models/service_space'
+require 'models/reservation'
+require 'models/event'
+
+get '/:service_space_url_name/?' do
+	load_service_space
+
+	reservations = Reservation.joins(:resource).includes(:event).
+		where(:resources => {:service_space_id => @space.id}).
+		where(:user_id => @user.id).
+		where('end_time >= ?', Time.now).
+		order(:start_time).all
+
+	events = Event.includes(:event_type).joins(:event_signups).
+		where(:event_signups => {:user_id => @user.id}, :service_space_id => @space.id).
+		where('end_time >= ?', Time.now).
+		order(:start_time).all
+
+	erb :space_home, :layout => :fixed, :locals => {
+		:reservations => reservations,
+		:events => events
+	}
+end
\ No newline at end of file
diff --git a/views/admin/events.erb b/views/admin/events.erb
new file mode 100644
index 0000000..f5070f7
--- /dev/null
+++ b/views/admin/events.erb
@@ -0,0 +1,110 @@
+<section class="wdn-grid-set reverse">
+    <div class="bp2-wdn-col-three-fourths">
+        <div id="pagetitle">
+        <h3>Events</h3>
+        </div>
+        <ul class="wdn_tabs">
+            <li class="<%='selected' if tab == 'upcoming' %>"><a href="?tab=upcoming">Upcoming</a></li>
+            <li class="<%='selected' if tab == 'past' %>"><a href="?tab=past">Past</a></li>
+        </ul>
+        <div class="wdn_tabs_content">
+            <div class="event-page">
+                <table class="event-list">
+                    <thead>
+                        <tr>
+                            <th>Title</th>
+                            <th>Date/Location</th>
+                            <th>Signups</th>
+                            <th>Actions</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                    <% events.each do |event| %>
+                        <tr>
+                            <td class="small-hidden">
+                                <a href="<%= @space.admin_events_href %><%= event.id %>/edit/"><%= event.title %></a>
+                            </td>
+                            <td>
+                                <ul>
+                                    <li>
+                                        <%= event.start_time.in_time_zone.strftime('%m/%d/%Y @ %l:%M %P') %><br>
+                                        <%= event.location.name %>
+                                    </li>
+                                </ul>
+                            </td>
+                            <td>
+                                <a href="<%= @space.admin_events_href %><%= event.id %>/signup_list/"><%= event.signups.count %> signed up</a>
+                                <% unless event.max_signups.nil? %>
+                                <br><%= event.max_signups %> total slots
+                                <% end %>
+                            </td>
+                            <td>
+                            <form class="delete-event delete-form" action="<%= @space.admin_events_href %><%=event.id%>/delete/" method="POST">
+                            <button type="submit" class="wdn-button">Delete</button>
+                            </form>
+                            </td>
+                        </tr>
+                    <% end %>
+                    </tbody>
+                </table>
+            </div>
+            <% if total_pages > 1 %>
+                <script type="text/javascript">
+                WDN.loadCSS(WDN.getTemplateFilePath('css/modules/pagination.css'));
+                </script>
+                <div style="text-align: center;">
+                    <div style="display: inline-block;">
+                        <ul id="pending-pagination" class="wdn_pagination" data-tab="pending" style="padding-left: 0;">
+                            <% if page != 1 %>
+                                <li class="arrow prev"><a href="?tab=<%= tab %>&amp;page=<%= page-1 %>" title="Go to the previous page">← prev</a></li>
+                            <% end %>
+                            <% before_ellipsis_shown = false; after_ellipsis_shown = false %>
+                            <% (1..total_pages).each do |i| %>
+                                    <% if i == page %>
+                                        <li class="selected"><span><%= i %></span></li>
+                                    <% elsif (i <= 3 || i >= total_pages - 2 || i == page - 1 || 
+                                                i == page - 2 || i == page + 1 || $i == page + 2) %>
+                                        <li><a href="?tab=<%= tab %>&amp;page=<%= i %>" title="Go to page <%= i %>"><%= i %></a></li>
+                                    <% elsif (i < page && !before_ellipsis_shown) %>
+                                        <li><span class="ellipsis">...</span></li>
+                                        <% before_ellipsis_shown = true %>
+                                    <% elsif (i > page && !after_ellipsis_shown) %>
+                                        <li><span class="ellipsis">...</span></li>
+                                        <% after_ellipsis_shown = true %>
+                                    <% end %>
+                            <% end %>
+                            <% if page != total_pages %>
+                                <li class="arrow next"><a href="?tab=<%= tab %>&amp;page=<%= page+1 %>" title="Go to the next page">next →</a></li>
+                            <% end %>
+                        </ul>
+                    </div>
+                </div>
+            <% end %>
+        </div>
+    </div>
+    <nav class="bp2-wdn-col-one-fourth">
+        <div class="toolbox">
+            <h3>Toolbox</h3>
+            <div class="tools">
+                <div style="text-align: center; margin-bottom: .8em">
+                    <a class="wdn-button wdn-button-brand" href="<%= @space.admin_events_href %>create/">
+                    <span style="font-size: 2em; vertical-align: middle; font-weight: 600">+</span>
+                    <span style="vertical-align: middle;">New Event</span>
+                    </a>
+                </div>
+            </div>
+        </div>
+    </nav>
+</section>
+
+<script type="text/javascript">
+require(['jquery'], function($) {
+    $(document).ready(function() {
+        $('.delete-event').submit(function (submit) {
+            if (!window.confirm('Are you sure you want to delete this event?')) {
+                submit.preventDefault();
+            }
+        });
+    });
+});
+</script>
\ No newline at end of file
diff --git a/views/admin/home.erb b/views/admin/home.erb
new file mode 100644
index 0000000..d4240e1
--- /dev/null
+++ b/views/admin/home.erb
@@ -0,0 +1,62 @@
+<div id="pagetitle">
+	<h3>NIS Manager Administration</h3>
+	<span class="wdn-subhead">Hello, <%= @user.full_name %> (<%= @user.username %>) <% if @user.is_super_user?(@space) %><br>Super User<% end %></span>
+</div>
+
+<table>
+	<tbody>
+		<% if @user.has_permission?(Permission::MANAGE_USERS, @space) || @user.has_permission?(Permission::SUPER_USER, @space) %>
+		<tr>
+			<td><strong>Users</strong></td>
+			<td><%= user_count %> users</td>
+			<td><a class="wdn-button wdn-button-brand" href="/admin/users/">Manage</a></td>
+		</tr>
+		<% end %>
+		<% if @user.has_permission?(Permission::MANAGE_EVENTS, @space) %>
+		<tr>
+			<td><strong>Events</strong></td>
+			<td><%= upcoming_event_count %> upcoming events</td>
+			<td><a class="wdn-button wdn-button-brand" href="<%= @space.admin_events_href %>">Manage</a></td>
+		</tr>
+		<% end %>
+		<% if @user.has_permission?(Permission::MANAGE_RESOURCES, @space) %>
+		<tr>
+			<td><strong>Resources</strong></td>
+			<td><%= resource_count %> resources</td>
+			<td><a class="wdn-button wdn-button-brand" href="/admin/tools/">Manage</a></td>
+		</tr>
+		<% end %>
+		<% if @user.has_permission?(Permission::MANAGE_SPACE_HOURS, @space) %>
+		<tr>
+			<td><strong>Hours</strong></td>
+			<td>Today: 
+<% unless space_hour.nil? %>
+    <%= space_hour.hours.map do |record|
+        start_time = date + record[:start].minutes
+        end_time = date + record[:end].minutes
+        "#{record[:status].capitalize_all}: #{start_time.in_time_zone.strftime('%l:%M %P')} - #{end_time.in_time_zone.strftime('%l:%M %P')}"
+    end.join(', ') %>
+<% else %>
+    The space is open all day.
+<% end %>
+			</td>
+			<td><a class="wdn-button wdn-button-brand" href="/admin/hours/">Manage</a></td>
+		</tr>
+		<% end %>
+		<% if @user.has_permission?(Permission::MANAGE_EMAILS, @space) %>
+		<tr>
+			<td><strong>Emails</strong></td>
+			<td></td>
+			<td><a class="wdn-button wdn-button-brand" href="/admin/emails/">Manage</a></td>
+		</tr>
+		<% end %>
+		<% if @user.has_permission?(Permission::SEE_AGENDA, @space) %>
+		<tr>
+			<td><strong>Agenda</strong></td>
+			<td></td>
+			<td><a class="wdn-button wdn-button-brand" href="/admin/agenda/">View</a></td>
+		</tr>
+		<% end %>
+	</tbody>
+</table>
+
diff --git a/views/admin/new_event.erb b/views/admin/new_event.erb
new file mode 100644
index 0000000..1bfa18d
--- /dev/null
+++ b/views/admin/new_event.erb
@@ -0,0 +1,177 @@
+<div class="wdn-grid-set">
+    <form id="create-event-form" action="" method="POST" enctype="multipart/form-data">
+        <div class="bp3-wdn-col-two-thirds">
+            <fieldset>
+                <legend style="margin-top: 0">Event Details</legend>
+                <label for="title"><span class="required">*</span> Title</label>
+                <input type="text" id="title" name="title" value="<%= event.title %>"/>
+
+                <label for="description">Description</label>
+                <textarea rows="4" id="description" name="description"><%= event.description %></textarea>
+
+                <label for="type">Type</label>
+                <select id="type" name="type" class="use-select2" style="width: 100%;">
+                <% types.each do |type| %>
+                    <option <%= 'selected="selected"' if !event.type.nil? && event.type.id == type.id %> value="<%= type.id %>"><%= type.description %></option>
+                <% end %>
+                </select>
+                <br><br>
+                <div>
+                    <input type="checkbox" <%= 'checked="checked"' unless event.max_signups.nil? %> id="limit-signups" name="limit_signups"><label for="limit-signups">Limit signups for this event to: </label>
+                    <input value="<%= event.max_signups %>" type="number" id="max-signups" name="max_signups" style="width: 100px;" />
+                </div>
+            </fieldset>
+
+            <fieldset>
+            <legend style="font-size: 1.6em">Location, Date, and Time</legend>
+                <label for="location"><span class="required">*</span> Location</label>
+                <select id="location" name="location" class="use-select2" style="width: 100%;">
+                    <% locations.each do |location| %>
+                        <option <%= 'selected="selected"' if !event.location.nil? && event.location.id == location.id %> value="<%= location.id %>"><%= location.name %></option>
+                    <% end %>
+                    <option value="new">-- New Location --</option>
+                </select>
+
+                <div style="display: none;" class="offset-field-group" id="new-location-details">
+                    <label for="location-name"><span class="required">*</span> Name</label>
+                    <input type="text" id="location-name" name="new_location[name]" />
+
+                    <label for="location-address">Address</label>
+                    <input type="text" id="location-address" name="new_location[streetaddress]" />
+
+                    <label for="location-address2">Address 2</label>
+                    <input type="text" id="location-address2" name="new_location[streetaddress2]" />
+
+                    <label for="location-city">City</label>
+                    <input type="text" id="location-city" name="new_location[city]" />
+
+                    <label for="location-state">State</label>
+                    <input type="text" id="location-state" name="new_location[state]" />
+
+                    <label for="location-zip">Zip</label>
+                    <input type="text" id="location-zip" name="new_location[zip]" />
+
+                    <label for="location-additionalinfo">Additional Info</label>
+                    <input type="text" id="location-additionalinfo" name="new_location[additionalinfo]" />
+
+                    <label>* This location will be saved for future use</label>
+                </div>
+
+                <div>
+                    <input type="checkbox" <%= 'checked="checked"' if !event.reservation.nil? && !event.reservation.resource.nil? %> id="reserve-resource" name="reserve_resource"><label for="reserve-resource">Reserve a resource for this event</label>
+                    <div id="resources-for-reserving" style="<%= 'display: none;' if event.reservation.nil? || event.reservation.resource.nil? %>">
+                        <label for="resource">Resource</label>
+                        <select id="resource" name="resource" class="use-select2" style="width: 100%;">
+                            <% resources.each do |resource| %>
+                                <option <%= 'selected="selected"' if !event.reservation.nil? && !event.reservation.resource.nil? && event.reservation.resource.id == resource.id %> value="<%= resource.id %>"><%= "#{resource.name} - #{resource.model}" %></option>
+                            <% end %>
+                        </select>
+                    </div>
+                </div>
+                <br>
+
+                <label for="start-date" ><span class="required">*</span> Start Date &amp; Time</label>
+                <div class="date-time-select"><span class="wdn-icon-calendar"></span>
+                    <input id="start-date" value="<%= event.start_time.in_time_zone.strftime('%m/%d/%Y') if !event.start_time.nil? %>" name="start_date" title="Start Date" type="text" class="datepicker" /><br class="hidden small-block"> @
+                    <select id="start-time-hour" name="start_time_hour" title="Start Time Hour">
+                        <option value=""></option>
+                        <% (1..12).each do |i| %>
+                        <option <%= 'selected="selected"' if !event.start_time.nil? && event.start_time.in_time_zone.hour.to_i % 12 == i % 12 %> value="<%= i %>"><%= i %></option>
+                        <% end %>
+                    </select> : 
+
+                    <select id="start-time-minute" name="start_time_minute" title="Start Time Minute">
+                        <option value=""></option>
+                        <% (0..11).each do |i| %>
+                        <option <%= 'selected="selected"' if !event.start_time.nil? && event.start_time.in_time_zone.min == i*5 %> value="<%= i * 5 %>"><%= (i*5).to_s.rjust(2, '0') %></option>
+                        <% end %>
+                    </select>
+
+                    <div id="start-time-am-pm" class="am_pm">
+                        <input <%= 'checked="checked"' if event.start_time.nil? || event.start_time.in_time_zone.hour < 12 %> id="start-time-am-pm-am" title="AM" type="radio" value="am" name="start_time_am_pm">AM<br>
+                        <input <%= 'checked="checked"' if !event.start_time.nil? && event.start_time.in_time_zone.hour >= 12 %> id="start-time-am-pm-pm" title="PM" type="radio" value="pm" name="start_time_am_pm">PM
+                    </div>
+                </div>
+
+                <label for="end-date">End Date &amp; Time (optional)</label>
+                <div class="date-time-select"><span class="wdn-icon-calendar"></span>
+                    <input id="end-date" value="<%= event.end_time.in_time_zone.strftime('%m/%d/%Y') if !event.end_time.nil? %>" name="end_date" title="End Date" type="text" class="datepicker" /><br class="hidden small-block"> @
+                    <select id="end-time-hour" name="end_time_hour" title="End Time Hour">
+                        <option value=""></option>
+                        <% (1..12).each do |i| %>
+                        <option <%= 'selected="selected"' if !event.end_time.nil? && event.end_time.in_time_zone.hour.to_i % 12 == i % 12 %> value="<%= i %>"><%= i %></option>
+                        <% end %>
+                    </select> : 
+
+                    <select id="end-time-minute" name="end_time_minute" title="End Time Minute">
+                        <option value=""></option>
+                        <% (0..11).each do |i| %>
+                        <option <%= 'selected="selected"' if !event.end_time.nil? && event.end_time.in_time_zone.min == i*5 %> value="<%= i * 5 %>"><%= (i*5).to_s.rjust(2, '0') %></option>
+                        <% end %>
+                    </select>
+
+                    <div id="end-time-am-pm" class="am_pm">
+                        <input <%= 'checked="checked"' if event.end_time.nil? || event.end_time.in_time_zone.hour < 12 %> id="end-time-am-pm-am" title="AM" type="radio" value="am" name="end_time_am_pm">AM<br>
+                        <input <%= 'checked="checked"' if !event.end_time.nil? && event.end_time.in_time_zone.hour >= 12 %> id="end-time-am-pm-pm" title="PM" type="radio" value="pm" name="end_time_am_pm">PM
+                    </div>
+                </div>
+            </fieldset>
+
+        </div>
+        <div class="bp3-wdn-col-one-third">
+            <div class="visual-island">
+                <div class="vi-header">
+                    <label>Image</label>
+                </div>
+
+                <div class="details">
+                    <% unless event.imagedata.nil? %>
+                        <img src="<%= event.image_src %>" alt="Image for Event <%= event.title %>">
+                        <br>
+                        <input type="checkbox" name="remove_image" id="remove-image">
+                        <label for="remove-image">Remove Image</label> 
+                    <% end %>
+                    <input style="font-size: 10px;" type="file" name="imagedata" id="imagedata" title="Event Image">
+                </div>
+            </div>
+        </div>
+
+        <div class="bp1-wdn-col-two-thirds">
+            <button class="wdn-button wdn-button-brand wdn-pull-left" type="submit"><%= event.id.nil? ? 'Create' : 'Save' %> Event</button>
+        </div>
+    </form>
+</div>
+<br>
+
+<script type="text/javascript">
+WDN.initializePlugin('jqueryui', [function() {  
+    $ = require('jquery');
+    $('.datepicker').datepicker();
+    $("LINK[href^='//unlcms.unl.edu/wdn/templates_4.0/scripts/plugins/ui/css/jquery-ui.min.css']").remove();
+
+    $('#reserve-resource').click(function(click) {
+        if ($('#reserve-resource').is(':checked')) {
+            $('#resources-for-reserving').show();
+        } else {
+            $('#resources-for-reserving').hide();
+        }
+    });
+
+    $('#location').change(function (change) {
+        if ($(this).val() == 'new') {
+            $('#new-location-details').show();
+        } else {
+            $('#new-location-details').hide();
+        }
+    }).change();
+
+    $('#export-to-unl-events').change(function (change) {
+        if ($('#export-to-unl-events').is(':checked')) {
+            $('#consider-for-unl-main').removeAttr('disabled');
+        } else {
+            $('#consider-for-unl-main').attr('checked', false);
+            $('#consider-for-unl-main').attr('disabled', 'disabled');
+        }
+    }).change();
+}]);
+</script>
\ No newline at end of file
diff --git a/views/calendar.erb b/views/calendar.erb
index cf013cf..b574847 100644
--- a/views/calendar.erb
+++ b/views/calendar.erb
@@ -13,8 +13,8 @@ end %>
 <h4 style="text-align: center; margin: 0;">
 <%= month = sunday.strftime('%B %Y') %><%= (month2 = (sunday+6.days).strftime('%B %Y')) == month ? '' : " - #{month2}" %>
 </h4>
-<a href="/calendar/?date=<%= (date-7.days).strftime('%Y-%m-%d') %>" class="wdn-button wdn-button-triad" id="prev-week">&lt; PREV</a>
-<a href="/calendar/?date=<%= (date+7.days).strftime('%Y-%m-%d') %>" class="wdn-button wdn-button-triad" style="float: right;" id="next-week">NEXT &gt;</a>
+<a href="/<%= @space.url_name %>/calendar/?date=<%= (date-7.days).strftime('%Y-%m-%d') %>" class="wdn-button wdn-button-triad" id="prev-week">&lt; PREV</a>
+<a href="/<%= @space.url_name %>/calendar/?date=<%= (date+7.days).strftime('%Y-%m-%d') %>" class="wdn-button wdn-button-triad" style="float: right;" id="next-week">NEXT &gt;</a>
 </div>
 
 <div class="calendar-container">
diff --git a/views/event_details.erb b/views/event_details.erb
new file mode 100644
index 0000000..82ab061
--- /dev/null
+++ b/views/event_details.erb
@@ -0,0 +1,62 @@
+<div class="event-details">
+	<div>
+		<h3>
+	    	<%= event.title %><span class="wdn-subhead"><%= event.type.description %></span>
+	    </h3>
+	</div>
+	<div>
+		<span class="date-wrapper eventicon-calendar-empty">
+	    	<time class="dtstart"><%= event.start_time.in_time_zone.strftime('%b %d, %Y') %></time>
+		</span>
+		<span class="time-wrapper eventicon-clock">
+	    	<%= event.start_time.in_time_zone.strftime('%l:%M %P') %>–<%= event.end_time.in_time_zone.strftime('%l:%M %P') %>
+	    </span>
+		<div class="location eventicon-location">
+			<%= event.location.name %>
+		</div>
+		<% unless event.imagedata.nil? %>
+		<div class="inset-image">
+			<img src="<%= event.image_src %>" alt="Image for Event: <%= event.title %>">
+		</div>
+		<% end %>
+		<div class="description">
+	    	<%= event.description.nl2br.force_encoding("UTF-8") %>
+	    </div>
+	</div>
+</div>
+
+<% # free events do not require signup but users can mark it on their homepage %>
+<% if @user && event.signups.map(&:user_id).include?(@user.id) %>
+	<% # the user is already signed up %>
+	<% if event.type.description == 'Free Event' %>
+		This event is noted on your homepage.<br>
+		<a href="<%= @space.href %>" class="wdn-button wdn-button-triad">View Homepage</a>
+	<% else %>
+		You have signed up for this event.<br>
+		<a href='<%= @space.href %>' class="wdn-button wdn-button-triad">View Homepage</a>
+	<% end %>
+<% elsif @user %>
+	<% # the user is logged in but not signed up %>
+	<% if event.max_signups.nil? || event.signups.count < event.max_signups %>
+	<form action="/<%= @space.url_name %>/events/<%= event.id %>/sign_up/" method="POST">
+	<button type="submit" class="wdn-button wdn-button-brand">
+		<% if event.type.description == 'Free Event' %>
+			Note event on my homepage
+		<% else %>
+			Sign up for this event
+		<% end %>
+	</a>
+	</form>
+	<% else %>
+	All slots for this event are filled.
+	<% end %>
+<% else %>
+	<% # a non user. May still sign up for the event UNLESS it is a tool training %>
+	<% if event.type.description != 'Machine Training' %>
+		<% if event.max_signups.nil? || event.signups.count < event.max_signups %>
+		<a class="wdn-button wdn-button-brand" href="/<%= @space.url_name %>/events/<%= event.id %>/sign_up_as_non_member/">Sign up for this event</a>
+		<% else %>
+		All slots for this event are filled.
+		<% end %>
+	<% end %>
+<% end %>
\ No newline at end of file
diff --git a/views/home.erb b/views/home.erb
new file mode 100644
index 0000000..5a6605c
--- /dev/null
+++ b/views/home.erb
@@ -0,0 +1,16 @@
+<div id="pagetitle">
+	<h3>Welcome to UNL Resource Scheduler<span class="wdn-subhead"><%= @user.full_name %></span></h3>
+</div>
+
+<% if spaces.empty? %>
+	Sorry, you don't have any spaces right now. If you'd like to use UNL Resource Scheduler for your organization, please request a service space here.
+<% else %>
+<h5 class="wdn-center wdn-brand">Your Service Spaces</h5>
+<div class="wdn-grid-set-thirds">
+<% spaces.each do |space| %>
+	<div class="wdn-col">
+		<a href="<%= space.href %>"><%= space.name %></a>
+	</div>
+<% end %>
+</div>
+<% end %>
\ No newline at end of file
diff --git a/views/reserve.erb b/views/reserve.erb
new file mode 100644
index 0000000..ec07065
--- /dev/null
+++ b/views/reserve.erb
@@ -0,0 +1,187 @@
+<% 
+start_hour = nil
+start_minute = nil
+start_am_pm = nil
+unless reservation.nil?
+    start_hour = reservation.start_time.in_time_zone.hour
+    if start_hour >= 12
+        start_hour -= 12
+        start_am_pm = 'pm'
+    else
+        start_am_pm = 'am'
+    end
+    start_hour += 12 if start_hour == 0
+    start_minute = reservation.start_time.in_time_zone.min
+end %>
+
+<div id="pagetitle">
+	<h3><%= reservation.nil? ? 'Reserve Time for ' : 'Edit Reservation for ' %><%= resource.name %></h3>
+</div>
+
+<form action="" method="POST">
+<section class="wdn-grid-set">
+    <div class="bp1-wdn-col-one-third">
+    	<label for="date">Date</label>
+    	<div class="date-time-select">
+    	<span class="wdn-icon-calendar"></span>
+        <input style="width: 90%;" id="date" name="date" title="Reservation Date" type="text" class="datepicker" value="<%= day.strftime('%m/%d/%Y') %>" />
+        </div>
+    </div>
+    <div class="bp1-wdn-col-one-third">
+    	<label>Schedule for <span id="current-date"><%= day.strftime('%m/%d/%Y') %></span></label>
+        <div class="calendar-container individual-day">
+            <div class="time-labels">
+                <div class="time-chart">
+                    <% (12..47).each do |j| %>
+                        <div class="calendar-half-hour">
+                            <label><%= "#{(j / 2) % 12 + (j==24?12:0)} #{j>=24?'PM':'AM'}" if j % 2 == 0 %></label>
+                        </div>
+                    <% end %>
+                </div>
+            </div>
+            <% slots = [0] * 36 %>
+            <div class="calendar-day" data-day="<%= day.strftime("%Y/%m/%d") %>">
+                <label class="day-header"><%= day.strftime("%^a %-m/%d") %></label>
+                <div class="day-chart" title="Open">
+                    <% reservations.each do |res| %>
+                        <% end_slot = [(((res.end_time.in_time_zone - day.midnight) / 60 - 360) / 30).floor, 35].min 
+                        if end_slot < 0
+                            next
+                        end
+                        top = (((res.start_time.in_time_zone - day.midnight) / 60 - 360) / 30) * 20
+                        height = res.length * 20 / 30
+                        %>
+                        <div class="reservation <%= 'editing' if !reservation.nil? && reservation.id == res.id %>" 
+                            style="top: <%= top %>px; height: <%= height %>px;">
+                            <% if !res.event.nil? %>
+                                <%= res.event.title %>
+                            <% else %>
+                                <%= res.user_id == @user.id ? 'My Reservation' : 'busy' %>
+                            <% end %>
+                            <%= '(Editing)' if !reservation.nil? && reservation.id == res.id %>
+                        </div>
+                    <% end %>
+                    <% unless space_hour.nil? 
+                        # figure out where the closed divs need to be
+                        # we can assume that all records in this space_hour are non-intertwined
+                        closed_start = 0
+                        closed_end = 0
+                        starts = space_hour.hours.map{|record| record[:start]}
+                        ends = space_hour.hours.map{|record| record[:end]}
+                        %> <%
+                        closeds = []
+                        (360..1439).each do |j|
+                            if starts.include?(j)
+                                closed_end = j
+                                closeds << [closed_start, closed_end]
+                                closed_start = 0
+                                closed_end = 0
+                            end
+                            if ends.include?(j)
+                                closed_start = j
+                            end
+                        end 
+                        closed_end = 1440
+                        closeds << [closed_start, closed_end]
+
+                        closeds.each do |closed|
+                            start_time = closed[0] %>
+                            <% end_time = closed[1] %>
+                            <% 
+                            if [((end_time - 360) / 30).floor, 35].min  < 0
+                                next
+                            end
+                            top = ((start_time - 360) / 30) * 20
+                            height = (end_time - start_time) * 20 / 30
+                            if top < 0
+                                height += top
+                                top = 0
+                            end 
+                            if top + height > 720
+                                height = 720 - top
+                            end
+                            %>
+                            <div class="status closed" title="Closed" style="top: <%= top %>px; height: <%= height %>px;">
+                                &nbsp;
+                            </div>
+                            <%
+                        end %>
+                        <% space_hour.hours.each do |record| %>
+                            <% if record[:status] != 'open' && record[:status] != 'closed' %>
+                                <% start_time = record[:start] %>
+                                <% end_time = record[:end] %>
+                                <% if [((end_time - 360) / 30).floor, 35].min  < 0
+                                    next
+                                end
+                                top = ((start_time - 360) / 30) * 20
+                                height = (end_time - start_time) * 20 / 30
+                                if top < 0
+                                    height += top
+                                    top = 0
+                                end 
+                                if top + height > 720
+                                    height = 720 - top
+                                end
+                                %>
+                                <div title="<%= record[:status].split('_').join(' ').capitalize_all %>" class="status <%= record[:status].downcase.split('_').join('-') %>" style="top: <%= top %>px; height: <%= height %>px;">
+                                    &nbsp;
+                                </div>
+                            <% end %>
+                        <% end %>
+                    <% end %>
+                    <div>
+                    <% (12..47).each do |j| %>
+                        <div class="calendar-half-hour">
+                            &nbsp;
+                        </div>
+                    <% end %>
+                    </div>
+                </div>
+            </div>
+    	</div>
+    </div>
+    <div class="bp1-wdn-col-one-third">
+    	<label for="start-minutes">Start Time</label>
+        <div class="date-time-select">
+            <% if available_start_times.empty? %>
+                No available times today.
+            <% else %>
+                <select id="start-minutes" name="start_minutes" style="width: 90%">
+                    <% available_start_times.each do |minutes| %>
+                        <option <%='selected="selected"' if !reservation.nil? && reservation.start_time.in_time_zone.midnight == day && reservation.start_time.in_time_zone.minutes_after_midnight == minutes %> value="<%= minutes %>"><%= Time.from_minutes(minutes).strftime("%l:%M %p") %></option>
+                    <% end %>
+                </select>
+            <% end %>
+        </div>
+
+        <label for="reservation-length">Reserve resource for:</label><br>
+        <% if resource.minutes_per_reservation.nil? %>
+        <select id="reservation-length" name="length">
+        	<% (1..4).each do |i| %>
+        	<option value="<%=i*15%>"><%=i*15%> minutes</option>
+        	<% end %>
+        	<option value="90">1.5 hours</option>
+        	<option value="120">2 hours</option>
+        </select>
+        <% else %>
+        <input style="width: 50px" disabled="disabled" value="<%= resource.minutes_per_reservation %>" />&nbsp;&nbsp;<label>minutes</label>
+        <input type="hidden" name="length" value="<%= resource.minutes_per_reservation %>" />
+        <% end %>
+        <br><br>
+        <button type="submit" class="wdn-button wdn-button-brand"><%= reservation.nil? ? 'Reserve' : 'Update' %></button>
+    </div>
+</section>
+</form>
+
+<script type="text/javascript">
+WDN.initializePlugin('jqueryui', [function() {  
+    $ = require('jquery');
+    $('.datepicker').datepicker();
+    $("LINK[href^='//unlcms.unl.edu/wdn/templates_4.0/scripts/plugins/ui/css/jquery-ui.min.css']").remove();
+
+    $('#date').change(function () {
+        var date = $('#date').val().split('/');
+        window.location = window.location.href.split('?')[0] + '?date=' + date[2] + '-' + date[0] + '-' + date[1];
+    });
+}]);
+</script>
\ No newline at end of file
diff --git a/views/resources.erb b/views/resources.erb
new file mode 100644
index 0000000..77821f5
--- /dev/null
+++ b/views/resources.erb
@@ -0,0 +1,34 @@
+<div id="pagetitle">
+<h3>Resources You Can Reserve</h3>
+</div>
+
+<% if available_resources.count > 0 %>
+<table>
+    <thead>
+        <tr>
+            <th>Resource</th>
+            <th>Actions</th>
+        </tr>
+    </thead>
+    <tbody>
+    <% available_resources.each do |resource| %>
+        <tr>
+            <td>
+                <%= resource.name %>
+            </td>
+            <td class="table-actions">
+                <% if resource.is_reservable %>
+                    <a href="/<%= @space.url_name %>/resources/<%= resource.id %>/reserve/" class="wdn-button wdn-button-brand">Reserve Time</a>
+                <% else %>
+                    Reservation not required
+                <% end %>
+            </td>
+        </tr>
+    <% end %>
+    </tbody>
+</table>
+<% else %>
+<p>
+    There are no resources currently available for you to reserve.
+</p>
+<% end %>
\ No newline at end of file
diff --git a/views/space_home.erb b/views/space_home.erb
new file mode 100644
index 0000000..7a6f3f6
--- /dev/null
+++ b/views/space_home.erb
@@ -0,0 +1,85 @@
+<div id="pagetitle">
+	<h3>Welcome to <%= @space.name %> Resource Scheduler<span class="wdn-subhead">Hello, <%= @user.full_name %></span></h3>
+</div>
+
+<h4>
+My Reservations
+</h4>
+<% if reservations.empty? %>
+You have no upcoming reservations. You can view upcoming trainings to get certified, or check out the list of resources you can reserve.<br>
+<a href="<%= @space.resources_href %>" class="wdn-button wdn-button-brand">View Resources</a>
+<% else %>
+<table>
+    <thead>
+        <tr>
+            <th>Tool</th>
+            <th>Model</th>
+            <th>Time</th>
+            <th>Actions</th>
+        </tr>
+    </thead>
+    <tbody>
+    <% reservations.each do |reservation| %>
+        <tr>
+            <td>
+                <%= reservation.resource.name %>
+                <% if !reservation.event.nil? %>
+                    <br><small><%= reservation.event.title %></small>
+                <% end %>
+            </td>
+            <td>
+                <%= reservation.resource.model %>
+            </td>
+            <td>
+            	<%= reservation.start_time.in_time_zone.strftime('%m/%d/%Y @ %l:%M %P') %><br>
+            	<%= reservation.length %> minutes
+            </td>
+            <td class="table-actions">
+                <a href="/<%= @space.url_name %>/resources/<%= reservation.resource.id %>/edit_reservation/<%= reservation.id %>/" class="wdn-button wdn-button-brand">Edit</a>
+                <form method="POST" action="/<%= @space.url_name %>/resources/<%= reservation.resource.id %>/cancel/<%= reservation.id %>/" class="delete-form">
+                    <button class="wdn-button" type="submit">Remove</button>
+                </form>
+            </td>
+        </tr>
+    <% end %>
+    </tbody>
+</table>
+<% end %>
+
+<h4>
+My Events
+</h4>
+<% if events.empty? %>
+You have not signed up for any upcoming events. Why not check out the calendar to find some?<br>
+<a href="<%= @space.calendar_href %>" class="wdn-button wdn-button-triad">View Calendar</a>
+<% else %>
+<table>
+    <thead>
+        <tr>
+            <th>Title</th>
+            <th>Date/Location</th>
+            <th>Actions</th>
+        </tr>
+    </thead>
+    <tbody>
+    <% events.each do |event| %>
+        <tr>
+            <td>
+                <a href="<%= event.info_link %>"><%= event.title %></a>
+            </td>
+            <td>
+                <%= event.start_time.in_time_zone.strftime('%m/%d/%Y @ %l:%M %P') %><br>
+                <%= event.location.name %>
+            </td>
+            <td class="table-actions">
+            	<form action="/events/<%= event.id %>/remove_signup/" method="POST" class="delete-form">
+					<button class="wdn-button" type="submit">
+					Remove
+					</button>
+				</form>
+            </td>
+        </tr>
+    <% end %>
+    </tbody>
+</table>
+<% end %>
\ No newline at end of file
diff --git a/views/template_partials/navigation.erb b/views/template_partials/navigation.erb
index 22c6536..7ba36e9 100644
--- a/views/template_partials/navigation.erb
+++ b/views/template_partials/navigation.erb
@@ -1,3 +1,30 @@
 <ul>
-    <li><a href="/" title="UNL Resource Scheduler">Home</a>
+    <li><a href="/" title="UNL Resource Scheduler">Home</a></li>
+    <% unless @space.nil? %>
+    <li><a href="<%= @space.calendar_href %>">Calendar</a></li>
+    <% unless @user.nil? || !@user.is_admin?(@space) %>
+	    <li><a href="<%= @space.admin_href %>" title="Admin">Admin</a>
+            <ul>
+                <% if @user.has_permission?(Permission::MANAGE_USERS, @space) || @user.has_permission?(Permission::SUPER_USER, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/users/" title="Users">Users</a></li>
+                <% end %>
+                <% if @user.has_permission?(Permission::MANAGE_EVENTS, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/events/" title="Events">Events</a></li>
+                <% end %>
+                <% if @user.has_permission?(Permission::MANAGE_RESOURCES, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/resources/" title="Resources">Resources</a></li>
+                <% end %>
+                <% if @user.has_permission?(Permission::MANAGE_SPACE_HOURS, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/hours/" title="Hours">Hours</a></li>
+                <% end %>
+                <% if @user.has_permission?(Permission::MANAGE_EMAILS, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/email/" title="Email">Email</a></li>
+                <% end %>
+                <% if @user.has_permission?(Permission::SEE_AGENDA, @space) %>
+                <li><a href="/<%= @space.url_name %>/admin/agenda/" title="Agenda">Agenda</a></li>
+                <% end %>
+            </ul>
+	    </li>
+    <% end %>
+   	<% end %>
 </ul>
-- 
GitLab