193 lines
7.7 KiB
JavaScript
193 lines
7.7 KiB
JavaScript
/**
|
|
* The MoveXblockModal to move XBlocks in course.
|
|
*/
|
|
define([
|
|
'jquery',
|
|
'backbone',
|
|
'underscore',
|
|
'gettext',
|
|
'js/views/baseview',
|
|
'js/views/utils/xblock_utils',
|
|
'js/views/utils/move_xblock_utils',
|
|
'edx-ui-toolkit/js/utils/html-utils',
|
|
'edx-ui-toolkit/js/utils/string-utils',
|
|
'common/js/components/views/feedback',
|
|
'js/models/xblock_info',
|
|
'js/views/modals/base_modal',
|
|
'js/views/move_xblock_list',
|
|
'js/views/move_xblock_breadcrumb',
|
|
'text!templates/move-xblock-modal.underscore'
|
|
],
|
|
function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, Feedback,
|
|
XBlockInfoModel, BaseModal, MoveXBlockListView, MoveXBlockBreadcrumbView, MoveXblockModalTemplate) {
|
|
'use strict';
|
|
|
|
var MoveXblockModal = BaseModal.extend({
|
|
events: _.extend({}, BaseModal.prototype.events, {
|
|
'click .action-move:not(.is-disabled)': 'moveXBlock'
|
|
}),
|
|
|
|
options: $.extend({}, BaseModal.prototype.options, {
|
|
modalName: 'move-xblock',
|
|
modalSize: 'lg',
|
|
showEditorModeButtons: false,
|
|
addPrimaryActionButton: true,
|
|
primaryActionButtonType: 'move',
|
|
viewSpecificClasses: 'move-modal',
|
|
primaryActionButtonTitle: gettext('Move'),
|
|
modalSRTitle: gettext('Choose a location to move your component to')
|
|
}),
|
|
|
|
initialize: function() {
|
|
var self = this;
|
|
BaseModal.prototype.initialize.call(this);
|
|
this.sourceXBlockInfo = this.options.sourceXBlockInfo;
|
|
this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo;
|
|
this.targetParentXBlockInfo = null;
|
|
this.XBlockURLRoot = this.options.XBlockURLRoot;
|
|
this.XBlockAncestorInfoURL = StringUtils.interpolate(
|
|
'{urlRoot}/{usageId}?fields=ancestorInfo',
|
|
{urlRoot: this.XBlockURLRoot, usageId: this.sourceXBlockInfo.get('id')}
|
|
);
|
|
this.outlineURL = this.options.outlineURL;
|
|
this.options.title = this.getTitle();
|
|
this.fetchCourseOutline().done(function(courseOutlineInfo, ancestorInfo) {
|
|
$('.ui-loading').addClass('is-hidden');
|
|
$('.breadcrumb-container').removeClass('is-hidden');
|
|
self.renderViews(courseOutlineInfo, ancestorInfo);
|
|
});
|
|
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
|
|
this.listenTo(Backbone, 'move:enableMoveOperation', this.enableMoveOperation);
|
|
this.listenTo(Backbone, 'move:hideMoveModal', this.hide);
|
|
},
|
|
|
|
getTitle: function() {
|
|
return StringUtils.interpolate(
|
|
gettext('Move: {displayName}'),
|
|
{displayName: this.sourceXBlockInfo.get('display_name')}
|
|
);
|
|
},
|
|
|
|
getContentHtml: function() {
|
|
return _.template(MoveXblockModalTemplate)({});
|
|
},
|
|
|
|
show: function() {
|
|
BaseModal.prototype.show.apply(this, [false]);
|
|
this.updateMoveState(false);
|
|
MoveXBlockUtils.hideMovedNotification();
|
|
},
|
|
|
|
hide: function() {
|
|
if (this.moveXBlockListView) {
|
|
this.moveXBlockListView.remove();
|
|
}
|
|
if (this.moveXBlockBreadcrumbView) {
|
|
this.moveXBlockBreadcrumbView.remove();
|
|
}
|
|
BaseModal.prototype.hide.apply(this);
|
|
Feedback.prototype.outFocus.apply(this);
|
|
},
|
|
|
|
resize: function() {
|
|
// Do Nothing. Overridden to use our own styling instead of one provided by base modal
|
|
},
|
|
|
|
focusModal: function() {
|
|
Feedback.prototype.inFocus.apply(this, [this.options.modalWindowClass]);
|
|
$(this.options.modalWindowClass).focus();
|
|
},
|
|
|
|
fetchCourseOutline: function() {
|
|
return $.when(
|
|
this.fetchData(this.outlineURL),
|
|
this.fetchData(this.XBlockAncestorInfoURL)
|
|
);
|
|
},
|
|
|
|
fetchData: function(url) {
|
|
var deferred = $.Deferred();
|
|
$.ajax({
|
|
url: url,
|
|
contentType: 'application/json',
|
|
dataType: 'json',
|
|
type: 'GET'
|
|
}).done(function(data) {
|
|
deferred.resolve(data);
|
|
}).fail(function() {
|
|
deferred.reject();
|
|
});
|
|
return deferred.promise();
|
|
},
|
|
|
|
renderViews: function(courseOutlineInfo, ancestorInfo) {
|
|
this.moveXBlockBreadcrumbView = new MoveXBlockBreadcrumbView({});
|
|
this.moveXBlockListView = new MoveXBlockListView(
|
|
{
|
|
model: new XBlockInfoModel(courseOutlineInfo, {parse: true}),
|
|
sourceXBlockInfo: this.sourceXBlockInfo,
|
|
ancestorInfo: ancestorInfo
|
|
}
|
|
);
|
|
},
|
|
|
|
updateMoveState: function(isValidMove) {
|
|
var $moveButton = this.$el.find('.action-move');
|
|
if (isValidMove) {
|
|
$moveButton.removeClass('is-disabled');
|
|
} else {
|
|
$moveButton.addClass('is-disabled');
|
|
}
|
|
},
|
|
|
|
isValidCategory: function(targetParentXBlockInfo) {
|
|
var basicBlockTypes = ['course', 'chapter', 'sequential', 'vertical'],
|
|
sourceParentType = this.sourceParentXBlockInfo.get('category'),
|
|
targetParentType = targetParentXBlockInfo.get('category'),
|
|
sourceParentHasChildren = this.sourceParentXBlockInfo.get('has_children'),
|
|
targetParentHasChildren = targetParentXBlockInfo.get('has_children');
|
|
|
|
// Treat source parent component as vertical to support move child components under content experiment
|
|
// and other similar xblocks.
|
|
if (sourceParentHasChildren && !_.contains(basicBlockTypes, sourceParentType)) {
|
|
sourceParentType = 'vertical'; // eslint-disable-line no-param-reassign
|
|
}
|
|
|
|
// Treat target parent component as a vertical to support move to parentable target parent components.
|
|
// Also, moving a component directly to content experiment is not allowed, we need to visit to group level.
|
|
if (targetParentHasChildren && !_.contains(basicBlockTypes, targetParentType) &&
|
|
targetParentType !== 'split_test') {
|
|
targetParentType = 'vertical'; // eslint-disable-line no-param-reassign
|
|
}
|
|
return targetParentType === sourceParentType;
|
|
},
|
|
|
|
enableMoveOperation: function(targetParentXBlockInfo) {
|
|
var isValidMove = false;
|
|
|
|
// update target parent on navigation
|
|
this.targetParentXBlockInfo = targetParentXBlockInfo;
|
|
if (this.isValidCategory(targetParentXBlockInfo) &&
|
|
this.sourceParentXBlockInfo.id !== targetParentXBlockInfo.id && // same parent case
|
|
this.sourceXBlockInfo.id !== targetParentXBlockInfo.id) { // same source item case
|
|
isValidMove = true;
|
|
}
|
|
this.updateMoveState(isValidMove);
|
|
},
|
|
|
|
moveXBlock: function() {
|
|
MoveXBlockUtils.moveXBlock(
|
|
{
|
|
sourceXBlockElement: $("li.studio-xblock-wrapper[data-locator='" + this.sourceXBlockInfo.id + "']"),
|
|
sourceDisplayName: this.sourceXBlockInfo.get('display_name'),
|
|
sourceLocator: this.sourceXBlockInfo.id,
|
|
sourceParentLocator: this.sourceParentXBlockInfo.id,
|
|
targetParentLocator: this.targetParentXBlockInfo.id
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
return MoveXblockModal;
|
|
});
|