diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index 3878340af3..afb38c3f9e 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -9,6 +9,9 @@ from xmodule.modulestore.django import _MODULESTORES, modulestore from xmodule.templates import update_templates from auth.authz import get_user_by_email +from selenium.webdriver.common.keys import Keys +import time + from logging import getLogger logger = getLogger(__name__) @@ -140,3 +143,14 @@ def add_subsection(name='Subsection One'): save_css = 'input.new-subsection-name-save' world.css_fill(name_css, name) world.css_click(save_css) + + +def set_date_and_time(date_css, desired_date, time_css, desired_time): + world.css_fill(date_css, desired_date) + # hit TAB to get to the time field + e = world.css_find(date_css).first + e._element.send_keys(Keys.TAB) + world.css_fill(time_css, desired_time) + e = world.css_find(time_css).first + e._element.send_keys(Keys.TAB) + time.sleep(float(1)) diff --git a/cms/djangoapps/contentstore/features/section.py b/cms/djangoapps/contentstore/features/section.py index 0c0f5536a0..fca14e21f0 100644 --- a/cms/djangoapps/contentstore/features/section.py +++ b/cms/djangoapps/contentstore/features/section.py @@ -4,8 +4,6 @@ from lettuce import world, step from common import * from nose.tools import assert_equal -from selenium.webdriver.common.keys import Keys -import time ############### ACTIONS #################### @@ -39,16 +37,8 @@ def i_click_the_edit_link_for_the_release_date(step): @step('I save a new section release date$') def i_save_a_new_section_release_date(step): - date_css = 'input.start-date.date.hasDatepicker' - time_css = 'input.start-time.time.ui-timepicker-input' - world.css_fill(date_css, '12/25/2013') - # hit TAB to get to the time field - e = world.css_find(date_css).first - e._element.send_keys(Keys.TAB) - world.css_fill(time_css, '12:00am') - e = world.css_find(time_css).first - e._element.send_keys(Keys.TAB) - time.sleep(float(1)) + set_date_and_time('input.start-date.date.hasDatepicker', '12/25/2013', + 'input.start-time.time.ui-timepicker-input', '12:00am') world.browser.click_link_by_text('Save') diff --git a/cms/djangoapps/contentstore/features/subsection.feature b/cms/djangoapps/contentstore/features/subsection.feature index e913c6a4bf..cc3b2b1cbb 100644 --- a/cms/djangoapps/contentstore/features/subsection.feature +++ b/cms/djangoapps/contentstore/features/subsection.feature @@ -25,6 +25,13 @@ Feature: Create Subsection And I reload the page Then I see it marked as Homework + Scenario: Set a due date in a different year (bug #256) + Given I have opened a new subsection in Studio + And I have set a release date and due date in different years + Then I see the correct dates + And I reload the page + Then I see the correct dates + @skip-phantom Scenario: Delete a subsection Given I have opened a new course section in Studio @@ -33,3 +40,5 @@ Feature: Create Subsection When I press the "subsection" delete icon And I confirm the alert Then the subsection does not exist + + diff --git a/cms/djangoapps/contentstore/features/subsection.py b/cms/djangoapps/contentstore/features/subsection.py index 4ab27fcb49..1ec43e6971 100644 --- a/cms/djangoapps/contentstore/features/subsection.py +++ b/cms/djangoapps/contentstore/features/subsection.py @@ -16,6 +16,18 @@ def i_have_opened_a_new_course_section(step): add_section() +@step('I have added a new subsection$') +def i_have_added_a_new_subsection(step): + add_subsection() + + +@step('I have opened a new subsection in Studio$') +def i_have_opened_a_new_subsection(step): + step.given('I have opened a new course section in Studio') + step.given('I have added a new subsection') + world.css_click('span.subsection-name-value') + + @step('I click the New Subsection link') def i_click_the_new_subsection_link(step): world.css_click('a.new-subsection-item') @@ -43,9 +55,20 @@ def i_see_complete_subsection_name_with_quote_in_editor(step): assert_equal(world.css_find(css).value, 'Subsection With "Quote"') -@step('I have added a new subsection$') -def i_have_added_a_new_subsection(step): - add_subsection() +@step('I have set a release date and due date in different years$') +def test_have_set_dates_in_different_years(step): + set_date_and_time('input#start_date', '12/25/2011', 'input#start_time', '3:00am') + world.css_click('.set-date') + # Use a year in the past so that current year will always be different. + set_date_and_time('input#due_date', '01/02/2012', 'input#due_time', '4:00am') + + +@step('I see the correct dates$') +def i_see_the_correct_dates(step): + assert_equal('12/25/2011', world.css_find('input#start_date').first.value) + assert_equal('3:00am', world.css_find('input#start_time').first.value) + assert_equal('01/02/2012', world.css_find('input#due_date').first.value) + assert_equal('4:00am', world.css_find('input#due_time').first.value) @step('I mark it as Homework$') diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index d38918d6b0..83a2bde72d 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -151,10 +151,6 @@ def compute_unit_state(unit): return UnitState.public -def get_date_display(date): - return date.strftime("%d %B, %Y at %I:%M %p") - - def update_item(location, value): """ If value is None, delete the db entry. Otherwise, update it using the correct modulestore. diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 9681f54350..8850f230eb 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -6,7 +6,6 @@ import sys import time import tarfile import shutil -from datetime import datetime from collections import defaultdict from uuid import uuid4 from path import path @@ -47,12 +46,13 @@ from functools import partial from xmodule.contentstore.django import contentstore from xmodule.contentstore.content import StaticContent +from xmodule.util.date_utils import get_default_time_display from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state, \ - get_date_display, UnitState, get_course_for_item, get_url_reverse, add_open_ended_panel_tab, \ + UnitState, get_course_for_item, get_url_reverse, add_open_ended_panel_tab, \ remove_open_ended_panel_tab from xmodule.modulestore.xml_importer import import_from_xml @@ -365,7 +365,7 @@ def edit_unit(request, location): 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, - 'release_date': get_date_display(datetime.fromtimestamp(time.mktime(containing_subsection.lms.start))) if containing_subsection.lms.start is not None else None, + 'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, 'section': containing_section, 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'), 'unit_state': unit_state, @@ -828,7 +828,7 @@ def upload_asset(request, org, course, coursename): readback = contentstore().find(content.location) response_payload = {'displayname': content.name, - 'uploadDate': get_date_display(readback.last_modified_at), + 'uploadDate': get_default_time_display(readback.last_modified_at.timetuple()), 'url': StaticContent.get_url_path_from_location(content.location), 'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None, 'msg': 'Upload completed' @@ -1433,7 +1433,7 @@ def asset_index(request, org, course, name): id = asset['_id'] display_info = {} display_info['displayname'] = asset['displayname'] - display_info['uploadDate'] = get_date_display(asset['uploadDate']) + display_info['uploadDate'] = get_default_time_display(asset['uploadDate'].timetuple()) asset_location = StaticContent.compute_location(id['org'], id['course'], id['name']) display_info['url'] = StaticContent.get_url_path_from_location(asset_location) diff --git a/cms/static/js/base.js b/cms/static/js/base.js index 657eb5c8d4..6ea918cc36 100644 --- a/cms/static/js/base.js +++ b/cms/static/js/base.js @@ -4,6 +4,9 @@ var $modalCover; var $newComponentItem; var $changedInput; var $spinner; +var $newComponentTypePicker; +var $newComponentTemplatePickers; +var $newComponentButton; $(document).ready(function () { $body = $('body'); @@ -242,7 +245,7 @@ function syncReleaseDate(e) { $("#start_time").val(""); } -function getEdxTimeFromDateTimeVals(date_val, time_val, format) { +function getEdxTimeFromDateTimeVals(date_val, time_val) { var edxTimeStr = null; if (date_val != '') { @@ -251,20 +254,17 @@ function getEdxTimeFromDateTimeVals(date_val, time_val, format) { // Note, we are using date.js utility which has better parsing abilities than the built in JS date parsing var date = Date.parse(date_val + " " + time_val); - if (format == null) - format = 'yyyy-MM-ddTHH:mm'; - - edxTimeStr = date.toString(format); + edxTimeStr = date.toString('yyyy-MM-ddTHH:mm'); } return edxTimeStr; } -function getEdxTimeFromDateTimeInputs(date_id, time_id, format) { +function getEdxTimeFromDateTimeInputs(date_id, time_id) { var input_date = $('#' + date_id).val(); var input_time = $('#' + time_id).val(); - return getEdxTimeFromDateTimeVals(input_date, input_time, format); + return getEdxTimeFromDateTimeVals(input_date, input_time); } function autosaveInput(e) { @@ -305,10 +305,8 @@ function saveSubsection() { } // Piece back together the date/time UI elements into one date/time string - // NOTE: our various "date/time" metadata elements don't always utilize the same formatting string - // so make sure we're passing back the correct format metadata['start'] = getEdxTimeFromDateTimeInputs('start_date', 'start_time'); - metadata['due'] = getEdxTimeFromDateTimeInputs('due_date', 'due_time', 'MMMM dd HH:mm'); + metadata['due'] = getEdxTimeFromDateTimeInputs('due_date', 'due_time'); $.ajax({ url: "/save_item", @@ -330,8 +328,8 @@ function saveSubsection() { function createNewUnit(e) { e.preventDefault(); - parent = $(this).data('parent'); - template = $(this).data('template'); + var parent = $(this).data('parent'); + var template = $(this).data('template'); $.post('/clone_item', {'parent_location': parent, diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss index 0d72e2d2bf..e5a294467e 100644 --- a/cms/static/sass/views/_outline.scss +++ b/cms/static/sass/views/_outline.scss @@ -271,7 +271,7 @@ body.course.outline { .section-published-date { float: right; - width: 265px; + width: 278px; margin-right: 220px; @include border-radius(3px); background: $lightGrey; diff --git a/cms/templates/edit_subsection.html b/cms/templates/edit_subsection.html index eb5a9a9824..80385de829 100644 --- a/cms/templates/edit_subsection.html +++ b/cms/templates/edit_subsection.html @@ -1,9 +1,7 @@ <%inherit file="base.html" /> <%! - from time import mktime - import dateutil.parser import logging - from datetime import datetime + from xmodule.util.date_utils import get_time_struct_display %> <%! from django.core.urlresolvers import reverse %> @@ -13,7 +11,6 @@ <%namespace name="units" file="widgets/units.html" /> <%namespace name='static' file='static_content.html'/> -<%namespace name='datetime' module='datetime'/> <%block name="content">
@@ -38,18 +35,15 @@
- <% - start_date = datetime.fromtimestamp(mktime(subsection.lms.start)) if subsection.lms.start is not None else None - parent_start_date = datetime.fromtimestamp(mktime(parent_item.lms.start)) if parent_item.lms.start is not None else None - %> - - + +
% if subsection.lms.start != parent_item.lms.start and subsection.lms.start: - % if parent_start_date is None: + % if parent_item.lms.start is None:

