diff --git a/cms/static/coffee/spec/views/upload_spec.coffee b/cms/static/coffee/spec/views/upload_spec.coffee index 24bfe7fe7c..b774ece227 100644 --- a/cms/static/coffee/spec/views/upload_spec.coffee +++ b/cms/static/coffee/spec/views/upload_spec.coffee @@ -1,6 +1,6 @@ -define ["js/models/uploads", "js/views/uploads", "js/models/chapter", +define ["sinon", "js/models/uploads", "js/views/uploads", "js/models/chapter", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers", "js/spec_helpers/modal_helpers"], - (FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) -> + (sinon, FileUpload, UploadDialog, Chapter, AjaxHelpers, modal_helpers) -> describe "UploadDialog", -> tpl = readFixtures("upload-dialog.underscore") diff --git a/cms/static/js/spec/utils/drag_and_drop_spec.js b/cms/static/js/spec/utils/drag_and_drop_spec.js index cb95fc2764..194e7766a2 100644 --- a/cms/static/js/spec/utils/drag_and_drop_spec.js +++ b/cms/static/js/spec/utils/drag_and_drop_spec.js @@ -1,6 +1,7 @@ -define(['js/utils/drag_and_drop', 'common/js/components/views/feedback_notification', +define(['sinon', 'js/utils/drag_and_drop', 'common/js/components/views/feedback_notification', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'jquery', 'underscore'], - function(ContentDragger, Notification, AjaxHelpers, $, _) { + function(sinon, ContentDragger, Notification, AjaxHelpers, $, _) { + 'use strict'; describe('Overview drag and drop functionality', function() { beforeEach(function() { setFixtures(readFixtures('mock/mock-outline.underscore')); diff --git a/lms/static/js/spec/instructor_dashboard/certificates_exception_spec.js b/lms/static/js/spec/instructor_dashboard/certificates_exception_spec.js index f80c932785..df8c1a9d22 100644 --- a/lms/static/js/spec/instructor_dashboard/certificates_exception_spec.js +++ b/lms/static/js/spec/instructor_dashboard/certificates_exception_spec.js @@ -1,20 +1,23 @@ -/* global define, sinon */ +/* global define */ define([ 'jquery', + 'sinon', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', 'js/certificates/models/certificate_exception', 'js/certificates/views/certificate_whitelist', 'js/certificates/views/certificate_whitelist_editor', 'js/certificates/collections/certificate_whitelist' ], - function($, AjaxHelpers, CertificateExceptionModel, CertificateWhiteListView, CertificateWhiteListEditorView, + function($, sinon, AjaxHelpers, CertificateExceptionModel, CertificateWhiteListView, CertificateWhiteListEditorView, CertificateWhiteListCollection) { 'use strict'; + describe('edx.certificates.models.certificates_exception.CertificateExceptionModel', function() { - var certificate_exception = null; + var certificateException = null; var assertValid = function(fields, isValid, expectedErrors) { - certificate_exception.set(fields); - var errors = certificate_exception.validate(certificate_exception.attributes); + var errors; + certificateException.set(fields); + errors = certificateException.validate(certificateException.attributes); if (isValid) { expect(errors).toBe(undefined); @@ -29,8 +32,8 @@ define([ }; beforeEach(function() { - certificate_exception = new CertificateExceptionModel({user_name: 'test_user'}, {url: 'test/url/'}); - certificate_exception.set({ + certificateException = new CertificateExceptionModel({user_name: 'test_user'}, {url: 'test/url/'}); + certificateException.set({ notes: 'Test notes' }); }); @@ -50,9 +53,9 @@ define([ }); describe('edx.certificates.collections.certificate_whitelist.CertificateWhiteList', function() { - var certificate_white_list = null, - certificate_exception_url = 'test/url/'; - var certificates_exceptions_json = [ + var certificateWhiteList = null, + certificateExceptionUrl = 'test/url/'; + var certificatesExceptionsJson = [ { id: 1, user_id: 1, @@ -74,38 +77,48 @@ define([ ]; beforeEach(function() { - certificate_white_list = new CertificateWhiteListCollection(certificates_exceptions_json, { + certificateWhiteList = new CertificateWhiteListCollection(certificatesExceptionsJson, { parse: true, canBeEmpty: true, - url: certificate_exception_url, - generate_certificates_url: certificate_exception_url + url: certificateExceptionUrl, + generate_certificates_url: certificateExceptionUrl }); }); it('has 2 models in the collection after initialization', function() { - expect(certificate_white_list.models.length).toEqual(2); + expect(certificateWhiteList.models.length).toEqual(2); }); it("returns correct model on getModel call and 'undefined' if queried model is not present", function() { - expect(certificate_white_list.getModel({user_name: 'test1'})).not.toBe(undefined); - expect(certificate_white_list.getModel({user_name: 'test_invalid_user'})).toBe(undefined); + expect(certificateWhiteList.getModel({user_name: 'test1'})).not.toBe(undefined); + expect(certificateWhiteList.getModel({user_name: 'test_invalid_user'})).toBe(undefined); - expect(certificate_white_list.getModel({user_email: 'test1@test.com'})).not.toBe(undefined); - expect(certificate_white_list.getModel({user_email: 'test_invalid_user@test.com'})).toBe(undefined); + expect(certificateWhiteList.getModel({user_email: 'test1@test.com'})).not.toBe(undefined); + expect(certificateWhiteList.getModel({user_email: 'test_invalid_user@test.com'})).toBe(undefined); - expect(certificate_white_list.getModel({user_name: 'test1'}).attributes).toEqual( + expect(certificateWhiteList.getModel({user_name: 'test1'}).attributes).toEqual( { - id: 1, user_id: 1, user_name: 'test1', user_email: 'test1@test.com', - course_id: 'edX/test/course', created: 'Thursday, October 29, 2015', - notes: 'test notes for test certificate exception', certificate_generated: '' + id: 1, + user_id: 1, + user_name: 'test1', + user_email: 'test1@test.com', + course_id: 'edX/test/course', + created: 'Thursday, October 29, 2015', + notes: 'test notes for test certificate exception', + certificate_generated: '' } ); - expect(certificate_white_list.getModel({user_email: 'test2@test.com'}).attributes).toEqual( + expect(certificateWhiteList.getModel({user_email: 'test2@test.com'}).attributes).toEqual( { - id: 2, user_id: 2, user_name: 'test2', user_email: 'test2@test.com', - course_id: 'edX/test/course', created: 'Thursday, October 29, 2015', - notes: 'test notes for test certificate exception', certificate_generated: '' + id: 2, + user_id: 2, + user_name: 'test2', + user_email: 'test2@test.com', + course_id: 'edX/test/course', + created: 'Thursday, October 29, 2015', + notes: 'test notes for test certificate exception', + certificate_generated: '' } ); }); @@ -114,13 +127,13 @@ define([ var successCallback = sinon.spy(), errorCallback = sinon.spy(), requests = AjaxHelpers.requests(this), - add_students = 'all'; + addStudents = 'all'; var expected = { - url: certificate_exception_url + add_students, + url: certificateExceptionUrl + addStudents, postData: [] }; - certificate_white_list.sync({success: successCallback, error: errorCallback}, add_students); + certificateWhiteList.sync({success: successCallback, error: errorCallback}, addStudents); AjaxHelpers.expectJsonRequest(requests, 'POST', expected.url, expected.postData); }); @@ -128,21 +141,22 @@ define([ var successCallback = sinon.spy(), errorCallback = sinon.spy(), requests = AjaxHelpers.requests(this), - add_students = 'new'; + addStudents = 'new', + expected; - certificate_white_list.add({user_name: 'test3', notes: 'test3 notes', new: true}); - certificate_white_list.sync({success: successCallback, error: errorCallback}, add_students); + certificateWhiteList.add({user_name: 'test3', notes: 'test3 notes', new: true}); + certificateWhiteList.sync({success: successCallback, error: errorCallback}, addStudents); - var expected = { - url: certificate_exception_url + add_students, + expected = { + url: certificateExceptionUrl + addStudents, postData: [ {user_id: '', - user_name: 'test3', - user_email: '', - created: '', - notes: 'test3 notes', - certificate_generated: '', - new: true} + user_name: 'test3', + user_email: '', + created: '', + notes: 'test3 notes', + certificate_generated: '', + new: true} ] }; AjaxHelpers.expectJsonRequest(requests, 'POST', expected.url, expected.postData); @@ -151,9 +165,9 @@ define([ describe('edx.certificates.views.certificate_whitelist.CertificateWhiteListView', function() { var view = null, - certificate_exception_url = 'test/url/'; + certificateExceptionUrl = 'test/url/'; - var certificates_exceptions_json = [ + var certificatesExceptionsJson = [ { id: 1, user_id: 1, @@ -175,17 +189,17 @@ define([ ]; beforeEach(function() { + var fixture; setFixtures(); - var fixture = - readFixtures('templates/instructor/instructor_dashboard_2/certificate-white-list.underscore'); + fixture = readFixtures('templates/instructor/instructor_dashboard_2/certificate-white-list.underscore'); setFixtures("' + "
"); - this.certificate_white_list = new CertificateWhiteListCollection(certificates_exceptions_json, { + this.certificate_white_list = new CertificateWhiteListCollection(certificatesExceptionsJson, { parse: true, canBeEmpty: true, - url: certificate_exception_url, - generate_certificates_url: certificate_exception_url + url: certificateExceptionUrl, + generate_certificates_url: certificateExceptionUrl }); @@ -231,10 +245,10 @@ define([ {user_name: user, notes: notes, user_email: email} ]); - expect(view.$el.find('table tbody tr td:contains("' + user + '")').parent().html()). - toMatch(notes); - expect(view.$el.find('table tbody tr td:contains("' + user + '")').parent().html()). - toMatch(email); + expect(view.$el.find('table tbody tr td:contains("' + user + '")').parent().html()) + .toMatch(notes); + expect(view.$el.find('table tbody tr td:contains("' + user + '")').parent().html()) + .toMatch(email); }); it('verifies collection sync is called when "Generate Exception Certificates" is clicked', function() { @@ -248,8 +262,8 @@ define([ view.$el.find('#generate-exception-certificates').click(); expect(view.collection.sync.called).toBe(true); - expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback})). - toBe(true); + expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback})) + .toBe(true); }); it('verifies sync is called with "new/all" argument depending upon selected radio button', function() { @@ -263,24 +277,24 @@ define([ view.$el.find('#generate-exception-certificates').click(); // By default 'Generate a Certificate for all New additions to the Exception list ' is selected - expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback}), 'new'). - toBe(true); + expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback}), 'new') + .toBe(true); // Select 'Generate a Certificate for all users on the Exception list ' option view.$el.find('input:radio[name=generate-exception-certificates-radio][value=all]').click(); view.$el.find('#generate-exception-certificates').click(); - expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback}), 'all'). - toBe(true); + expect(view.collection.sync.calledWith({success: successCallback, error: errorCallback}), 'all') + .toBe(true); }); }); describe('edx.certificates.views.certificate_whitelist_editor.CertificateWhiteListEditorView', function() { var view = null, - list_view = null, - certificate_exception_url = 'test/url/'; - var certificates_exceptions_json = [ + listView = null, + certificateExceptionUrl = 'test/url/'; + var certificatesExceptionsJson = [ { - url: certificate_exception_url, + url: certificateExceptionUrl, id: 1, user_id: 1, user_name: 'test1', @@ -291,7 +305,7 @@ define([ new: true }, { - url: certificate_exception_url, + url: certificateExceptionUrl, id: 2, user_id: 2, user_name: 'test2', @@ -303,41 +317,42 @@ define([ ]; beforeEach(function() { + var fixture, fixture2, certificateWhiteList; setFixtures(); - var fixture = readFixtures( + fixture = readFixtures( 'templates/instructor/instructor_dashboard_2/certificate-white-list-editor.underscore' ); - var fixture_2 = readFixtures( + fixture2 = readFixtures( 'templates/instructor/instructor_dashboard_2/certificate-white-list.underscore' ); setFixtures( "' + - "' + + "' + "" + "" ); - var certificate_white_list = new CertificateWhiteListCollection(certificates_exceptions_json, { + certificateWhiteList = new CertificateWhiteListCollection(certificatesExceptionsJson, { parse: true, canBeEmpty: true, - url: certificate_exception_url, - generate_certificates_url: certificate_exception_url + url: certificateExceptionUrl, + generate_certificates_url: certificateExceptionUrl }); view = new CertificateWhiteListEditorView({ - collection: certificate_white_list, - url: certificate_exception_url + collection: certificateWhiteList, + url: certificateExceptionUrl }); view.render(); - list_view = new CertificateWhiteListView({ - collection: certificate_white_list, + listView = new CertificateWhiteListView({ + collection: certificateWhiteList, certificateWhiteListEditorView: view }); - list_view.render(); + listView.render(); }); it('verifies view is initialized and rendered successfully', function() { @@ -348,16 +363,16 @@ define([ }); it('verifies success and error messages', function() { - var message_selector = '.message', - success_message = 'test_user has been successfully added to the exception list. Click Generate' + + var messageSelector = '.message', + successMessage = 'test_user has been successfully added to the exception list. Click Generate' + ' Exception Certificate below to send the certificate.', requests = AjaxHelpers.requests(this), - duplicate_user = 'test_user'; + duplicateUser = 'test_user'; - var error_messages = { + var errorMessages = { empty_user_name_email: 'Student username/email field is required and can not be empty. ' + 'Kindly fill in username/email and then press "Add to Exception List" button.', - duplicate_user: '' + (duplicate_user) + ' already in exception list.
' + duplicate_user: '' + (duplicateUser) + ' already in exception list.
' }; // click 'Add Exception' button with empty username/email field @@ -365,10 +380,10 @@ define([ view.$el.find('#add-exception').click(); // Verify error message for missing username/email - expect(view.$el.find(message_selector).html()).toMatch(error_messages.empty_user_name_email); + expect(view.$el.find(messageSelector).html()).toMatch(errorMessages.empty_user_name_email); // Add a new Exception to list - view.$el.find('#certificate-exception').val(duplicate_user); + view.$el.find('#certificate-exception').val(duplicateUser); view.$el.find('#notes').val('test user notes'); view.$el.find('#add-exception').click(); @@ -377,7 +392,7 @@ define([ { id: 3, user_id: 3, - user_name: duplicate_user, + user_name: duplicateUser, user_email: 'test2@test.com', course_id: 'edX/test/course', created: 'Thursday, October 29, 2015', @@ -386,29 +401,29 @@ define([ ); // Verify success message - expect(view.$el.find(message_selector).html()).toMatch(success_message); + expect(view.$el.find(messageSelector).html()).toMatch(successMessage); // Add a duplicate Certificate Exception - view.$el.find('#certificate-exception').val(duplicate_user); + view.$el.find('#certificate-exception').val(duplicateUser); view.$el.find('#notes').val('test user notes'); view.$el.find('#add-exception').click(); // Verify success message - expect(view.$el.find(message_selector).html()).toEqual(error_messages.duplicate_user); + expect(view.$el.find(messageSelector).html()).toEqual(errorMessages.duplicate_user); }); it('verifies certificate exception can be deleted by clicking "delete" ', function() { - var user_name = 'test1', - certificate_exception_selector = "div.white-listed-students table tr:contains('" + user_name + "')", - delete_btn_selector = - certificate_exception_selector + ' td .delete-exception', + var username = 'test1', + certificateExceptionSelector = "div.white-listed-students table tr:contains('" + username + "')", + deleteBtnSelector = + certificateExceptionSelector + ' td .delete-exception', requests = AjaxHelpers.requests(this); - $(delete_btn_selector).click(); + $(deleteBtnSelector).click(); AjaxHelpers.respondWithJson(requests, {}); // Verify the certificate exception is removed from the list - expect($(certificate_exception_selector).length).toBe(0); + expect($(certificateExceptionSelector).length).toBe(0); }); }); } diff --git a/lms/static/sass/features/_course-experience.scss b/lms/static/sass/features/_course-experience.scss index a702fe5858..e1d040717d 100644 --- a/lms/static/sass/features/_course-experience.scss +++ b/lms/static/sass/features/_course-experience.scss @@ -10,6 +10,10 @@ font-weight: $font-bold; color: $black; } + + .dismiss-message { + @include float(right); + } } // Course sidebar diff --git a/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmark_button.js b/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmark_button.js index d64ac41af5..5dece91125 100644 --- a/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmark_button.js +++ b/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmark_button.js @@ -1,5 +1,6 @@ (function(define) { 'use strict'; + define(['gettext', 'jquery', 'underscore', 'backbone', 'js/views/message_banner'], function(gettext, $, _, Backbone, MessageBannerView) { return Backbone.View.extend({ diff --git a/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmarks_list.js b/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmarks_list.js index 229c4bcb51..7f1b3d0bc6 100644 --- a/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmarks_list.js +++ b/openedx/features/course_bookmarks/static/course_bookmarks/js/views/bookmarks_list.js @@ -1,5 +1,6 @@ (function(define) { 'use strict'; + define([ 'gettext', 'jquery', 'underscore', 'backbone', 'logger', 'moment', 'edx-ui-toolkit/js/utils/html-utils', 'common/js/components/views/paging_header', 'common/js/components/views/paging_footer', diff --git a/openedx/features/course_experience/static/course_experience/fixtures/welcome-message-fragment.html b/openedx/features/course_experience/static/course_experience/fixtures/welcome-message-fragment.html new file mode 100644 index 0000000000..7bc36fcb14 --- /dev/null +++ b/openedx/features/course_experience/static/course_experience/fixtures/welcome-message-fragment.html @@ -0,0 +1,6 @@ + diff --git a/openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js b/openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js new file mode 100644 index 0000000000..52c948c3a2 --- /dev/null +++ b/openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js @@ -0,0 +1,20 @@ +/* globals $ */ +import 'jquery.cookie'; + +export class WelcomeMessage { // eslint-disable-line import/prefer-default-export + + constructor(dismissUrl) { + $('.dismiss-message button').click(() => { + $.ajax({ + type: 'POST', + url: dismissUrl, + headers: { + 'X-CSRFToken': $.cookie('csrftoken'), + }, + success: () => { + $('.welcome-message').hide(); + }, + }); + }); + } +} diff --git a/openedx/features/course_experience/static/course_experience/js/spec/WelcomeMessage_spec.js b/openedx/features/course_experience/static/course_experience/js/spec/WelcomeMessage_spec.js new file mode 100644 index 0000000000..6c7129e05e --- /dev/null +++ b/openedx/features/course_experience/static/course_experience/js/spec/WelcomeMessage_spec.js @@ -0,0 +1,33 @@ +/* globals $, loadFixtures */ + +import { + expectRequest, + requests as mockRequests, + respondWithJson, +} from 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'; +import { WelcomeMessage } from '../WelcomeMessage'; + +describe('Welcome Message factory', () => { + describe('Ensure button click', () => { + const endpointUrl = '/course/course_id/dismiss_message/'; + + beforeEach(() => { + loadFixtures('course_experience/fixtures/welcome-message-fragment.html'); + new WelcomeMessage(endpointUrl); // eslint-disable-line no-new + }); + + it('When button click is made, ajax call is made and message is hidden.', () => { + const $message = $('.welcome-message'); + const requests = mockRequests(this); + document.querySelector('.dismiss-message button').dispatchEvent(new Event('click')); + expectRequest( + requests, + 'POST', + endpointUrl, + ); + respondWithJson(requests); + expect($message.attr('style')).toBe('display: none;'); + requests.restore(); + }); + }); +}); diff --git a/openedx/features/course_experience/templates/course_experience/welcome-message-fragment.html b/openedx/features/course_experience/templates/course_experience/welcome-message-fragment.html index 4ab4f08873..1c1ea9d228 100644 --- a/openedx/features/course_experience/templates/course_experience/welcome-message-fragment.html +++ b/openedx/features/course_experience/templates/course_experience/welcome-message-fragment.html @@ -4,11 +4,21 @@ <%namespace name='static' file='../static_content.html'/> <%! +from openedx.core.djangolib.js_utils import js_escaped_string +from django.utils.translation import ugettext as _ from openedx.core.djangolib.markup import HTML %> <%block name="content"> %block> + +<%static:webpack entry="WelcomeMessage"> + new WelcomeMessage("${dismiss_url | n, js_escaped_string}"); +%static:webpack> diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index 591ded0675..27e8f8211d 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -89,7 +89,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase): course_home_url(self.course) # Fetch the view and verify the query counts - with self.assertNumQueries(48): + with self.assertNumQueries(49): with check_mongo_calls(5): url = course_home_url(self.course) self.client.get(url) diff --git a/openedx/features/course_experience/tests/views/test_welcome_message.py b/openedx/features/course_experience/tests/views/test_welcome_message.py index fc93401345..4bbb436700 100644 --- a/openedx/features/course_experience/tests/views/test_welcome_message.py +++ b/openedx/features/course_experience/tests/views/test_welcome_message.py @@ -27,6 +27,18 @@ def welcome_message_url(course): ) +def dismiss_message_url(course): + """ + Returns the URL for the dismiss message endpoint. + """ + return reverse( + 'openedx.course_experience.dismiss_welcome_message', + kwargs={ + 'course_id': unicode(course.id), + } + ) + + class TestWelcomeMessageView(ModuleStoreTestCase): """ Tests for the course welcome message fragment view. @@ -41,10 +53,8 @@ class TestWelcomeMessageView(ModuleStoreTestCase): chapter = ItemFactory.create(category='chapter', parent_location=self.course.location) section = ItemFactory.create(category='sequential', parent_location=chapter.location) ItemFactory.create(category='vertical', parent_location=section.location) - self.user = UserFactory(password=TEST_PASSWORD) CourseEnrollment.enroll(self.user, self.course.id) - self.client.login(username=self.user.username, password=TEST_PASSWORD) def tearDown(self): @@ -58,6 +68,7 @@ class TestWelcomeMessageView(ModuleStoreTestCase): response = self.client.get(welcome_message_url(self.course)) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Second Update') + self.assertContains(response, 'Dismiss') def test_replace_urls(self): img_url = 'img.png' @@ -72,3 +83,15 @@ class TestWelcomeMessageView(ModuleStoreTestCase): def test_empty_welcome_message(self): response = self.client.get(welcome_message_url(self.course)) self.assertEqual(response.status_code, 204) + + def test_dismiss_message(self): + create_course_update(self.course, self.user, 'First Update', date='January 1, 2017') + + response = self.client.get(welcome_message_url(self.course)) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'First Update') + + self.client.post(dismiss_message_url(self.course)) + response = self.client.get(welcome_message_url(self.course)) + self.assertNotIn('First Update', response) + self.assertEqual(response.status_code, 204) diff --git a/openedx/features/course_experience/urls.py b/openedx/features/course_experience/urls.py index 39d5ecf833..e554d53332 100644 --- a/openedx/features/course_experience/urls.py +++ b/openedx/features/course_experience/urls.py @@ -7,8 +7,8 @@ from django.conf.urls import url from views.course_home import CourseHomeFragmentView, CourseHomeView from views.course_outline import CourseOutlineFragmentView from views.course_updates import CourseUpdatesFragmentView, CourseUpdatesView -from views.welcome_message import WelcomeMessageFragmentView from views.course_sock import CourseSockFragmentView +from views.welcome_message import WelcomeMessageFragmentView, dismiss_welcome_message urlpatterns = [ url( @@ -46,4 +46,9 @@ urlpatterns = [ CourseSockFragmentView.as_view(), name='openedx.course_experience.course_sock_fragment_view', ), + url( + r'^dismiss_welcome_message$', + dismiss_welcome_message, + name='openedx.course_experience.dismiss_welcome_message', + ), ] diff --git a/openedx/features/course_experience/views/welcome_message.py b/openedx/features/course_experience/views/welcome_message.py index aa571a04ca..3f8340627d 100644 --- a/openedx/features/course_experience/views/welcome_message.py +++ b/openedx/features/course_experience/views/welcome_message.py @@ -2,13 +2,19 @@ View logic for handling course welcome messages. """ +from django.core.urlresolvers import reverse +from django.http import HttpResponse, HttpResponseBadRequest from django.template.loader import render_to_string +from django.views.decorators.csrf import ensure_csrf_cookie from opaque_keys.edx.keys import CourseKey from web_fragments.fragment import Fragment from course_updates import CourseUpdatesFragmentView from courseware.courses import get_course_info_section_module, get_course_with_access from openedx.core.djangoapps.plugin_api.views import EdxFragmentView +from openedx.core.djangoapps.user_api.course_tag.api import set_course_tag, get_course_tag + +PREFERENCE_KEY = 'view-welcome-message' class WelcomeMessageFragmentView(EdxFragmentView): @@ -27,12 +33,20 @@ class WelcomeMessageFragmentView(EdxFragmentView): if not welcome_message_html: return None + dismiss_url = reverse( + 'openedx.course_experience.dismiss_welcome_message', kwargs={'course_id': unicode(course_key)} + ) + context = { + 'dismiss_url': dismiss_url, 'welcome_message_html': welcome_message_html, } - html = render_to_string('course_experience/welcome-message-fragment.html', context) - return Fragment(html) + if get_course_tag(request.user, course_key, PREFERENCE_KEY) == 'False': + return None + else: + html = render_to_string('course_experience/welcome-message-fragment.html', context) + return Fragment(html) @classmethod def welcome_message_html(cls, request, course): @@ -51,3 +65,13 @@ class WelcomeMessageFragmentView(EdxFragmentView): content = info_block.system.replace_urls(ordered_updates[0]['content']) return content + + +@ensure_csrf_cookie +def dismiss_welcome_message(request, course_id): + """ + Given the course_id in the request, disable displaying the welcome message for the user. + """ + course_key = CourseKey.from_string(course_id) + set_course_tag(request.user, course_key, PREFERENCE_KEY, 'False') + return HttpResponse() diff --git a/package.json b/package.json index 832ce7d89b..241fb4845b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "eslint-config-edx": "^2.0.1", "eslint-config-edx-es5": "^2.0.0", "eslint-import-resolver-webpack": "^0.8.1", - "jasmine-core": "^2.4.1", + "jasmine-core": "^2.6.4", "jasmine-jquery": "^2.1.1", "karma": "^0.13.22", "karma-chrome-launcher": "^0.2.3", @@ -50,7 +50,7 @@ "pa11y": "4.0.1", "pa11y-reporter-json-oldnode": "1.0.0", "plato": "1.2.2", - "sinon": "^1.17.7", + "sinon": "2.3.5", "squirejs": "^0.1.0" } } diff --git a/webpack.config.js b/webpack.config.js index 6719071a23..d4ac165849 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,6 +20,7 @@ var wpconfig = { entry: { CourseOutline: './openedx/features/course_experience/static/course_experience/js/CourseOutline.js', CourseSock: './openedx/features/course_experience/static/course_experience/js/CourseSock.js', + WelcomeMessage: './openedx/features/course_experience/static/course_experience/js/WelcomeMessage.js', Import: './cms/static/js/features/import/factories/import.js' },