Skip to content

Instantly share code, notes, and snippets.

@dancgray
Last active July 10, 2019 14:59
Show Gist options
  • Save dancgray/bf9e026d8b79cd19151aed1976b6b851 to your computer and use it in GitHub Desktop.
Save dancgray/bf9e026d8b79cd19151aed1976b6b851 to your computer and use it in GitHub Desktop.
Overly complex version of scaffoldListView.js with courseassets cleaned up on item removal
define([
'core/origin',
'core/helpers',
'core/models/courseAssetModel',
'core/collections/contentCollection',
'backbone-forms',
'backbone-forms-lists'
], function(Origin, Helpers, CourseAssetModel, ContentCollection, BackboneForms) {
var ScaffoldListView = Backbone.Form.editors.List.extend({
defaultValue: [],
render: function() {
var instance = Backbone.Form.editors.__List.prototype.render.apply(this, arguments);
// set-up drag 'n drop
this.$list.sortable({
placeholder: 'sortable-placeholder',
containment: '.app-inner',
update: this.updateItemPositions.bind(this),
start: function(event, ui) {
Origin.scaffold.getCurrentModel().set('_isDragging', true);
ui.placeholder.height(ui.item.height());
},
stop: function(event, ui) {
_.defer(function() {
Origin.scaffold.getCurrentModel().set('_isDragging', false);
});
}
});
return instance;
},
updateItemPositions: function(e, ui) {
var $items = $('.list-item', this.$el);
var newIndex = $items.index(ui.item);
for(var i = 0, count = this.items.length; i < count; i++) {
var item = this.items[i];
if(!item.$el.is(ui.item)) continue;
this.items.splice(i, 1);
this.items.splice(newIndex, 0, item);
this.trigger('change', this);
return; // found our match, end here
}
},
// HACK needed to fix reset functionality (see https://github.com/powmedia/backbone-forms/issues/517)
setValue: function(value) {
this.value = value;
// remove previous items, and add new ones
this.items.forEach(function(item) { item.remove(); });
this.items = [];
this.value.forEach(this.addItem.bind(this));
},
/**
* Accomodate sweetalert in item removal
*/
removeItem: function(item) {
var remove = function(isConfirmed) {
if(isConfirmed === false) return;
var index = this.items.indexOf(item);
// pass item data to asset checker, use editor value so we can delete assets added before model is saved
var itemFormData = this.items[index] ? this.items[index] : {};
if (itemFormData.editor && itemFormData.editor.value) {
itemAssetCheck(itemFormData.editor.value, 'delete');
}
this.items[index].remove();
this.items.splice(index, 1);
if(item.addEventTriggered) {
this.trigger('remove', this, item.editor);
this.trigger('change', this);
}
if(!this.items.length && !this.Editor.isAsync) {
this.addItem();
}
}.bind(this);
// no confirmation needed, just remove
if(! this.schema.confirmDelete) return remove();
// confirm delete action
window.confirm({
title: this.schema.confirmDelete,
type: 'warning',
callback: remove
});
}
});
var ScaffoldListItemView = Backbone.Form.editors.List.Item.extend({
events: function() {
return _.extend({}, Backbone.Form.editors.__List.__Item.prototype.events, {
'click [data-action="clone"]': 'cloneItem'
});
},
cloneItem: function(event) {
itemAssetCheck(this.editor.value, 'create');
this.list.addItem(this.editor.value, true);
}
});
Origin.on('origin:dataReady', function() {
// NOTE override default list view (keep the old one in case...)
Backbone.Form.editors.__List = Backbone.Form.editors.List;
Backbone.Form.editors.__List.__Item = Backbone.Form.editors.List.Item;
Backbone.Form.editors.List = ScaffoldListView;
Backbone.Form.editors.List.Item = ScaffoldListItemView;
// overrides
Backbone.Form.editors.List.prototype.constructor.template = Handlebars.templates.list;
Backbone.Form.editors.List.Item.prototype.constructor.template = Handlebars.templates.listItem;
Backbone.Form.editors.List.Modal.prototype.itemToString = modalItemToString;
Backbone.Form.editors.List.Modal.prototype.__openEditor = Backbone.Form.editors.List.Modal.prototype.openEditor;
Backbone.Form.editors.List.Modal.prototype.openEditor = openEditor;
});
/**
* Helper functions
*/
/**
* Builds a string from nested values
* OVERRIDES Backbone.Form.editors.List.Modal.prototype.itemToString
*/
function modalItemToString(value) {
if(!value) {
return '';
}
return Object.keys(this.nestedSchema).reduce(function(parts, key) {
var val = getModalItemValueString(value[key]);
var title = this.nestedSchema[key].title || Backbone.Form.Field.prototype.createTitle.call({ key: key });
return parts + '<p class="list-item-modal-item">' + wrapSchemaTitle(title) + val + '</p>';
}.bind(this), '');
}
/**
* FIX to avoid opening the modal after stopping dragging a list item
* OVERRIDES Backbone.Form.editors.List.Modal.prototype.openEditor
*/
function openEditor() {
if(Origin.scaffold.getCurrentModel().get('_isDragging')) {
return;
}
Backbone.Form.editors.List.Modal.prototype.__openEditor.apply(this, arguments);
}
/**
* Returns an apt string value from Modal.Item value
*/
function getModalItemValueString(value) {
if (typeof value !== 'object') {
return value;
}
if(Array.isArray(value)) {
return Origin.l10n.t('app.items', { smart_count: Object.keys(value).length });
}
// print nested name/value pairs
var pairs = '';
for (var name in value) {
if(value.hasOwnProperty(name)) pairs += '<br />' + wrapSchemaTitle(name) + value[name];
}
return '<p class="list-item-modal-object">' + pairs + '</p>';
}
function wrapSchemaTitle(value) {
return '<span class="list-item-description">' + value + ':</span>';
}
function getAssetId(filterText, cb) {
var pattern = '.*' + filterText.toLowerCase() + '.*';
$.ajax({
url: 'api/asset/query',
type:'GET',
data: {search: { filename: filterText }},
success: function (data) {
if (data.length == 0) return cb(Origin.l10n.t('app.errorsaveasset'));
return cb(null, data[0]._id);
},
error: function() {
cb(Origin.l10n.t('app.errorsaveasset'));
}
});
}
function openNotifyAlert(type, text) {
Origin.Notify.alert({
type: type,
text: text
});
}
/**
* checks all attributes in a list item for assets and creates or deletes associated course asset record
*/
function itemAssetCheck(assetItem, action) {
if (typeof assetItem !== 'object' || !action) {
return 'error';
}
var flatItem = Helpers.flattenNestedProperties(assetItem);
var itemValues = _.values(flatItem);
var parentAttributes = Origin.scaffold.getCurrentModel().attributes;
_.each(itemValues, function(item) {
if (item.length !== 0 && item.indexOf('course/assets') === 0) {
var itemFileName = item.substring(item.lastIndexOf('/')+1);
if (typeof itemFileName !== 'string') openNotifyAlert('error', Origin.l10n.t('app.errorsaveasset'));
if (action === 'create') {
getAssetId(itemFileName, function(error, assetId) {
if (error || !assetId) {
return openNotifyAlert('error', Origin.l10n.t('app.errorsaveasset'));
}
(new CourseAssetModel()).save({
_courseId : Origin.editor.data.course.get('_id'),
_contentType : parentAttributes._type,
_contentTypeId : parentAttributes._id,
_fieldName : itemFileName,
_assetId : assetId,
_contentTypeParentId: parentAttributes._parentId
}, {
error: function(error) {
openNotifyAlert('error', Origin.l10n.t('app.errorsaveasset'));
}
});
});
} else if (action === 'delete') {
var itemAssetCollection = new ContentCollection(null, { _type: 'courseasset' }).fetch({
data: {
_contentTypeId: parentAttributes._id,
_contentType: parentAttributes._type,
_fieldName: itemFileName
},
success: function(collection) {
if (!collection.length) return;
var listModels = collection.models ? collection.models.slice() : collection.slice();
var listModel = listModels[0];
if (!listModel) return;
listModel.destroy({
error: function() {
console.error('Failed to destroy courseasset record', listModel.get('_id'));
return;
}
});
},
error: function(model, response) {
return openNotifyAlert('error', Origin.l10n.t('app.errordelete'));
}
});
}
}
});
}
return ScaffoldListView;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment