From 3573e23c4e6eef538fb6b09c6f55c8d7c9fea33a Mon Sep 17 00:00:00 2001 From: Ali-D-Akbar Date: Wed, 3 Jun 2020 14:06:08 +0500 Subject: [PATCH 01/16] PROD-1281 --- .../js/certificates/views/signatory_details.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/cms/static/js/certificates/views/signatory_details.js b/cms/static/js/certificates/views/signatory_details.js index 9d6543e279..9286b28259 100644 --- a/cms/static/js/certificates/views/signatory_details.js +++ b/cms/static/js/certificates/views/signatory_details.js @@ -11,10 +11,11 @@ define([ 'js/views/baseview', 'js/certificates/views/signatory_editor', 'text!templates/signatory-details.underscore', - 'text!templates/signatory-actions.underscore' + 'text!templates/signatory-actions.underscore', + 'edx-ui-toolkit/js/utils/html-utils' ], function($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, SignatoryEditorView, - signatoryDetailsTemplate, signatoryActionsTemplate) { + signatoryDetailsTemplate, signatoryActionsTemplate, HtmlUtils) { 'use strict'; var SignatoryDetailsView = BaseView.extend({ tagName: 'div', @@ -52,20 +53,20 @@ function($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, Signa editSignatory: function(event) { // Retrieve the edit view for this model if (event && event.preventDefault) { event.preventDefault(); } - this.$el.html(this.edit_view.render()); - $(_.template(signatoryActionsTemplate)()).appendTo(this.el); + this.$el.html(HtmlUtils.HTML(this.edit_view.render()).toString()); + this.$el.append(HtmlUtils.template(signatoryActionsTemplate)().toString()); this.edit_view.delegateEvents(); this.delegateEvents(); }, saveSignatoryData: function(event) { // Persist the data for this model - if (event && event.preventDefault) { event.preventDefault(); } var certificate = this.model.get('certificate'); + var self = this; + if (event && event.preventDefault) { event.preventDefault(); } if (!certificate.isValid()) { return; } - var self = this; ViewUtils.runOperationShowingMessage( gettext('Saving'), function() { @@ -94,7 +95,7 @@ function($, _, str, Backbone, gettext, TemplateUtils, ViewUtils, BaseView, Signa var attributes = $.extend({}, this.model.attributes, { signatory_number: this.model.collection.indexOf(this.model) + 1 }); - return $(this.el).html(_.template(signatoryDetailsTemplate)(attributes)); + return HtmlUtils.setHtml(this.$el, HtmlUtils.template(signatoryDetailsTemplate)(attributes)); } }); return SignatoryDetailsView; From 7470a9d4475a80fe5da4a26d10bec9255427944f Mon Sep 17 00:00:00 2001 From: Ali-D-Akbar Date: Wed, 3 Jun 2020 14:13:51 +0500 Subject: [PATCH 02/16] PROD-1285 --- cms/static/js/views/abstract_editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/static/js/views/abstract_editor.js b/cms/static/js/views/abstract_editor.js index 81ff65a5e1..c67fc164ad 100644 --- a/cms/static/js/views/abstract_editor.js +++ b/cms/static/js/views/abstract_editor.js @@ -9,6 +9,7 @@ define(['js/views/baseview', 'underscore'], function(BaseView, _) { // Backbone model cid is only unique within the collection. this.uniqueId = _.uniqueId(templateName + '_'); this.template = this.loadTemplate(templateName); + // xss-lint: disable=javascript-jquery-html this.$el.html(this.template({model: this.model, uniqueId: this.uniqueId})); this.listenTo(this.model, 'change', this.render); this.render(); From e883636cbc9f7886100c26e34db35a3bfa3011d6 Mon Sep 17 00:00:00 2001 From: Ali-D-Akbar Date: Wed, 3 Jun 2020 14:16:30 +0500 Subject: [PATCH 03/16] PROD-1288 --- cms/static/js/views/course_info_handout.js | 179 +++++++++++---------- 1 file changed, 91 insertions(+), 88 deletions(-) diff --git a/cms/static/js/views/course_info_handout.js b/cms/static/js/views/course_info_handout.js index f2fcc8723f..621f644b2c 100644 --- a/cms/static/js/views/course_info_handout.js +++ b/cms/static/js/views/course_info_handout.js @@ -1,102 +1,105 @@ -define(['js/views/baseview', 'codemirror', 'common/js/components/views/feedback_notification', 'js/views/course_info_helper', 'js/utils/modal'], - function(BaseView, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils) { +define([ + 'js/views/baseview', + 'codemirror', + 'common/js/components/views/feedback_notification', + 'js/views/course_info_helper', + 'js/utils/modal', + 'edx-ui-toolkit/js/utils/html-utils' +], +function(BaseView, CodeMirror, NotificationView, CourseInfoHelper, ModalUtils, HtmlUtils) { + 'use strict'; // the handouts view is dumb right now; it needs tied to a model and all that jazz - var CourseInfoHandoutsView = BaseView.extend({ - // collection is CourseUpdateCollection - events: { - 'click .save-button': 'onSave', - 'click .cancel-button': 'onCancel', - 'click .edit-button': 'onEdit' - }, + var CourseInfoHandoutsView = BaseView.extend({ + // collection is CourseUpdateCollection + events: { + 'click .save-button': 'onSave', + 'click .cancel-button': 'onCancel', + 'click .edit-button': 'onEdit' + }, - initialize: function() { - this.template = this.loadTemplate('course_info_handouts'); - var self = this; - this.model.fetch({ - complete: function() { - self.render(); - }, - reset: true - }); - }, + initialize: function() { + var self = this; + this.template = this.loadTemplate('course_info_handouts'); + this.model.fetch({ + complete: function() { + self.render(); + }, + reset: true + }); + }, - render: function() { - CourseInfoHelper.changeContentToPreview( - this.model, 'data', this.options.base_asset_url); + render: function() { + CourseInfoHelper.changeContentToPreview( + this.model, 'data', this.options.base_asset_url); + this.$el.html(HtmlUtils.HTML($(this.template({model: this.model}))).toString()); + HtmlUtils.setHtml($('.handouts-content'), HtmlUtils.HTML(this.model.get('data'))); + this.$preview = this.$el.find('.handouts-content'); + this.$form = this.$el.find('.edit-handouts-form'); + this.$editor = this.$form.find('.handouts-content-editor'); + this.$form.hide(); - this.$el.html( - $(this.template({ - model: this.model - })) - ); - $('.handouts-content').html(this.model.get('data')); - this.$preview = this.$el.find('.handouts-content'); - this.$form = this.$el.find('.edit-handouts-form'); - this.$editor = this.$form.find('.handouts-content-editor'); - this.$form.hide(); + return this; + }, - return this; - }, + onEdit: function() { + var self = this; + this.$editor.val(this.$preview.html()); + this.$form.show(); - onEdit: function(event) { - var self = this; - this.$editor.val(this.$preview.html()); - this.$form.show(); + this.$codeMirror = CourseInfoHelper.editWithCodeMirror( + self.model, 'data', self.options.base_asset_url, this.$editor.get(0)); - this.$codeMirror = CourseInfoHelper.editWithCodeMirror( - self.model, 'data', self.options.base_asset_url, this.$editor.get(0)); + ModalUtils.showModalCover(false, function() { self.closeEditor(); }); + }, - ModalUtils.showModalCover(false, function() { self.closeEditor(); }); - }, - - onSave: function(event) { - var handoutsContent = this.$codeMirror.getValue(); - $('#handout_error').removeClass('is-shown'); - $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); - if ($('.CodeMirror-lines').find('.cm-error').length == 0) { - if (handoutsContent === '') { - handoutsContent = '
    '; - } - this.model.set('data', handoutsContent); - var saving = new NotificationView.Mini({ - title: gettext('Saving') - }); - saving.show(); - this.model.save({}, { - success: function() { - saving.hide(); - } - }); - this.render(); - this.$form.hide(); - this.closeEditor(); - - analytics.track('Saved Course Handouts', { - course: course_location_analytics - }); - } else { - $('#handout_error').addClass('is-shown'); - $('.save-button').addClass('is-disabled').attr('aria-disabled', true); - event.preventDefault(); + onSave: function(event) { + var saving = new NotificationView.Mini({ + title: gettext('Saving') + }); + var handoutsContent = this.$codeMirror.getValue(); + $('#handout_error').removeClass('is-shown'); + $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); + if ($('.CodeMirror-lines').find('.cm-error').length === 0) { + if (handoutsContent === '') { + handoutsContent = '
      '; } - }, - - onCancel: function(event) { - $('#handout_error').removeClass('is-shown'); - $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); + this.model.set('data', handoutsContent); + saving.show(); + this.model.save({}, { + success: function() { + saving.hide(); + } + }); + this.render(); this.$form.hide(); this.closeEditor(); - }, - closeEditor: function() { - $('#handout_error').removeClass('is-shown'); - $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); - this.$form.hide(); - ModalUtils.hideModalCover(); - this.$form.find('.CodeMirror').remove(); - this.$codeMirror = null; + analytics.track('Saved Course Handouts', { // eslint-disable-line no-undef + course: course_location_analytics // eslint-disable-line no-undef + }); + } else { + $('#handout_error').addClass('is-shown'); + $('.save-button').addClass('is-disabled').attr('aria-disabled', true); + event.preventDefault(); } - }); + }, - return CourseInfoHandoutsView; - }); // end define() + onCancel: function() { + $('#handout_error').removeClass('is-shown'); + $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); + this.$form.hide(); + this.closeEditor(); + }, + + closeEditor: function() { + $('#handout_error').removeClass('is-shown'); + $('.save-button').removeClass('is-disabled').attr('aria-disabled', false); + this.$form.hide(); + ModalUtils.hideModalCover(); + this.$form.find('.CodeMirror').remove(); + this.$codeMirror = null; + } + }); + + return CourseInfoHandoutsView; +}); // end define() From 26f4b0f54b52826144769828118835b736a6798c Mon Sep 17 00:00:00 2001 From: Ali-D-Akbar Date: Wed, 3 Jun 2020 20:01:59 +0500 Subject: [PATCH 04/16] PROD-1280 --- .../js/certificates/views/certificate_preview.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cms/static/js/certificates/views/certificate_preview.js b/cms/static/js/certificates/views/certificate_preview.js index d07f3d1fdc..5300f784c9 100644 --- a/cms/static/js/certificates/views/certificate_preview.js +++ b/cms/static/js/certificates/views/certificate_preview.js @@ -8,9 +8,10 @@ define([ 'js/views/baseview', 'common/js/components/utils/view_utils', 'common/js/components/views/feedback_notification', - 'text!templates/certificate-web-preview.underscore' + 'text!templates/certificate-web-preview.underscore', + 'edx-ui-toolkit/js/utils/html-utils' ], -function(_, gettext, BaseView, ViewUtils, NotificationView, certificateWebPreviewTemplate) { +function(_, gettext, BaseView, ViewUtils, NotificationView, certificateWebPreviewTemplate, HtmlUtils) { 'use strict'; var CertificateWebPreview = BaseView.extend({ el: $('.preview-certificate'), @@ -27,7 +28,7 @@ function(_, gettext, BaseView, ViewUtils, NotificationView, certificateWebPrevie }, render: function() { - this.$el.html(_.template(certificateWebPreviewTemplate)({ + HtmlUtils.setHtml(this.$el, HtmlUtils.template(certificateWebPreviewTemplate)({ course_modes: this.course_modes, certificate_web_view_url: this.certificate_web_view_url, is_active: this.is_active @@ -36,13 +37,8 @@ function(_, gettext, BaseView, ViewUtils, NotificationView, certificateWebPrevie }, toggleCertificateActivation: function() { - var msg = 'Activating'; - if (this.is_active) { - msg = 'Deactivating'; - } - var notification = new NotificationView.Mini({ - title: gettext(msg) + title: gettext(this.is_active ? 'Deactivating' : 'Activating') }); $.ajax({ From d3a9895f822f31599c3ffd6bd95c7515a3b759ac Mon Sep 17 00:00:00 2001 From: Ali-D-Akbar Date: Thu, 4 Jun 2020 15:04:34 +0500 Subject: [PATCH 05/16] PROD-1283 --- cms/static/js/views/asset.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cms/static/js/views/asset.js b/cms/static/js/views/asset.js index 541ffe780c..ee5721343c 100644 --- a/cms/static/js/views/asset.js +++ b/cms/static/js/views/asset.js @@ -1,6 +1,7 @@ define(['js/views/baseview', 'underscore', 'gettext', 'common/js/components/views/feedback_prompt', - 'common/js/components/views/feedback_notification'], - function(BaseView, _, gettext, PromptView, NotificationView) { + 'common/js/components/views/feedback_notification', 'edx-ui-toolkit/js/utils/html-utils'], + function(BaseView, _, gettext, PromptView, NotificationView, HtmlUtils) { + 'use strict'; var AssetView = BaseView.extend({ initialize: function() { this.template = this.loadTemplate('asset'); @@ -14,7 +15,7 @@ define(['js/views/baseview', 'underscore', 'gettext', 'common/js/components/view render: function() { var uniqueId = _.uniqueId('lock_asset_'); - this.$el.html(this.template({ + var attributes = { display_name: this.model.get('display_name'), thumbnail: this.model.get('thumbnail'), date_added: this.model.get('date_added'), @@ -23,32 +24,32 @@ define(['js/views/baseview', 'underscore', 'gettext', 'common/js/components/view portable_url: this.model.get('portable_url'), asset_type: this.model.get_extension(), uniqueId: uniqueId - })); + }; + this.$el.html(HtmlUtils.HTML(this.template(attributes)).toString()); this.updateLockState(); return this; }, updateLockState: function() { - var locked_class = 'is-locked'; + var lockedClass = 'is-locked'; // Add a class of "locked" to the tr element if appropriate, // and toggle locked state of hidden checkbox. if (this.model.get('locked')) { - this.$el.addClass(locked_class); + this.$el.addClass(lockedClass); this.$el.find('.lock-checkbox').attr('checked', 'checked'); } else { - this.$el.removeClass(locked_class); + this.$el.removeClass(lockedClass); this.$el.find('.lock-checkbox').removeAttr('checked'); } }, confirmDelete: function(e) { + var asset = this.model; if (e && e.preventDefault) { e.preventDefault(); } - var asset = this.model, - collection = this.model.collection; new PromptView.Warning({ title: gettext('Delete File Confirmation'), - message: gettext('Are you sure you wish to delete this item. It cannot be reversed!\n\nAlso any content that links/refers to this item will no longer work (e.g. broken images and/or links)'), + message: gettext('Are you sure you wish to delete this item. It cannot be reversed!\n\nAlso any content that links/refers to this item will no longer work (e.g. broken images and/or links)'), // eslint-disable-line max-len actions: { primary: { text: gettext('Delete'), @@ -76,7 +77,7 @@ define(['js/views/baseview', 'underscore', 'gettext', 'common/js/components/view }).show(); }, - lockAsset: function(e) { + lockAsset: function() { var asset = this.model; var saving = new NotificationView.Mini({ title: gettext('Saving') From 028c6e1d56f0b40bceebbadd5e4784bd768626d9 Mon Sep 17 00:00:00 2001 From: SaadYousaf Date: Fri, 5 Jun 2020 09:08:03 +0500 Subject: [PATCH 06/16] PROD-1255 --- cms/static/js/certificates/views/certificate_editor.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cms/static/js/certificates/views/certificate_editor.js b/cms/static/js/certificates/views/certificate_editor.js index e08ac582b4..fa19bd2de2 100644 --- a/cms/static/js/certificates/views/certificate_editor.js +++ b/cms/static/js/certificates/views/certificate_editor.js @@ -8,10 +8,11 @@ define([ 'js/views/list_item_editor', 'js/certificates/models/signatory', 'js/certificates/views/signatory_editor', - 'text!templates/certificate-editor.underscore' + 'text!templates/certificate-editor.underscore', + 'edx-ui-toolkit/js/utils/html-utils' ], function($, _, Backbone, gettext, - ListItemEditorView, SignatoryModel, SignatoryEditorView, certificateEditorTemplate) { + ListItemEditorView, SignatoryModel, SignatoryEditorView, certificateEditorTemplate, HtmlUtils) { 'use strict'; // If signatories limit is required to specific value then we can change it. @@ -75,7 +76,7 @@ function($, _, Backbone, gettext, isEditingAllCollections: true, eventAgg: self.eventAgg }); - self.$('div.signatory-edit-list').append($(signatory_view.render())); + self.$('div.signatory-edit-list').append(HtmlUtils.HTML((signatory_view.render())).toString()); }); this.disableAddSignatoryButton(); return this; From 684be2382ec17d6c6c63c030e4c314cb768ecc8f Mon Sep 17 00:00:00 2001 From: SaadYousaf Date: Mon, 8 Jun 2020 08:25:50 +0500 Subject: [PATCH 07/16] PROD-1284 --- .../models/settings/course_grading_policy.js | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/cms/static/js/models/settings/course_grading_policy.js b/cms/static/js/models/settings/course_grading_policy.js index 5b2e7003d8..f954e5bf03 100644 --- a/cms/static/js/models/settings/course_grading_policy.js +++ b/cms/static/js/models/settings/course_grading_policy.js @@ -1,6 +1,7 @@ /* globals _ */ -define(['backbone', 'js/models/location', 'js/collections/course_grader'], - function(Backbone, Location, CourseGraderCollection) { +define(['backbone', 'js/models/location', 'js/collections/course_grader', 'edx-ui-toolkit/js/utils/string-utils'], + function(Backbone, Location, CourseGraderCollection, StringUtils) { + 'use strict'; var CourseGradingPolicy = Backbone.Model.extend({ defaults: { graders: null, // CourseGraderCollection @@ -37,9 +38,15 @@ define(['backbone', 'js/models/location', 'js/collections/course_grader'], }, gracePeriodToDate: function() { var newDate = new Date(); - if (this.has('grace_period') && this.get('grace_period').hours) { newDate.setHours(this.get('grace_period').hours); } else newDate.setHours(0); - if (this.has('grace_period') && this.get('grace_period').minutes) { newDate.setMinutes(this.get('grace_period').minutes); } else newDate.setMinutes(0); - if (this.has('grace_period') && this.get('grace_period').seconds) { newDate.setSeconds(this.get('grace_period').seconds); } else newDate.setSeconds(0); + if (this.has('grace_period') && this.get('grace_period').hours) { + newDate.setHours(this.get('grace_period').hours); + } else newDate.setHours(0); + if (this.has('grace_period') && this.get('grace_period').minutes) { + newDate.setMinutes(this.get('grace_period').minutes); + } else newDate.setMinutes(0); + if (this.has('grace_period') && this.get('grace_period').seconds) { + newDate.setSeconds(this.get('grace_period').seconds); + } else newDate.setSeconds(0); return newDate; }, @@ -62,6 +69,7 @@ define(['backbone', 'js/models/location', 'js/collections/course_grader'], return parseInt(minimum_grade_credit); }, validate: function(attrs) { + var minimumGradeCutoff; if (_.has(attrs, 'grace_period')) { if (attrs.grace_period === null) { return { @@ -71,12 +79,13 @@ define(['backbone', 'js/models/location', 'js/collections/course_grader'], } if (this.get('is_credit_course') && _.has(attrs, 'minimum_grade_credit')) { // Getting minimum grade cutoff value - var minimum_grade_cutoff = _.min(_.values(attrs.grade_cutoffs)); - if (isNaN(attrs.minimum_grade_credit) || attrs.minimum_grade_credit === null || attrs.minimum_grade_credit < minimum_grade_cutoff) { + minimumGradeCutoff = _.min(_.values(attrs.grade_cutoffs)); + if (isNaN(attrs.minimum_grade_credit) || attrs.minimum_grade_credit === null || + attrs.minimum_grade_credit < minimumGradeCutoff) { return { - minimum_grade_credit: interpolate( + minimum_grade_credit: StringUtils.interpolate( gettext('Not able to set passing grade to less than %(minimum_grade_cutoff)s%.'), - {minimum_grade_cutoff: minimum_grade_cutoff * 100}, + {minimum_grade_cutoff: minimumGradeCutoff * 100}, true ) }; From 33543bb9142d8a72514dd8db8300332bcf26cc1d Mon Sep 17 00:00:00 2001 From: SaadYousaf Date: Mon, 8 Jun 2020 08:28:19 +0500 Subject: [PATCH 08/16] PROD-1258 --- .../js/models/settings/course_details.js | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/cms/static/js/models/settings/course_details.js b/cms/static/js/models/settings/course_details.js index 394883d480..c0d8c506f4 100644 --- a/cms/static/js/models/settings/course_details.js +++ b/cms/static/js/models/settings/course_details.js @@ -1,5 +1,8 @@ -define(['backbone', 'underscore', 'gettext', 'js/models/validation_helpers', 'js/utils/date_utils'], - function(Backbone, _, gettext, ValidationHelpers, DateUtils) { +define(['backbone', 'underscore', 'gettext', 'js/models/validation_helpers', 'js/utils/date_utils', + 'edx-ui-toolkit/js/utils/string-utils' +], + function(Backbone, _, gettext, ValidationHelpers, DateUtils, StringUtils) { + 'use strict'; var CourseDetails = Backbone.Model.extend({ defaults: { org: '', @@ -51,11 +54,17 @@ define(['backbone', 'underscore', 'gettext', 'js/models/validation_helpers', 'js if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) { errors.end_date = gettext('The course end date must be later than the course start date.'); } - if (newattrs.start_date && newattrs.enrollment_start && newattrs.start_date < newattrs.enrollment_start) { - errors.enrollment_start = gettext('The course start date must be later than the enrollment start date.'); + if (newattrs.start_date && newattrs.enrollment_start && + newattrs.start_date < newattrs.enrollment_start) { + errors.enrollment_start = gettext( + 'The course start date must be later than the enrollment start date.' + ); } - if (newattrs.enrollment_start && newattrs.enrollment_end && newattrs.enrollment_start >= newattrs.enrollment_end) { - errors.enrollment_end = gettext('The enrollment start date cannot be after the enrollment end date.'); + if (newattrs.enrollment_start && newattrs.enrollment_end && + newattrs.enrollment_start >= newattrs.enrollment_end) { + errors.enrollment_end = gettext( + 'The enrollment start date cannot be after the enrollment end date.' + ); } if (newattrs.end_date && newattrs.enrollment_end && newattrs.end_date < newattrs.enrollment_end) { errors.enrollment_end = gettext('The enrollment end date cannot be after the course end date.'); @@ -78,7 +87,9 @@ define(['backbone', 'underscore', 'gettext', 'js/models/validation_helpers', 'js max: 100 }; if (!ValidationHelpers.validateIntegerRange(newattrs.entrance_exam_minimum_score_pct, range)) { - errors.entrance_exam_minimum_score_pct = interpolate(gettext('Please enter an integer between %(min)s and %(max)s.'), range, true); + errors.entrance_exam_minimum_score_pct = StringUtils.interpolate(gettext( + 'Please enter an integer between %(min)s and %(max)s.' + ), range, true); } } if (!_.isEmpty(errors)) return errors; @@ -90,7 +101,8 @@ define(['backbone', 'underscore', 'gettext', 'js/models/validation_helpers', 'js set_videosource: function(newsource) { // newsource either is