404 lines
18 KiB
JavaScript
404 lines
18 KiB
JavaScript
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; under version 2
|
|
* of the License (non-upgradable).
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright (c) 2016-2017 (original work) Open Assessment Technologies SA;
|
|
*
|
|
*/
|
|
define([
|
|
'jquery',
|
|
'i18n',
|
|
'lodash',
|
|
'helpers',
|
|
'ui/component',
|
|
'ui/hider',
|
|
'ui/switch/switch',
|
|
'ui/button',
|
|
'tpl!qtiItemPci/pciManager/tpl/layout',
|
|
'tpl!qtiItemPci/pciManager/tpl/listing',
|
|
'tpl!qtiItemPci/pciManager/tpl/packageMeta',
|
|
'async',
|
|
'ui/dialog/confirm',
|
|
'ui/dialog',
|
|
'ui/feedback',
|
|
'ui/modal',
|
|
'ui/uploader',
|
|
'ui/filesender'
|
|
], function ($, __, _, helpers, component, hider, switchFactory, buttonFactory, layoutTpl, listingTpl, packageMetaTpl, asyncLib, confirmBox, dialog, feedback) {
|
|
'use strict';
|
|
|
|
var _fileTypeFilters = ['application/zip', 'application/x-zip-compressed', 'application/x-zip'],
|
|
_fileExtFilter = /.+\.(zip)$/;
|
|
|
|
var _defaults = {
|
|
loadUrl: null,
|
|
disableUrl: null,
|
|
enableUrl: null,
|
|
verifyUrl: null,
|
|
addUrl: null
|
|
};
|
|
|
|
var pciManager = {
|
|
open: function open() {
|
|
this.trigger('showListing');
|
|
this.getElement().appendTo('.pci-manager');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create a pci manager
|
|
*
|
|
* @param {Object} config
|
|
* @param {String} config.loadUrl - the service be called to load the list of pcis
|
|
* @param {String} config.verifyUrl - the service be called to verify a pci package
|
|
* @param {String} config.addUrl - the service be called to add a pci
|
|
* @param {String} config.enableUrl - the service be called to enable the pcis
|
|
* @param {String} config.disableUrl - the service be called to disable the pcis
|
|
* @returns {*}
|
|
*/
|
|
return function pciManagerFactory(config) {
|
|
|
|
var listing = {};
|
|
|
|
/**
|
|
* Create pci manager component
|
|
*
|
|
* @returns {Object} a pciManager component
|
|
* @fires pciManager#loaded - when the pci manager is initially loaded
|
|
* @fires pciManager#showListing - when the list of pci is displayed
|
|
* @fires pciManager#hideListing - when the list of pci is hidden
|
|
* @fires pciManager#updateListing - when the list of pci is updated
|
|
* @fires pciManager#pciEnabled - when a pci is enabled
|
|
* @fires pciManager#pciDisabled - when a pci is pci-disabled
|
|
*/
|
|
return component(pciManager, _defaults)
|
|
.setTemplate(layoutTpl)
|
|
.on('showListing', function () {
|
|
var $fileSelector = this.getElement().find('.file-selector'),
|
|
$title = $fileSelector.find('.title'),
|
|
$uploader = $fileSelector.find('.file-upload-container'),
|
|
$uploadForm = $uploader.parent('form'),
|
|
$switcher = $fileSelector.find('.upload-switcher a');
|
|
|
|
hider.show($switcher.filter('.upload'));
|
|
hider.hide($switcher.filter('.listing'));
|
|
$uploadForm.hide();
|
|
hider.hide($uploader);
|
|
$title.text(__('Manage custom interactions'));
|
|
|
|
this.trigger('updateListing');
|
|
})
|
|
.on('hideListing', function () {
|
|
var $fileSelector = this.getElement().find('.file-selector'),
|
|
$fileContainer = $fileSelector.find('.files'),
|
|
$placeholder = $fileSelector.find('.empty'),
|
|
$title = $fileSelector.find('.title'),
|
|
$uploader = $fileSelector.find('.file-upload-container'),
|
|
$uploadForm = $uploader.parent('form'),
|
|
$switcher = $fileSelector.find('.upload-switcher a');
|
|
|
|
hider.show($switcher.filter('.listing'));
|
|
hider.hide($switcher.filter('.upload'));
|
|
$switcher.filter('.listing').css({display: 'inline-block'});
|
|
$uploadForm.show();
|
|
hider.hide($fileContainer);
|
|
hider.hide($placeholder);
|
|
$title.text(__('Upload new custom interaction (zip package)'));
|
|
|
|
$uploader.uploader('reset');
|
|
hider.show($uploader);
|
|
})
|
|
.on('updateListing', function () {
|
|
var self = this,
|
|
urls = _.pick(this.config, ['disableUrl', 'enableUrl', 'unregisterUrl', 'exportPciUrl']),
|
|
$fileSelector = this.getElement().find('.file-selector'),
|
|
$fileContainer = $fileSelector.find('.files'),
|
|
$placeholder = $fileSelector.find('.empty');
|
|
if (_.size(listing)) {
|
|
|
|
hider.hide($placeholder);
|
|
|
|
$fileContainer
|
|
.empty()
|
|
.html(listingTpl({
|
|
interactions: listing
|
|
}));
|
|
$fileContainer.find('.actions').each(function () {
|
|
var pciDownloadButton = $(this).find('.pci-download-button');
|
|
var pciswitch = $(this).find('.pci-switch');
|
|
var pciUnregisterButton = $(this).find('.pci-unregister-button');
|
|
var $li = $(this).closest('li');
|
|
var typeIdentifier = $li.data('typeIdentifier');
|
|
var runtimeOnly = listing[typeIdentifier].runtimeOnly;
|
|
if (!runtimeOnly) {
|
|
switchFactory(pciswitch, {
|
|
on: {
|
|
active: !$li.hasClass('pci-disabled')
|
|
},
|
|
off: {
|
|
active: $li.hasClass('pci-disabled')
|
|
}
|
|
})
|
|
.on('on', function () {
|
|
$li.removeClass('pci-disabled');
|
|
$.getJSON(urls.enableUrl, {typeIdentifier: typeIdentifier}, function (data) {
|
|
if (data.success) {
|
|
listing[typeIdentifier].enabled = true;
|
|
self.trigger('pciEnabled', typeIdentifier);
|
|
}
|
|
});
|
|
})
|
|
.on('off', function () {
|
|
$li.addClass('pci-disabled');
|
|
$.getJSON(urls.disableUrl, {typeIdentifier: typeIdentifier}, function (data) {
|
|
if (data.success) {
|
|
listing[typeIdentifier].enabled = false;
|
|
self.trigger('pciDisabled', typeIdentifier);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
buttonFactory({
|
|
id: 'unregister',
|
|
type: 'info',
|
|
icon: 'bin',
|
|
label: __('Delete'),
|
|
class: 'unregister',
|
|
renderTo: pciUnregisterButton
|
|
})
|
|
.on('click', function confirmDialog() {
|
|
dialog({
|
|
class: 'icon-warning',
|
|
heading: __('Warning'),
|
|
message: __('You are about to delete the Portable Custom Interaction <strong>%s</strong> from the system.', typeIdentifier),
|
|
content: __('This action will affect all items that may be using it and cannot be undone. Please confirm your choice.'),
|
|
autoRender: true,
|
|
autoDestroy: true,
|
|
buttons: [
|
|
{
|
|
id: 'cancel',
|
|
type: 'regular',
|
|
label: __('Cancel'),
|
|
close: true
|
|
},
|
|
{
|
|
id: 'delete',
|
|
type: 'error',
|
|
label: __('Delete'),
|
|
close: true
|
|
}],
|
|
onDeleteBtn: function onDeleteBtn() {
|
|
$.getJSON(urls.unregisterUrl, {typeIdentifier: typeIdentifier}, function (data) {
|
|
if (data.success) {
|
|
delete listing[typeIdentifier];
|
|
self.trigger('pciDisabled', typeIdentifier);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
if(!runtimeOnly) {
|
|
buttonFactory({
|
|
id: 'exportPci',
|
|
type: 'info',
|
|
icon: 'import',
|
|
label: __('Download'),
|
|
renderTo: pciDownloadButton
|
|
})
|
|
.on('click', function () {
|
|
window.location = (urls.exportPciUrl + '?typeIdentifier=' + typeIdentifier);
|
|
})
|
|
}
|
|
});
|
|
|
|
hider.show($fileContainer);
|
|
} else {
|
|
hider.hide($fileContainer);
|
|
hider.show($placeholder);
|
|
}
|
|
})
|
|
.on('pciEnabled', function () {
|
|
this.trigger('updateListing');
|
|
})
|
|
.on('pciDisabled', function () {
|
|
this.trigger('updateListing');
|
|
})
|
|
.on('render', function () {
|
|
|
|
//init variables:
|
|
var self = this,
|
|
urls = _.pick(this.config, ['loadUrl', 'disableUrl', 'enableUrl', 'verifyUrl', 'addUrl']),
|
|
$container = this.getElement(),
|
|
$fileSelector = $container.find('.file-selector'),
|
|
$uploader = $fileSelector.find('.file-upload-container'),
|
|
$switcher = $fileSelector.find('.upload-switcher a'),
|
|
$uploadForm;
|
|
|
|
//init event listeners
|
|
initEventListeners();
|
|
initUploader();
|
|
|
|
//load list of custom interactions from server
|
|
$.getJSON(urls.loadUrl, function (data) {
|
|
//note : init as empty object and not array otherwise _.size will fail later
|
|
listing = _.size(data) ? data : {};
|
|
self.trigger('updateListing', data);
|
|
self.trigger('loaded', data);
|
|
});
|
|
|
|
function initEventListeners() {
|
|
//switch to upload mode
|
|
$switcher.on('click', function (e) {
|
|
e.preventDefault();
|
|
if (hider.isHidden($uploader)) {
|
|
self.trigger('hideListing');
|
|
} else {
|
|
self.trigger('showListing');
|
|
}
|
|
});
|
|
}
|
|
|
|
function initUploader() {
|
|
|
|
var errors = [],
|
|
selectedFiles = {};
|
|
|
|
$uploader.on('upload.uploader', function (e, file, interactionHook) {
|
|
|
|
listing[interactionHook.typeIdentifier] = interactionHook;
|
|
self.trigger('pciAdded', interactionHook.typeIdentifier);
|
|
|
|
})
|
|
.on('fail.uploader', function (e, file, err) {
|
|
|
|
errors.push(__('Unable to upload file %s : %s', file.name, err));
|
|
|
|
})
|
|
.on('end.uploader', function () {
|
|
|
|
if (errors.length === 0) {
|
|
self.trigger('showListing');
|
|
} else {
|
|
feedback().error("<ul><li>" + errors.join('</li><li>') + "</li></ul>", {encodeHtml: false});
|
|
}
|
|
//reset errors
|
|
errors = [];
|
|
|
|
})
|
|
.on('create.uploader', function () {
|
|
|
|
//get ref to the uploadForm for later verification usage
|
|
$uploadForm = $uploader.parent('form');
|
|
$uploadForm.hide();
|
|
|
|
})
|
|
.on('fileselect.uploader', function () {
|
|
$uploadForm.find('li[data-file-name]').each(function () {
|
|
|
|
var $li = $(this),
|
|
filename = $li.data('file-name'),
|
|
packageMeta = selectedFiles[filename];
|
|
|
|
if (packageMeta) {
|
|
//update label:
|
|
$li.prepend(packageMetaTpl(packageMeta));
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
$uploader.uploader({
|
|
upload: true,
|
|
multiple: true,
|
|
uploadUrl: urls.addUrl,
|
|
fileSelect: function fileSelect(files, done) {
|
|
|
|
var givenLength = files.length;
|
|
|
|
//check the mime-type
|
|
files = _.filter(files, function (file) {
|
|
// for some weird reasons some browsers have quotes around the file type
|
|
var checkType = file.type.replace(/("|')/g, '');
|
|
return _.contains(_fileTypeFilters, checkType) || (checkType === '' && _fileExtFilter.test(file.name));
|
|
});
|
|
|
|
if (files.length !== givenLength) {
|
|
feedback().error('Invalid files have been removed');
|
|
}
|
|
|
|
//reset selectedFiles list
|
|
selectedFiles = {};
|
|
|
|
//verify selected files
|
|
asyncLib.filter(files, verify, done);
|
|
}
|
|
});
|
|
|
|
function verify(file, cb) {
|
|
|
|
$uploadForm.sendfile({
|
|
url: urls.verifyUrl,
|
|
file: file,
|
|
loaded: function (r) {
|
|
|
|
function done(ok) {
|
|
if (ok) {
|
|
selectedFiles[file.name] = {
|
|
typeIdentifier: r.typeIdentifier,
|
|
label: r.label,
|
|
version: r.version,
|
|
model: r.model
|
|
};
|
|
}
|
|
cb(ok);
|
|
}
|
|
|
|
if (r.valid) {
|
|
if (r.exists) {
|
|
confirmBox(
|
|
__('There is already one interaction with the same identifier "%s" (label : "%s") and same version : %s. Do you want to override the existing one ?', r.typeIdentifier, r.label, r.version),
|
|
function () {
|
|
done(true);
|
|
}, function () {
|
|
done(false);
|
|
});
|
|
} else {
|
|
done(true);
|
|
}
|
|
} else {
|
|
if (_.isArray(r.package)) {
|
|
_.each(r.package, function (report) {
|
|
if (_.isArray(report.messages)) {
|
|
_.forEach(report.messages, function (msg) {
|
|
feedback().error(msg.message);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
done(false);
|
|
}
|
|
},
|
|
failed: function (message) {
|
|
cb(new Error(message));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
})
|
|
.init(config);
|
|
};
|
|
});
|