diff --git a/app/assets/javascripts/confirm_discard_changes.js b/app/assets/javascripts/confirm_discard_changes.js new file mode 100644 index 000000000..3f11f4be5 --- /dev/null +++ b/app/assets/javascripts/confirm_discard_changes.js @@ -0,0 +1,29 @@ +;(function() { + // Check for changes before navigating away if there are unsubmitted changes. + // TODO: This script won't recognize text that has been changed twice + // resulting in no net effect to the input. + + var changes = false; + + $(document).ready(function() { + $('input').on('change', function(e) { + changes = true; + }); + $('textarea').on('change', function(e) { + changes = true; + }); + + window.onbeforeunload = function() { + if (changes) { + return 'It looks like you might have unsubmitted changes. Are you sure you want to continue?'; + } + else { + return null; + } + } + + $('form').on('submit', function() { + changes = false; + }); + }); +})(); diff --git a/app/assets/javascripts/initialize_datetimepickers.js b/app/assets/javascripts/initialize_datetimepickers.js index e4d8c57dc..d6a5e03ce 100644 --- a/app/assets/javascripts/initialize_datetimepickers.js +++ b/app/assets/javascripts/initialize_datetimepickers.js @@ -7,27 +7,30 @@ var lessThans = $(this).data('dateLessThan'); var greaterThans = $(this).data('dateGreaterThan'); + function compareDates(dp_id, cmp) { + if (dp_id) { + var theirID = '#' + dp_id; + var theirDateData = $(theirID).data('DateTimePicker'); + + // Check whether the other target has been initialized + if (theirDateData) { + var theirDate = theirDateData.date(); + $(theirID).data('DateTimePicker').date(cmp(theirDate, ourDate)); + } + } + } + // We are less than (older than) each of these elements if (lessThans) { lessThans.split(' ').forEach(function(dp_id, idx, arr) { - if (dp_id) { - var theirID = '#' + dp_id; - var theirDate = $(theirID).data('DateTimePicker').date(); - - $(theirID).data('DateTimePicker').date(moment.max(theirDate, ourDate)); - } + compareDates(dp_id, moment.max); }); } // We are greater than (younger than) each of these elements if (greaterThans) { greaterThans.split(' ').forEach(function(dp_id, idx, arr) { - if (dp_id) { - var theirID = '#' + dp_id; - var theirDate = $(theirID).data('DateTimePicker').date(); - - $(theirID).data('DateTimePicker').date(moment.min(theirDate, ourDate)); - } + compareDates(dp_id, moment.min); }); } } diff --git a/app/controllers/announcements_controller.rb b/app/controllers/announcements_controller.rb index f7425886a..6d74ddb57 100755 --- a/app/controllers/announcements_controller.rb +++ b/app/controllers/announcements_controller.rb @@ -1,3 +1,5 @@ +# Extend the Rails ApplicationController to define some RESTful endpoints for +# dealing with Announcements. class AnnouncementsController < ApplicationController action_auth_level :index, :instructor def index @@ -10,16 +12,19 @@ def index action_auth_level :new, :instructor def new - # for consistency with REST + @announcement = Announcement.new end action_auth_level :create, :instructor def create if !@cud.user.administrator? && params[:announcement][:system] - flash[:error] = "You don't have the permission to create system announcements!" + flash[:error] = "You don't have the permission " \ + "to create system announcements!" redirect_to(action: "index") && return end + @announcement = @course.announcements.create(announcement_params) + if @announcement.save flash[:success] = "Create success!" redirect_to(course_announcements_path(@course)) && return @@ -37,11 +42,13 @@ def show action_auth_level :edit, :instructor def edit @announcement = Announcement.find(params[:id]) + # Prevent non-admin from entering system announcements edit page - if !@cud.user.administrator? && @announcement.system - flash[:error] = "You don't have the permission to edit system announcements!" - redirect_to(action: "index") && return - end + return unless @cud.user.administrator? || @announcement.system + + flash[:error] = "You don't have the permission " \ + "to edit system announcements!" + redirect_to(action: "index") && return end action_auth_level :update, :instructor @@ -60,7 +67,8 @@ def update def destroy @announcement = Announcement.find(params[:id]) if !@cud.user.administrator? && @announcement.system - flash[:error] = "You don't have the permission to destroy system announcements!" + flash[:error] = "You don't have the permission " \ + "to destroy system announcements!" redirect_to(action: "index") && return end @announcement.destroy @@ -70,6 +78,7 @@ def destroy private def announcement_params - params.require(:announcement).permit(:title, :description, :start_date, :end_date, :system, :persistent) + params.require(:announcement).permit(:title, :description, :start_date, + :end_date, :system, :persistent) end end diff --git a/app/form_builders/form_builder_with_date_time_input.rb b/app/form_builders/form_builder_with_date_time_input.rb index 6cb714651..e7c043c14 100755 --- a/app/form_builders/form_builder_with_date_time_input.rb +++ b/app/form_builders/form_builder_with_date_time_input.rb @@ -1,3 +1,8 @@ +# Extend the Rails FormBuilder class. +# +# The naming is unfortunate, as this FormBuilder does more than just add a +# custom datetimepicker. In reality, it's goal is to wrap common form builder +# methods in Bootstrap boilerplate code. class FormBuilderWithDateTimeInput < ActionView::Helpers::FormBuilder %w(text_field text_area).each do |method_name| # retain access to default textfield, etc. helpers @@ -21,7 +26,8 @@ def score_adjustment_input(name, *args) @template.content_tag :div, class: "score-adjustment input-group" do (f.vanilla_text_field :value, class: "form-control value") + (@template.content_tag :div, class: "input-group-addon" do - f.select(:kind, { "points" => "points", "%" => "percent" }, {}, class: "form-control kind input-group-addon") + f.select(:kind, { "points" => "points", "%" => "percent" }, {}, + class: "form-control kind input-group-addon") end) end end @@ -68,21 +74,33 @@ def datetime_select(name, options = {}, _html_options = {}) # :greater_than properties to initialize relationships between datepicker # fields. def date_helper(name, options, strftime, date_format) - existing_time = @object.send(name) - formatted_datetime = existing_time.to_time.strftime(strftime) if existing_time.present? + begin + existing_time = @object.send(name) + rescue + existing_time = nil + end + + if existing_time.present? + formatted_datetime = existing_time.to_time.strftime(strftime) + else + formatted_datetime = "" + end - field = vanilla_text_field(name, :value => formatted_datetime, - :class => "form-control datetimepicker", - :"data-date-format" => date_format, - :"data-date-less-than" => options[:less_than], - :"data-date-greater-than" => options[:greater_than]) + field = vanilla_text_field( + name, + :value => formatted_datetime, + :class => "form-control datetimepicker", + :"data-date-format" => date_format, + :"data-date-less-than" => options[:less_than], + :"data-date-greater-than" => options[:greater_than]) wrap_field name, field, options[:help_text] end def wrap_field(name, field, help_text, display_name = nil) @template.content_tag :div, class: "form-group" do - label(name, display_name, class: "control-label") + field + help_text(name, help_text) + label(name, display_name, class: "control-label") + + field + help_text(name, help_text) end end diff --git a/app/views/announcements/_announcementFields.html.erb b/app/views/announcements/_announcementFields.html.erb index 32ecee54a..1d922c56d 100755 --- a/app/views/announcements/_announcementFields.html.erb +++ b/app/views/announcements/_announcementFields.html.erb @@ -1,31 +1,29 @@ -
Title: | -<%= f.text_field :title %> | -
---|---|
Description: | -<%= f.text_area :description %> | -
Start Date: | -<%= f.datetime_select :start_date, step: 1 %> | -
End Date: | -<%= f.datetime_select :end_date, step: 1 %> | -
Persistent Announcement? | -<%= f.check_box (:persistent) %> | -
System Announcement? | -<%= f.check_box (:system) %> | -
<%= f.submit 'Submit' , {:class=>"btn primary"} %> | -