/*
* Copyright 2018 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Singleton class to add a service into selected page and zone
* @private
*/
Ext.define('Ametys.plugins.web.zone.ServiceActions', {
singleton: true,
/**
* @private
* @property {Ametys.window.DialogBox} _box The service dialog box.
*/
/**
* @private
* @property {Ametys.form.ConfigurableFormPanel} _form The configurable form panel in the service dialog box.
*/
/**
* @private
* @property {Boolean} _hasTabs True if the current service has tabs.
*/
/**
* @private
* @property {Number[]} _unvisitedCards The indexes of the cards which were not visited yet.
*/
/**
* Action function to be called by the controller.
* Update its state given the controller configuration and open the service configuration dialog box to create a service
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
* @param {String} controller.serviceId (required) The service identifier
*/
add: function (controller)
{
var target = controller.getMatchingTargets()[0];
if (target != null)
{
var btnConfig = controller.getInitialConfig();
var params = {
pageId: target.getParameters().id,
zoneName: target.getParameters()['zone-name'],
serviceId: controller.getInitialConfig('serviceId')
};
// Additional parameters
for (var c in btnConfig)
{
if (c.indexOf("service-") == 0)
{
params[c] = btnConfig[c];
}
}
this.open ('new', params, controller.getInitialConfig('params-action'));
}
},
/**
* Action function to be called by the controller.
* Update its state given the controller configuration and open the service configuration dialog box to edit a service.
* @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function
*/
edit: function (controller)
{
var target = controller.getMatchingTargets()[0];
var zoneItemId = target.getParameters()['zoneitem-id'];
var zoneItemTarget = target.getSubtarget(function (target) { return target.getId() == 'zoneitem' && target.getParameters().id == zoneItemId});
if (target == null || zoneItemId == null || !zoneItemTarget.getParameters().service)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WEB_CONTENT_CONFIGURESERVICE_SELECTION_ERROR}}",
text: "{{i18n PLUGINS_WEB_CONTENT_CONFIGURESERVICE_SELECTION_ERROR_DESC}}",
details: "",
category: this.self.getName()
});
return;
}
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.PageDAO",
methodName: 'getServiceParametersAction',
parameters: [zoneItemTarget.getParameters().service],
callback: {
handler: this._getServiceParametersActionCb,
scope: this,
arguments: {
serviceId: zoneItemTarget.getParameters().service,
pageId: target.getParameters().id,
zoneItemId: zoneItemTarget.getParameters().id
}
},
errorMessage: false
});
},
/**
* @private
* Callback function invoked after retrieving service parameters' action
* @param {String} paramAction The service parameters' action. If null or empty, the default one #_open will execute
* @param {Object} args The callback arguments
*/
_getServiceParametersActionCb: function (paramAction, args)
{
var params = {
serviceId: args.serviceId,
pageId: args.pageId,
zoneItemId: args.zoneItemId
};
this.open ('edit', params, paramAction);
},
/**
* Open the dialog box to configure service
* @param {String} mode The mode: 'new' for service creation or 'edit' for service configuration
* @param {Object} params the parameters
* @param {String} params.pageId (required) The page id
* @param {String} params.zoneName The zone name. Required for service creation
* @param {String} params.zoneItemId The zone item id. Required for service edition
* @param {String} params.serviceId (required) The service id
* @param {String} [paramsAction] the action name to execute to configure parameters instead of the default one
*/
open: function (mode, params, paramsAction)
{
var cb = mode == 'new' ? this._createServiceCb : this._editServiceCb;
if (!Ext.isEmpty(paramsAction))
{
Ametys.executeFunctionByName (paramsAction, null, null, params, Ext.bind(cb, this), mode);
}
else
{
this._open (params, Ext.bind(cb, this), mode);
}
},
/**
* @private
* Callback function after service has created and dialog box has closed.
* @param {Object} params The params passed to the {@link #open} method
*/
_createServiceCb: function (params)
{
var i18nDesc = "{{i18n PLUGINS_WEB_NOTIFICATION_CREATE_SERVICE_DESCRIPTION}}";
Ametys.data.ServerComm.callMethod({
role: "org.ametys.web.repository.page.PageDAO",
methodName: 'getServiceInfo',
parameters: [params.pageId, params.serviceId],
callback: {
handler: function(serviceInfo) {
Ametys.notify({
type: 'info',
iconGlyph: serviceInfo.iconGlyph,
icon: serviceInfo.iconGlyph != null ? null : Ametys.CONTEXT_PATH + serviceInfo.mediumIcon,
title: "{{i18n PLUGINS_WEB_NOTIFICATION_CREATE_SERVICE_TITLE}}",
description: Ext.String.format(i18nDesc, serviceInfo.label, serviceInfo['page-title']),
action: Ext.bind(Ametys.tool.ToolsManager.openTool, Ametys.tool.ToolsManager, [params.pageId.startsWith('sitemap://') ? 'uitool-sitemappage' : 'uitool-page', {id: params.pageId}], false)
});
}
},
errorMessage: true
});
},
/**
* @private
* Callback function after service has edited and dialog box has closed.
* @param {Object} params The params passed to the {@link #open} method
*/
_editServiceCb: function ()
{
// Nothing to do
},
/**
* Open the dialog box to configure service
* @param {Object} params the parameters
* @param {String} params.pageId (required) The page id
* @param {String} params.zoneName The zone name. Required for service creation
* @param {String} params.zoneItemId The zone item id. Required for service edition
* @param {String} params.serviceId (required) The service id
* @param {Function} callback the callback function
* @param {String} mode The mode: 'new' for service creation or 'edit' for service configuration
*/
_open: function(params, callback, mode)
{
var me = this;
function configureCallback(success)
{
if (success)
{
me._box.show();
me._initForm (params);
}
}
this._params = params;
this._mode = mode;
this._callback = callback;
this.configureForm(params, configureCallback);
},
/**
* @private
* Create the service dialog box
* @param {Object} cfg the configuration of the dialog box
*/
_createDialogBox: function(cfg)
{
this._box = Ext.create('Ametys.window.DialogBox', Ext.apply(cfg, {
title :"{{i18n PLUGINS_WEB_CONTENT_ADDSERVICE_PARAMETERS}}",
iconCls : 'ametysicon-hammer2',
scrollable: false,
layout: 'fit',
bodyPadding: '0',
items: [],
closeAction: 'destroy',
referenceHolder: true,
defaultButton: 'validate',
buttons : [{
itemId: 'button-previous',
text :"{{i18n PLUGINS_WEB_CONTENT_ADDSERVICE_PREV_BUTTON}}",
disabled: true,
handler: Ext.bind(this._navHandler, this, [-1])
},
{
itemId: 'button-next',
text :"{{i18n PLUGINS_WEB_CONTENT_ADDSERVICE_NEXT_BUTTON}}",
disabled: true,
handler: Ext.bind(this._navHandler, this, [1])
},
{
reference: 'validate',
itemId: 'button-ok',
text :"{{i18n PLUGINS_WEB_ZONE_ADDSERVICEACTION_OK}}",
handler: this._validate,
scope: this
}, {
text :"{{i18n PLUGINS_WEB_ZONE_ADDSERVICEACTION_CANCEL}}",
handler: function() { this._box.close(); },
scope: this
} ]
}));
},
/**
* Configure form for service parameters
* @param {Object} params the parameters
* @param {String} params.serviceId (required) The service id
* @param {String} params.pageId The page id. This is required if #zoneItemId is empty
* @param {String} params.zoneName The zone name. This is required if #zoneItemId is empty
* @param {String} params.zoneItemId The zone item id. This is required if #pageId is empty.
* @param {Function} callback the callback function
*/
configureForm: function(params, callback)
{
Ametys.plugins.web.zone.ZoneItemManager.getServiceParameterDefinitions([params.serviceId, params.pageId, params.zoneItemId, params.zoneName], this._getServiceParametersCb, {scope: this, arguments: [callback]});
},
/**
* @private
* Callback function after retrieving service's parameters.<br/>
* Draw the forms.
* @param {Object} response the server response
* @param {Object[]} args the callback arguments.
*/
_getServiceParametersCb: function(response, args)
{
// Configurable Form Panel
var elements = response.parameters.elements;
this._hasTabs = false;
var me = this;
Ext.Object.each(elements, function(key, value, object) {
if (value.role && value.role == 'tab')
{
me._hasTabs = true;
}
});
if (Ext.Object.isEmpty(elements))
{
// There is no parameter
if (this._mode == 'edit')
{
// Should never happen given that the button should be disabled
Ametys.Msg.show({
title: "{{i18n PLUGINS_WEB_CONTENT_CONFIGURESERVICE_LABEL}}",
msg: "{{i18n PLUGINS_WEB_CONTENT_CONFIGURESERVICE_NO_PARAMETERS}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.INFO
});
}
else
{
// Create service directly
Ametys.plugins.web.zone.ZoneItemManager.addService([this._params.pageId, this._params.zoneName, this._params.serviceId, {}], this._saveParametersCb, {scope: this});
}
return;
}
var cfpConfig = {
hideDisabledFields: true,
additionalWidgetsConfFromParams: {
contentType: 'contentType' // some widgets requires the contentType configuration
}
};
var tabsWidth = 180;
if (this._hasTabs)
{
Ext.apply(cfpConfig, {
tabPosition: 'left',
layout: 'fit',
itemsLayout: 'fit',
tabItemsLayout: 'anchor',
scrollable: false,
tabBar: {defaults: {width: tabsWidth}},
spacingCls: false
});
}
this._form = Ext.create('Ametys.form.ConfigurableFormPanel', cfpConfig);
this._form.configure(elements);
// Dialog box
var boxCfg = {};
this._boxDimensions(boxCfg, response.height, response.width, tabsWidth);
this._createDialogBox(boxCfg);
var unvisitedCardsSize = this._hasTabs ? Ext.Object.getSize(elements) : 1;
this._unvisitedCards = Array(unvisitedCardsSize).fill(null).map(function(v, i) {return i;}); // creates array [0, 1, 2, 3, 4, 5...]
this._box.add(this._form);
args[0](true);
},
/**
* @private
* Fill in the configuration object the height and the width of the dialog box.
* @param {Object} boxCfg The configuration of the dialog box to fill
* @param {Number} height The height to apply. Can be null
* @param {Number} width The width to apply. Can be null
* @param {Number} tabsDefaultWidth The width of the tabs when multiple groups
*/
_boxDimensions: function(boxCfg, height, width, tabsDefaultWidth)
{
var ratio = 0.80,
multGroupMaxHeight = window.innerHeight * ratio,
maxWidth = window.innerWidth * ratio;
if (height)
{
if (height >= 0)
{
// Apply specified height, but cannot be bigger than multGroupMaxHeight
boxCfg['height'] = Math.min(height, multGroupMaxHeight);
}
else
{
// height < 0, apply the max possible height
boxCfg['height'] = multGroupMaxHeight;
}
}
else
{
// Default, fit the height to the form
Ext.apply(boxCfg, {
freezeHeight: this._hasTabs,
minHeight: 300,
maxHeight: 500
});
}
// Default width
var boxCfgWidth = 820;
if (width)
{
if (width >= 0)
{
// Apply specified width, but cannot be bigger than maxWidth
boxCfgWidth = Math.min(width, maxWidth);
}
else
{
// width < 0, apply the max possible width
boxCfgWidth = maxWidth;
}
}
if (this._hasTabs)
{
// Add the tabs width for a service with multiple groups
boxCfgWidth += tabsDefaultWidth;
}
boxCfg['width'] = boxCfgWidth;
},
/**
* @private
* Initialize forms
* @param {Object} params the initial parameters
*/
_initForm: function(params)
{
if (this._hasTabs)
{
var tabPanel = this._getFormTabPanel();
tabPanel.on('tabchange', this._updateButtons, this);
}
this._updateButtons();
if (this._mode == 'edit')
{
Ametys.plugins.web.zone.ZoneItemManager.getServiceParameterValues([params.zoneItemId, params.serviceId], this._initFormCb, {scope: this});
}
else
{
this._initFormCb();
}
},
/**
* @private
* Initialize form values
* @param {Object} response The values of service parameters
*/
_initFormCb: function (response)
{
this._form.setValues(response);
this._form.focus();
},
/**
* @private
* Go to the previous or next page
* @param {Number} index 1 to go the next page, -1 to go to the previous page
*/
_navHandler: function (index)
{
// 'tabchange' listener will do the job for updating buttons
this._getFormTabPanel().setActiveTab(this._getCurrentCardIndex() + index);
},
/**
* @private
* Update dialog box buttons
*/
_updateButtons: function()
{
var currentCardIndex = 0,
lastCardIndex = 0;
if (this._hasTabs)
{
var cfpTabPanel = this._getFormTabPanel();
currentCardIndex = this._getCurrentCardIndex();
lastCardIndex = cfpTabPanel.items.getCount() - 1;
}
// PREVIOUS BUTTON
var hasAPreviousCard = currentCardIndex > 0;
// NEXT BUTTON
var hasANextCard = currentCardIndex < lastCardIndex;
// OK BUTTON
Ext.Array.remove(this._unvisitedCards, currentCardIndex);
var allCardsVisited = this._unvisitedCards.length == 0;
var enableOkBtn = allCardsVisited || this._mode == 'edit';
// APPLY
var nextButton = this._box.down("button[itemId='button-next']");
var previousButton = this._box.down("button[itemId='button-previous']");
var okButton = this._box.down("button[itemId='button-ok']");
previousButton.setDisabled(!hasAPreviousCard);
nextButton.setDisabled(!hasANextCard);
okButton.setDisabled(!enableOkBtn);
},
/**
* @private
* Gets the TabPanel of the ConfigurableFormPanel
* @return {Ext.tab.Panel} The tab panel of the form. Can be null (if there is only one group and thus no tab panel for instance)
*/
_getFormTabPanel: function()
{
if (this._hasTabs)
{
return this._form && this._form.getFormContainer().items.first();
}
else
{
return null;
}
},
/**
* @private
* Gets the index of the current card
* @return {Number} The index of the current card
*/
_getCurrentCardIndex: function()
{
if (this._hasTabs)
{
var cfpTabPanel = this._getFormTabPanel();
return cfpTabPanel.items.indexOf(cfpTabPanel.getLayout().activeItem);
}
else
{
return 0;
}
},
/**
* @private
* Validate the dialog box and create the service.
*/
_validate: function ()
{
var invalidFields = this._form.getInvalidFields();
var values = this._form.getJsonValues();
if (invalidFields.length > 0)
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_WEB_CONTENT_ADDSERVICE_PARAMETERS_SAVE}}",
msg: "{{i18n PLUGINS_WEB_CONTENT_ADDSERVICE_PARAMETERS_SAVE_INVALIDFIELDS}}" + Ametys.form.SaveHelper.getInvalidFieldsAsReadableList(invalidFields),
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
return;
}
if (this._mode == 'new')
{
Ametys.plugins.web.zone.ZoneItemManager.addService([this._params.pageId, this._params.zoneName, this._params.serviceId, values], this._saveParametersCb, {scope: this});
}
else
{
Ametys.plugins.web.zone.ZoneItemManager.editServiceParameterValues([this._params.zoneItemId, this._params.serviceId, values], this._saveParametersCb, {scope: this});
}
},
/**
* @private
* Callback after saving service
* @param {Object} response The server response
* @param {Object} args The callback arguments
*/
_saveParametersCb: function (response, args)
{
var errors = response.error;
if (Ext.isEmpty(errors))
{
if (this._box)
{
this._box.close();
}
// Execute callback function passed to #open method
if (Ext.isFunction(this._callback))
{
this._callback(this._params);
}
}
},
/**
* Action function to be called by the controller.
* Allow an user to copy a service, in order to duplicate it later (with a paste action).
* @param {Ametys.plugins.web.page.controller.CopyServiceController} controller The controller calling this function
*/
copy: function (controller)
{
var target = controller.getMatchingTargets()[0];
if (target != null)
{
var zoneItemId = target.getParameters()['zoneitem-id'];
var zoneItemTarget = target.getSubtarget(function (target) { return target.getId() == 'zoneitem' && target.getParameters().id == zoneItemId});
if (zoneItemId == null || !zoneItemTarget.getParameters().service)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WEB_CONTENT_COPY_SERVICE_SELECTION_ERROR}}",
text: "{{i18n PLUGINS_WEB_CONTENT_COPY_SERVICE_SELECTION_ERROR_DESC}}",
details: "",
category: this.self.getName()
});
return;
}
Ametys.clipboard.Clipboard.setData (Ametys.message.MessageTarget.ZONE_ITEM, {id: zoneItemId, service: zoneItemTarget.getParameters().service});
}
},
/**
* This action pastes the copied service in a new zone
* @param {Ametys.plugins.web.page.controller.PasteServiceController} controller The controller calling this function
*/
paste: function (controller)
{
var clipboardData = Ametys.clipboard.Clipboard.getData();
if (clipboardData.length > 0 && Ametys.clipboard.Clipboard.getType() == Ametys.message.MessageTarget.ZONE_ITEM && clipboardData[0].service)
{
var target = controller.getMatchingTargets()[0];
if (target != null)
{
Ametys.plugins.web.zone.ZoneItemManager.pasteService([clipboardData[0].id, clipboardData[0].service, target.getParameters().id, target.getParameters()['zone-name']]);
}
}
else
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_WEB_CONTENT_PASTE_SERVICE_LABEL}}",
msg: "{{i18n PLUGINS_WEB_CONTENT_PASTE_SERVICE_NO_COPIED_SERVICE_MSG}}",
buttons: Ext.Msg.OK,
icon: Ext.Msg.ERROR
});
}
}
});