The date above differs from the release date of ${parent_item.display_name_with_default}, which is unset. % else: -

The date above differs from the release date of ${parent_item.display_name_with_default} – ${parent_start_date.strftime('%m/%d/%Y')} at ${parent_start_date.strftime('%H:%M')}. +

The date above differs from the release date of ${parent_item.display_name_with_default} – + ${get_time_struct_display(parent_item.lms.start, '%m/%d/%Y at %I:%M %p')}. % endif Sync to ${parent_item.display_name_with_default}.

% endif @@ -66,12 +60,8 @@ Set a due date

- <% - # due date uses it own formatting for stringifying the date. As with capa_module.py, there's a utility module available for us to use - due_date = dateutil.parser.parse(subsection.lms.due) if subsection.lms.due else None - %> - - + + Remove due date

diff --git a/cms/templates/overview.html b/cms/templates/overview.html index d45a90093e..04aae12f4a 100644 --- a/cms/templates/overview.html +++ b/cms/templates/overview.html @@ -1,9 +1,7 @@ <%inherit file="base.html" /> <%! - from time import mktime - import dateutil.parser import logging - from datetime import datetime + from xmodule.util.date_utils import get_time_struct_display %> <%! from django.core.urlresolvers import reverse %> <%block name="title">Course Outline @@ -163,11 +161,10 @@