From c02556a4e1f0938a52f859e2695eb7f8d6a0d454 Mon Sep 17 00:00:00 2001 From: Ryan Shaw Date: Wed, 18 Apr 2012 15:44:14 -0600 Subject: [PATCH] New/Edit calendar event page with section support closes: #7978 test plan: with cal2 enabled: * click on event in calendar, hit edit, click "more options" * you should be at a new page. * it should have transferred any new description, start/end date you entered * if it's for a course, you should see a checkbox "different due date per section", if it's on your own or a group cal, should not see that checkbox * creating event should save properly so: * verify that all events show up on calendar2 * when you go back to edit any of those events on calendar the info should all load on this page again * when you click "edit" on one of the section events on the calendar it should let you edit the info for all of the sections not just the one you clicked on. with calendar 2 disabled: * make sure you can still create events as you used to be able to Change-Id: I3491a7dbf4103db2f8ae95c9acbdc49597552d2c Reviewed-on: https://gerrit.instructure.com/10141 Reviewed-by: Jon Jensen Tested-by: Hudson --- .../bundles/edit_calendar_event.coffee | 9 +++ .../calendar/CalendarEvent.coffee | 72 +++++++++++++++++ .../calendar/EditEventView.coffee | 77 +++++++++++++++++++ .../calendar/editCalendarEventFull.sass | 29 +++++++ app/views/calendar_events/new.html.erb | 31 ++++++-- .../calendar/editCalendarEventFull.handlebars | 52 +++++++++++++ config/assets.yml | 2 + public/javascripts/wikiSidebar.js | 1 + 8 files changed, 266 insertions(+), 7 deletions(-) create mode 100644 app/coffeescripts/bundles/edit_calendar_event.coffee create mode 100644 app/coffeescripts/calendar/CalendarEvent.coffee create mode 100644 app/coffeescripts/calendar/EditEventView.coffee create mode 100644 app/stylesheets/calendar/editCalendarEventFull.sass create mode 100644 app/views/jst/calendar/editCalendarEventFull.handlebars diff --git a/app/coffeescripts/bundles/edit_calendar_event.coffee b/app/coffeescripts/bundles/edit_calendar_event.coffee new file mode 100644 index 00000000000..a36f7be2cdb --- /dev/null +++ b/app/coffeescripts/bundles/edit_calendar_event.coffee @@ -0,0 +1,9 @@ +require [ + 'jquery' + 'compiled/calendar/CalendarEvent' + 'compiled/calendar/EditEventView' +], ($, CalendarEvent, EditEventView) -> + + $ -> + calendarEvent = new CalendarEvent(ENV.CALENDAR_EVENT) + new EditEventView(model: calendarEvent) diff --git a/app/coffeescripts/calendar/CalendarEvent.coffee b/app/coffeescripts/calendar/CalendarEvent.coffee new file mode 100644 index 00000000000..e0e175e3a51 --- /dev/null +++ b/app/coffeescripts/calendar/CalendarEvent.coffee @@ -0,0 +1,72 @@ +define [ + 'jquery' + 'underscore' + 'Backbone' + 'compiled/str/splitAssetString' +], ($, _, Backbone, splitAssetString) -> + + class CalendarEvent extends Backbone.Model + + urlRoot: '/api/v1/calendar_events/' + + dateAttributes: ['created_at', 'end_at', 'start_at', 'updated_at'] + + _filterAttributes: (obj) -> + filtered = _(obj).pick 'start_at', 'end_at', 'title', + 'description', 'context_code' + if obj.use_section_dates && obj.child_event_data + filtered.child_event_data = _.chain(obj.child_event_data) + .compact() + .filter(@_hasValidInputs) + .map(@_filterAttributes) + .value() + filtered + + _hasValidInputs: (o) -> + # has a date, and either has both a start and end time or neither + o.start_date && (!!o.start_time == !!o.end_time) + + toJSON: (forView) -> + json = super + if forView + json + else + {calendar_event: @_filterAttributes(json)} + + fetch: (options = {}) -> + options = _.clone(options) + model = this + + success = options.success + delete options.success + + error = Backbone.wrapError(options.error, model, options) + delete options.error + + if @get('id') + syncDfd = (this.sync || Backbone.sync).call(this, 'read', this, options) + if @get('sections_url') + sectionsDfd = $.getJSON @get('sections_url') + + combinedSuccess = (syncArgs=[], sectionArgs=[]) -> + [syncResp, syncStatus, syncXhr] = syncArgs + [sectionsResp] = sectionArgs + calEventData = CalendarEvent.mergeSectionsIntoCalendarEvent(syncResp, sectionsResp) + return false unless model.set(model.parse(calEventData, syncXhr), options) + success?(model, calEventData) + + $.when(syncDfd, sectionsDfd) + .fail(error) + .done(combinedSuccess) + + @mergeSectionsIntoCalendarEvent = (eventData = {}, sections) -> + eventData.course_sections = sections + eventData.use_section_dates = !!eventData.child_events?.length + _(eventData.child_events).each (child, index) -> + # 'parse' turns string dates into Date objects + child = eventData.child_events[index] = CalendarEvent::parse(child) + sectionId = splitAssetString(child.context_code)[1] + section = _(sections).find (section) -> section.id == sectionId + section.event = child + eventData + diff --git a/app/coffeescripts/calendar/EditEventView.coffee b/app/coffeescripts/calendar/EditEventView.coffee new file mode 100644 index 00000000000..ccff163f171 --- /dev/null +++ b/app/coffeescripts/calendar/EditEventView.coffee @@ -0,0 +1,77 @@ +define [ + 'jquery' + 'underscore' + 'i18n!calendar.edit' + 'Backbone' + 'jst/calendar/editCalendarEventFull' + 'wikiSidebar' + 'compiled/object/unflatten' + 'tinymce.editor_box' + 'compiled/tinymce' +], ($, _, I18n, Backbone, editCalendarEventFullTemplate, wikiSidebar, unflatten) -> + + ## + # View for editing a calendar event on it's own page + class EditCalendarEventView extends Backbone.View + + el: $('#content') + + template: editCalendarEventFullTemplate + + events: + 'submit form': 'submit' + 'change [name="use_section_dates"]': 'toggleUseSectionDates' + 'click .delete_link': 'destroyModel' + + initialize: -> + @model.fetch().done => + if ENV.NEW_CALENDAR_EVENT_ATTRIBUTES + attrs = @model.parse(ENV.NEW_CALENDAR_EVENT_ATTRIBUTES) + # if start and end are at the beginning of a day, assume it is an all day date + attrs.all_day = !!attrs.start_at?.equals(attrs.end_at) and attrs.start_at.equals(attrs.start_at.clearTime()) + @model.set(attrs) + + @render() + @model.on 'change:use_section_dates', @toggleUsingSectionClass + + render: => + @$el.html @template(@model.toJSON('forView')) + + @$(".date_field").date_field() + @$(".time_field").time_field() + $textarea = @$('textarea').editorBox() + wikiSidebar.init() unless wikiSidebar.inited + wikiSidebar.attachToEditor($textarea).show() + this + + destroyModel: => + msg = I18n.t "confirm_delete_calendar_event", "Are you sure you wan to delete this calendar event?" + if confirm(msg) + @$el.disableWhileLoading @model.destroy success: => + @redirectWithMessage I18n.t('event_deleted', "%{event_title} deleted successfully", event_title: @model.get('title')) + + + # boilerplate that could be replaced with data bindings + toggleUsingSectionClass: => + @$('#editCalendarEventFull').toggleClass 'use_section_dates', @model.get('use_section_dates') + toggleUseSectionDates: => + @model.set 'use_section_dates', !@model.get('use_section_dates') + + redirectWithMessage: (message) -> + $.flashMessage message + window.location = @model.get('return_to_url') if @model.get('return_to_url') + + submit: (event) -> + event?.preventDefault() + eventData = unflatten @$el.getFormData() + # force use_section_dates to boolean, so it doesnt cause 'change' if it is '1' + eventData.use_section_dates = !!eventData.use_section_dates + _.each [eventData].concat(eventData.child_event_data), @setStartEnd + + @$el.disableWhileLoading @model.save eventData, success: -> + @redirectWithMessage I18n.t 'event_saved', 'Event Saved Successfully' + + setStartEnd: (obj) -> + return unless obj + obj.start_at = Date.parse obj.start_date+' '+obj.start_time + obj.end_at = Date.parse obj.start_date+' '+obj.end_time diff --git a/app/stylesheets/calendar/editCalendarEventFull.sass b/app/stylesheets/calendar/editCalendarEventFull.sass new file mode 100644 index 00000000000..f1629346363 --- /dev/null +++ b/app/stylesheets/calendar/editCalendarEventFull.sass @@ -0,0 +1,29 @@ +@import environment.sass + +#editCalendarEventFull + [name="description"] + width: 100% + min-height: 200px + [name="title"] + width: 100% + font-size: 21px + + .date_start_end_row + +clearfix + border + th, td + vertical-align: top + span + float: left + padding: 0px 5px + min-height: 40px + label + font-weight: bold + .show_if_using_sections + display: none + &.use_section_dates + .show_if_using_sections + display: block + .hide_if_using_sections + display: none + diff --git a/app/views/calendar_events/new.html.erb b/app/views/calendar_events/new.html.erb index ae575e5052f..3ea4884cc9c 100644 --- a/app/views/calendar_events/new.html.erb +++ b/app/views/calendar_events/new.html.erb @@ -1,12 +1,28 @@ -<% content_for :page_title do %><%= @event.title || t(:page_title, "New Calendar Event") %><% end %> +<% + content_for :page_title, @event.title || t(:page_title, "New Calendar Event") + return_to_url = clean_return_to(params[:return_to] && params[:return_to].match(/calendar/) && params[:return_to]) || calendar_url_for(@context, :anchor => {:month => (@event.try_rescue(:start_at).try_rescue(:month)), :year => (@event.try_rescue(:start_at).try_rescue(:year))}.to_json) -<% content_for :right_side do %> + if @domain_root_account.enable_scheduler? + event_attrs = { + :id => @event.id, + :context_code => @context.asset_string, + :return_to_url => return_to_url + } + if @context.is_a? Course + event_attrs[:sections_url] = context_url(@context, :api_v1_context_sections_url) + end + js_env :CALENDAR_EVENT => event_attrs + # TODO: we should just do this in the JS + js_env :NEW_CALENDAR_EVENT_ATTRIBUTES => params.slice(:start_at, :end_at, :title, :description) + + js_bundle :edit_calendar_event + jammit_css :tinymce, :edit_calendar_event_full + content_for :right_side, render(:partial => 'shared/wiki_sidebar') + else + content_for :right_side do +%>