diff --git a/app.rb b/app.rb index 147d3c9198f091a5d9d66c64f9bcb377ca7242a7..ff6862662d5066acc5ca618048096af2f9c24ed1 100644 --- a/app.rb +++ b/app.rb @@ -6,6 +6,7 @@ require 'rack-cas' require 'rack-cas/session_store/active_record' require 'models/session' require 'models/user' +require 'models/resource' require 'models/service_space' include ERB::Util @@ -71,13 +72,13 @@ def calculate_time(date_string, hour, minute, am_pm) end helpers do - def load_service_space + def load_service_space(authorize_user = true) url_name = params[:service_space_url_name] space = ServiceSpace.find_by(:url_name => url_name) raise Sinatra::NotFound if space.nil? @space = space - if @user && !@user.in_space?(@space) + if authorize_user && @user && !@user.in_space?(@space) flash :error, 'Unauthorized', 'Sorry, you don\'t have access to this service space.' redirect '/' end @@ -135,12 +136,35 @@ error do end get '/' do - require_login + check_login @breadcrumbs << {:text => 'Home'} # get the service spaces that this user has access to - spaces = @user.service_spaces + spaces = @user ? @user.service_spaces : [] + app_wide_resources = Resource.where(:available_app_wide => true).all + + page = params[:page].to_i + page = 1 if page.nil? || page <= 0 + + reservations = [] + total_pages = 1 + if @user + reservations = Reservation.joins(:resource).includes(:event). + where(:resources => {:available_app_wide => true}). + where(:user_id => @user.id). + where('end_time >= ?', Time.now.midnight). + order(:start_time).limit(5).offset((page-1)*5) + + total_pages = (Reservation.joins(:resource).includes(:event). + where(:resources => {:available_app_wide => true}). + where(:user_id => @user.id). + where('end_time >= ?', Time.now.midnight).count + 4) / 5 + end + erb :home, :layout => :fixed, :locals => { - spaces: spaces + spaces: spaces, + resources: app_wide_resources, + reservations: reservations, + total_pages: total_pages } end diff --git a/db/migrate/20171027090000_add_available_outside_space.rb b/db/migrate/20171027090000_add_available_outside_space.rb new file mode 100644 index 0000000000000000000000000000000000000000..c84dbaaa1904dd13bfaf36d434e9d5ad92bc9c44 --- /dev/null +++ b/db/migrate/20171027090000_add_available_outside_space.rb @@ -0,0 +1,7 @@ +require 'active_record' + +class AddAvailableOutsideSpace < ActiveRecord::Migration + def change + add_column :resources, :available_app_wide, :boolean, :default => false + end +end \ No newline at end of file diff --git a/db/migrate/20171027092700_add_imagemime.rb b/db/migrate/20171027092700_add_imagemime.rb new file mode 100644 index 0000000000000000000000000000000000000000..717cb5fd8f87e4b1ffaf4f339943fbe9495f30e5 --- /dev/null +++ b/db/migrate/20171027092700_add_imagemime.rb @@ -0,0 +1,7 @@ +require 'active_record' + +class AddImagemime < ActiveRecord::Migration + def change + add_column :service_spaces, :imagemime, :string + end +end \ No newline at end of file diff --git a/models/reservation.rb b/models/reservation.rb index cd9af1ab04950ed7fccdee649d17f5ced149c11f..5e312ecbff3879eaa5a7f04fa49b5e2f038f9bfa 100644 --- a/models/reservation.rb +++ b/models/reservation.rb @@ -40,6 +40,18 @@ class Reservation < ActiveRecord::Base calendar.to_ical end + def edit_link + "/#{resource.service_space.url_name}/resources/#{resource.id}/edit_reservation/#{id}/" + end + + def cancel_link + "/#{resource.service_space.url_name}/resources/#{resource.id}/cancel/#{id}/" + end + + def cancel_all_link + "/#{resource.service_space.url_name}/resources/#{resource.id}/cancel_all/#{id}/" + end + def download_link "/#{resource.service_space.url_name}/resources/#{resource.id}/reservation/#{id}/reservation.ics" end diff --git a/models/service_space.rb b/models/service_space.rb index 449a56c7e49fabd6af9295c148f7320ec5d96e6f..d3ac770b6c0afcf559d6f66603df5d13da3f8765 100644 --- a/models/service_space.rb +++ b/models/service_space.rb @@ -42,9 +42,26 @@ class ServiceSpace < ActiveRecord::Base def admin_email_href "/#{self.url_name}/admin/email/" end + + def edit_image_href + "/#{self.url_name}/admin/edit-image/" + end + alias_method :admin_emails_href, :admin_email_href def agenda_href "/#{self.url_name}/admin/agenda/" end + + def remove_image_data + self.imagedata = nil + self.imagemime = nil + self.save + end + + def set_image_data(data) + self.imagemime = data[:type] + self.imagedata = data[:tempfile].read if data[:tempfile].is_a?(Tempfile) + self.save + end end \ No newline at end of file diff --git a/routes/admin.rb b/routes/admin.rb index b5bf8942e2b1d7b762be983849b47acfd57876fb..a1bbc5e5c5b8528ef5a63a22098ce0f614b1ba0a 100644 --- a/routes/admin.rb +++ b/routes/admin.rb @@ -39,4 +39,24 @@ get '/:service_space_url_name/admin/?' do } end +post '/:service_space_url_name/admin/edit-image/?' do + require_login + load_service_space + + unless @user.has_permission?(Permission::SUPER_USER, @space) + flash :alert, "Permission Denied", "Sorry, you don't have permission to change the image of this space." + redirect @space.admin_href + end + + if params.checked?('remove_image') + @space.remove_image_data + else + @space.set_image_data(params[:imagedata]) + end + + # notify that it worked + flash(:success, 'Image Changed', "Your space's image was updated.") + redirect @space.admin_href +end + Dir.glob("#{ROOT}/routes/admin/*.rb") { |file| require file } \ No newline at end of file diff --git a/routes/admin/agenda.rb b/routes/admin/agenda.rb index bcb6569b4e143f0abbc8e25a0b7e5633a09466f5..dc9f337b726ecdd2afea9bc0f3420c5cd9aa9268 100644 --- a/routes/admin/agenda.rb +++ b/routes/admin/agenda.rb @@ -34,4 +34,16 @@ get '/:service_space_url_name/admin/agenda/' do :date => date, :space_hour => correct_hour } +end + +post '/:service_space_url_name/admin/agenda/reservations/:reservation_id/remove/?' do + reservation = Reservation.find_by(:id => params[:reservation_id]) + if reservation.nil? + flash :error, 'Not Found', "Could not find that reservation." + redirect "#{params[:service_space_url_name]}/admin/agenda/" + end + + reservation.delete + flash :success, 'Reservation Removed', "#{reservation.user.full_name}'s reservation for #{reservation.resource.name} has been removed." + redirect "#{params[:service_space_url_name]}/admin/agenda/" end \ No newline at end of file diff --git a/routes/admin/resources.rb b/routes/admin/resources.rb index db95b6c70509faf861eb1547696dc13339da93ec..a651f0a7d0c39b6adac6b0a69785ba993bfb055f 100644 --- a/routes/admin/resources.rb +++ b/routes/admin/resources.rb @@ -32,6 +32,7 @@ post '/:service_space_url_name/admin/resources/create/?' do resource.service_space_id = @space.id resource.needs_authorization = true resource.is_reservable = params.checked?('is_reservable') + resource.available_app_wide = params.checked?('available_app_wide') resource.time_slot_type = params[:time_slot_type] resource.minutes_per_reservation = params[:minutes_per_reservation] resource.min_minutes_per_reservation = params[:min_minutes_per_reservation] @@ -72,6 +73,7 @@ post '/:service_space_url_name/admin/resources/:resource_id/edit/?' do resource.name = params[:name] resource.description = params[:description] resource.is_reservable = params.checked?('is_reservable') + resource.available_app_wide = params.checked?('available_app_wide') resource.time_slot_type = params[:time_slot_type] resource.minutes_per_reservation = params[:minutes_per_reservation] resource.min_minutes_per_reservation = params[:min_minutes_per_reservation] diff --git a/routes/resources.rb b/routes/resources.rb index ef8e656bbdc56ddfaa000a5f0e8dac7499355b04..d7cb34ed34c6532779ba1978fe698e3fe07d3471 100644 --- a/routes/resources.rb +++ b/routes/resources.rb @@ -53,16 +53,20 @@ end # form for reserving a resource get '/:service_space_url_name/resources/:resource_id/reserve/?' do require_login - 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 + url_name = params[:service_space_url_name] + space = ServiceSpace.find_by(:url_name => url_name) + raise Sinatra::NotFound if space.nil? + @space = 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 + redirect back end + load_service_space(!resource.available_app_wide) + @breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Reserve'} + date = params[:date].nil? ? Time.now.midnight.in_time_zone : Time.parse(params[:date]).midnight.in_time_zone week_hours = resource.get_weeks_available_hours(date) @@ -106,14 +110,18 @@ end # submit form for reserving a resource post '/:service_space_url_name/resources/:resource_id/reserve/?' do require_login - load_service_space - + url_name = params[:service_space_url_name] + space = ServiceSpace.find_by(:url_name => url_name) + raise Sinatra::NotFound if space.nil? + @space = 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 + redirect back end + load_service_space(!resource.available_app_wide) + if params[:start_minutes].nil? flash(:alert, 'Please specify a start time', 'Please specify a start time for your reservation.') redirect back @@ -394,20 +402,28 @@ post '/:service_space_url_name/resources/:resource_id/reserve/?' do end 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.url_name}/resources/#{resource.id}/calendar/#{params[:kiosk_mode] ? '?kiosk_mode=true' : ''}" + if @user.in_space?(@space) + redirect "/#{@space.url_name}/resources/#{resource.id}/calendar/#{params[:kiosk_mode] ? '?kiosk_mode=true' : ''}" + else + redirect '/' + end end get '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do require_login - load_service_space - @breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Edit Reservation'} - + url_name = params[:service_space_url_name] + space = ServiceSpace.find_by(:url_name => url_name) + raise Sinatra::NotFound if space.nil? + @space = 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 + redirect back end + load_service_space(!resource.available_app_wide) + @breadcrumbs << {:text => 'Resources', :href => @space.resources_href} << {:text => 'Edit Reservation'} + # check that this reservation exists reservation = Reservation.find(params[:reservation_id]) if reservation.nil? @@ -460,14 +476,18 @@ end post '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservation_id/?' do require_login - load_service_space - + url_name = params[:service_space_url_name] + space = ServiceSpace.find_by(:url_name => url_name) + raise Sinatra::NotFound if space.nil? + @space = 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 + redirect back end + load_service_space(!resource.available_app_wide) + # check that this reservation exists reservation = Reservation.find(params[:reservation_id]) if reservation.nil? @@ -545,12 +565,16 @@ post '/:service_space_url_name/resources/:resource_id/edit_reservation/:reservat ) 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 "/#{@space.url_name}/resources/#{resource.id}/calendar/" + if @user.in_space?(@space) + redirect "/#{@space.url_name}/resources/#{resource.id}/calendar/" + else + redirect '/' + end end post '/:service_space_url_name/resources/:resource_id/cancel/:reservation_id/?' do require_login - load_service_space + load_service_space(false) # check that the user requesting cancel is the same as the one on the reservation reservation = Reservation.find(params[:reservation_id]) @@ -572,7 +596,7 @@ end post '/:service_space_url_name/resources/:resource_id/cancel_all/:recurring_reference_id/?' do require_login - load_service_space + load_service_space(false) # check that the user requesting cancel is the same as the one on the reservation reservations = Reservation.where(:recurring_reference_id => params[:recurring_reference_id]).all @@ -597,7 +621,7 @@ end get '/:service_space_url_name/resources/:resource_id/reservation/:reservation_id/reservation.ics' do require_login - load_service_space + load_service_space(false) resource = Resource.find_by(:service_space_id => @space.id, :id => params[:resource_id]) if resource.nil? diff --git a/views/admin/agenda.erb b/views/admin/agenda.erb index 8fbe4c596f6bc7c3681c1473dd841331670cf085..f2baa923b35ba01c24d36beab3129ac1ebf3f3e8 100644 --- a/views/admin/agenda.erb +++ b/views/admin/agenda.erb @@ -38,6 +38,7 @@ No reservations today. Hopefully someone will still come in to use the space!<br <th>Name</th> <th>Resource</th> <th>Time</th> + <th>Actions</th> </tr> </thead> <tbody> @@ -60,6 +61,11 @@ No reservations today. Hopefully someone will still come in to use the space!<br <%= reservation.start_time.in_time_zone.strftime('%l:%M %P') %><br> <%= reservation.length %> minutes </td> + <td> + <form class="delete-form" method="POST" action="/<%= @space.url_name %>/admin/agenda/reservations/<%= reservation.id %>/remove/"> + <button class="wdn-button wdn-button-brand" type="submit">Remove</button> + </form> + </td> </tr> <% end %> </tbody> diff --git a/views/admin/edit_resource.erb b/views/admin/edit_resource.erb index 06d5c3196d70fda22479493d3748e5dd10d804a1..bddd2d3811c03b7ddc519228206d34eddb2f96ba 100644 --- a/views/admin/edit_resource.erb +++ b/views/admin/edit_resource.erb @@ -21,7 +21,12 @@ <label for="time-slot-type-range">Range:</label> <label for="min-minutes-per-reservation">Min:</label> <input style="width: 100px" type="number" name="min_minutes_per_reservation" id="min-minutes-per-reservation" value="<%= resource.min_minutes_per_reservation %>"> <label for="max-minutes-per-reservation">Max:</label> <input style="width: 100px" type="number" name="max_minutes_per_reservation" id="max-minutes-per-reservation" value="<%= resource.max_minutes_per_reservation %>"> - <label for="increment-minutes-per-reservation">Increment:</label> <input style="width: 100px" type="number" name="increment_minutes_per_reservation" id="increment-minutes-per-reservation" value="<%= resource.increment_minutes_per_reservation %>"> + <label for="increment-minutes-per-reservation">Increment:</label> <input style="width: 100px" type="number" name="increment_minutes_per_reservation" id="increment-minutes-per-reservation" value="<%= resource.increment_minutes_per_reservation %>"><br> + </div> + <div> + <input type="checkbox" <%= 'checked="checked"' if resource.available_app_wide %> name='available_app_wide' id="available-app-wide"> + <label for="available-app-wide">Open to all Users? (Checking this will allow ANYONE who signs in to UNL Resource Scheduler + to reserve the resource)</label> </div> <br> diff --git a/views/admin/home.erb b/views/admin/home.erb index 46ba1ee72f9eb0ccbf47f36b18d1a0510fdc35b6..9cc8261cad5df806ca5bf6edca95b6d12492054d 100644 --- a/views/admin/home.erb +++ b/views/admin/home.erb @@ -30,15 +30,15 @@ <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 %> + <% 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="<%= @space.admin_hours_href %>">Manage</a></td> </tr> @@ -60,3 +60,19 @@ </tbody> </table> +<% if @user.has_permission?(Permission::SUPER_USER, @space) %> +<h4>Edit Space Image</h4> +<form id="edit-space-image" action="<%= @space.edit_image_href %>" method="POST" enctype="multipart/form-data"> +<% unless @space.imagedata.nil? %> + <img src="<%= @space.image_src %>" alt="Image for Service Space <%= @space.name %>"> + <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="Service Space Image"> +<br><br> +<button type="submit" class="wdn-button wdn-button-brand">Submit</button> +</form> + +<% end %> + diff --git a/views/home.erb b/views/home.erb index 7dcfd471b27c959d83cdbf939d31d73a21f66f98..d61d7425a4417596aedaff3de6f1bc0f53af6b6a 100644 --- a/views/home.erb +++ b/views/home.erb @@ -1,25 +1,139 @@ <div id="pagetitle"> - <h3>Welcome to UNL Resource Scheduler<span class="wdn-subhead"><%= @user.full_name %></span></h3> + <h3>Welcome to UNL Resource Scheduler<span class="wdn-subhead"><%= @user.full_name if @user %></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 wdn-center"> - <a href="<%= space.href %>"> - <div class="space-image"> - <% if space.imagedata.nil? %> - <img src="/images/wordmark02.svg" alt="Image for Space: <%= space.name %>"> - <% else %> - <img src="<%= space.image_src %>" alt="Image for Space: <%= space.name %>"> - <% end %> - </div> - <%= space.name %> - </a> +<p> +UNL Resource Scheduler is the quick and easy way to reserve time for various machines, rooms, and tools around campus. +If you'd like your organization, building, or department in our scheduler, please <a href="mailto:lemburg@unl.edu">email</a> +to set up a service space. Enjoy! +</p> + +<% unless spaces.empty? %> + <h5 class="wdn-center wdn-brand">Your Service Spaces</h5> + <div class="wdn-grid-set-fourths"> + <% spaces.each do |space| %> + <div class="wdn-col wdn-center"> + <a href="<%= space.href %>"> + <div class="space-image"> + <% if space.imagedata.nil? %> + <img src="/images/wordmark02.svg" alt="Image for Space: <%= space.name %>"> + <% else %> + <img src="<%= space.image_src %>" alt="Image for Space: <%= space.name %>"> + <% end %> + </div> + <%= space.name %> + </a> + </div> + <% end %> </div> <% end %> + +<% unless resources.empty? %> + <h5 class="wdn-brand">Available Resources</h5> + <table> + <thead> + <tr> + <th>Resource</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + <% resources.each do |resource| %> + <tr> + <td> + <%= resource.name %> + <br><small><%= resource.service_space.name %></small> + </td> + <td class="table-actions"> + <% if resource.is_reservable %> + <a href="/<%= resource.service_space.url_name %>/resources/<%= resource.id %>/reserve/" class="wdn-button wdn-button-brand">Reserve Time</a> + <a href="/<%= resource.service_space.url_name %>/resources/<%= resource.id %>/calendar/" class="wdn-button wdn-button-triad">See Calendar</a> + <% else %> + Reservation not required + <% end %> + </td> + </tr> + <% end %> + </tbody> +</table> +<% end %> + + +<% if !reservations.empty? %> +<h4> +My Reservations (for resources available UNL-wide) +</h4> +<table> + <thead> + <tr> + <th>Resource</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 %> + <% unless reservation.title.nil? || reservation.title.empty? %> + <br><small><%= reservation.title %></small> + <% end %> + </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="<%= reservation.download_link %>" class="wdn-button wdn-button-triad">Download</a> + <a href="<%= reservation.edit_link %>" class="wdn-button wdn-button-brand">Edit</a> + <form method="POST" action="<%= reservation.cancel_link %>" class="delete-form"> + <button class="wdn-button" type="submit">Remove</button> + </form> + <% unless reservation.recurring_reference_id.nil? %> + <form method="POST" action="<%= reservation.cancel_all_link %>" class="delete-form delete-recurring-reservation"> + <button class="wdn-button" type="submit">Remove All</button> + </form> + <% end %> + </td> + </tr> + <% end %> + </tbody> +</table> +<% if total_pages > 1 %> +<script> + WDN.loadCSS(WDN.getTemplateFilePath('css/modules/pagination.css')); +</script> +<div style="text-align: center;"> + <div style="display: inline-block;"> + <ul class="wdn_pagination" data-tab="pending" style="padding-left: 0;"> + <% if page != 1 %> + <li class="arrow prev"><a href="?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="?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="?page=<%= page+1 %>" title="Go to the next page">next →</a></li> + <% end %> + </ul> + </div> </div> +<% end %> + <% end %> \ No newline at end of file