/*
* Copyright 2016 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.
*/
/**
* Dialog box displaying a form to edit or create a project
* @private
*/
Ext.define('Ametys.plugins.workspaces.project.helper.ProjectEdition', {
singleton: true,
/**
* @property {Function} _cbFn Optional callback
* @private
*/
/**
* @property {Ametys.plugins.workspaces.project.Project} _project The editing project. Can be null
* @private
*/
/**
* @property {String} _mode The mode of the form (either 'new' for a creation, or 'edit' for an edition)
*/
/**
* @property {Ext.form.Panel} _form The form panel
*/
/**
* @property {Ametys.window.DialogBox} _box The dialog box that contains the form
*/
/**
* @property {Ext.util.DelayedTask} _updateNameTask The task to update the name field. It is used to buffer the execution of the update name method.
* @private
*/
/**
* @property {Boolean} _initialized True if the dialog box was already initialized
* @private
*/
_initialized: false,
/**
* Open and initialize the dialogbox given mode and parameters
* @param {String} mode The mode: "new" to create a new project, "edit" to edit an existing project
* @param {Ametys.plugins.workspaces.project.Project} project The project to edit. Can be null on "new" mode
* @param {Function} callback The callback on successful creation/modification of the project. The arguments are:
* @param {Function} callback.id The identifier of the created or modified project
* @param {Object} scope The callback scope
*/
act: function(mode, project, callback, scope)
{
this._mode = mode || 'new';
this._cbFn = callback;
this._cbScope = scope;
this._project = project;
if (!this._isConfigurationComplete())
{
Ametys.log.ErrorDialog.display({
title: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_ERROR_CONFIGURATION}}",
text: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_ERROR_CONFIGURATION_INCOMPLETE}}",
category: Ext.getClassName(this)
});
return;
}
this._delayedInitialize();
this._box.setTitle(this._mode == 'edit' ? "{{i18n PLUGINS_WORKSPACES_PROJECT_EDIT_DIALOG_TITLE}}" : "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_TITLE}}");
this._box.show();
this._initForm();
},
/**
* Ensure that mandatory configuration parameters are present.
* @return {Boolean}
*/
_isConfigurationComplete: function()
{
return (this._mode == 'new')
|| (this._mode == 'edit' && this._project.getId());
},
/**
* Initialize the dialog box
* @private
*/
_delayedInitialize: function()
{
if (this._initialized)
{
return;
}
this._form = Ext.create('Ext.form.Panel', {
border: false,
layout: {
type: 'vbox',
align: 'stretch'
},
defaults: {
cls: 'ametys',
labelWidth: 120,
labelAlign: 'right',
labelSeparator: '',
msgTarget: 'side'
},
items: this._getFormItemsCfg()
});
this._box = Ext.create('Ametys.window.DialogBox', {
iconCls: 'ametysicon-folder250',
width: 550,
items : [ this._form ],
closeAction: 'hide',
defaultFocus: 'title',
referenceHolder: true,
defaultButton: 'validate',
buttons : [{
reference: 'validate',
text :"{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_OK}}",
handler : this._ok,
scope: this
}, {
text :"{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_CANCEL}}",
handler: this._cancel,
scope: this
}]
});
this._initialized = true;
return;
},
/**
* Provide the items configuration of the form panel
* @return {Object/Object[]} the items configuration of the form panel
*/
_getFormItemsCfg: function()
{
var inscriptionStatusStore = Ext.create('Ext.data.Store', {
fields: ['value', 'text'],
data : [
{ value: 'open', text: "{{i18n PLUGINS_WORKSPACES_PROJECT_INSCRIPTION_STATUS_ENUM_OPEN}}" },
{ value: 'moderated', text: "{{i18n PLUGINS_WORKSPACES_PROJECT_INSCRIPTION_STATUS_ENUM_MODERATED}}" },
{ value: 'private', text: "{{i18n PLUGINS_WORKSPACES_PROJECT_INSCRIPTION_STATUS_ENUM_PRIVATE}}" }
]
});
return [{
xtype: 'component',
cls: 'a-text',
itemId: 'create-help-text',
html: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_HELP_TEXT}}"
}, {
xtype: 'component',
cls: 'a-text',
itemId: 'edition-help-text-title',
html: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_HELP_TEXT_TITLE}}"
}, {
xtype: 'textfield',
fieldLabel: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_TITLE}} *",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_TITLE_DESC}}",
itemId: 'title',
name: 'title',
allowBlank: false,
regex: /^[0-9-_]*[a-z].*$/i,
regexText: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_TITLE_REGEX_ERROR}}",
listeners: {
change: {
fn: this._updateNameOnTitleChange,
scope: this
}
}
}, {
xtype: 'component',
cls: 'a-text',
itemId: 'create-help-text-name',
html: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_HELP_TEXT_NAME}}"
}, {
xtype: 'textfield',
fieldLabel: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_FORM_NAME}} *",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_FORM_NAME_DESC}}",
name: 'name',
allowBlank: false,
regex: /^[a-z][a-z0-9\.\-_]*$/,
regexText: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_DIALOG_FORM_NAME_REGEX_ERROR}}"
}, {
xtype: 'textarea',
fieldLabel :"{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_DESCRIPTION}}",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_DESCRIPTION_DESC}}",
name :'description',
height: 100
}, {
xtype: 'textfield',
fieldLabel: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_MAILING_LIST}}",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_EDITION_DIALOG_FORM_MAILING_LIST_DESC}}",
name: 'mailing-list',
regex: /^((\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z0-9][A-Za-z0-9]+))|([^<]+<(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z0-9][A-Za-z0-9]+)>)$/,
regexText: "{{i18n plugin.core:PLUGINS_CORE_REGEXP_INVALID_MAIL}}"
}, {
xtype: 'combobox',
store: inscriptionStatusStore,
queryMode: 'local',
displayField: 'text',
valueField: 'value',
fieldLabel: "{{i18n PLUGINS_WORKSPACES_PROJECT_INSCRIPTION_STATUS_FORM_NAME}} *",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_INSCRIPTION_STATUS_FORM_NAME_DESC}}",
name: 'inscriptionStatus',
allowBlank: false,
listeners: {
'change': this._onChangeInscriptionStatus,
scope: this
}
}, {
xtype: 'combobox',
itemId: 'defaultProfile',
fieldLabel: "{{i18n PLUGINS_WORKSPACES_PROJECT_DEFAULT_PROFILE_FORM_NAME}} *",
ametysDescription: "{{i18n PLUGINS_WORKSPACES_PROJECT_DEFAULT_PROFILE_FORM_NAME_DESC}}",
name: 'defaultProfile',
valueField: 'id',
displayField: 'label',
store: {
autoDestroy: true,
proxy: {
type: 'ametys',
role: 'org.ametys.plugins.workspaces.project.ProjectManager',
methodName: 'getProjectProfiles',
methodArguments: [],
reader: {
type: 'json',
rootProperty: 'profiles'
}
},
sorters: [{property: 'label', direction: 'ASC'}],
fields: [
{name: 'id'},
{name: 'label', type: 'string'}
]
},
style: {
marginBottom: '10px'
}
}, {
xtype: 'component',
cls: 'a-text',
itemId: 'edit-site-help-text',
padding: '5 10',
tpl: new Ext.XTemplate(
'<a href="#">',
'<tpl if="title">',
"{{i18n PLUGINS_WORKSPACES_PROJECT_EDIT_DIALOG_SITE_CONFIG_HELP_TEXT}}",
'</tpl>',
'</a>'
),
listeners:
{
// add click listener on the link to open the site config.
afterrender: {
scope: this,
fn: function(component) {
component.addListener({
click: {
fn: this._openSiteConfig,
scope: this,
element: 'el',
delegate: 'a'
}
});
}
}
}
}]
},
/**
* @private
* Initialize the box form
*/
_initForm: function ()
{
var form = this._form.getForm();
if (this._mode == 'new')
{
form.findField('name').setValue();
form.findField('name').enable();
form.findField('title').setValue();
form.findField('description').setValue();
form.findField('mailing-list').setValue();
form.findField('inscriptionStatus').setValue();
form.findField('defaultProfile').setValue();
form.findField('defaultProfile').setDisabled(true);
this._form.down('#edit-site-help-text').setVisible(false);
}
else
{
form.findField('name').setValue(this._project.getName());
form.findField('name').disable();
form.findField('title').setValue(Ext.String.htmlDecode(this._project.getTitle())); // decode previously encoded HTML chars.
form.findField('description').setValue(this._project.getDescription());
form.findField('mailing-list').setValue(this._project.getMailingList());
var inscriptionStatus = this._project.getInscriptionStatus();
form.findField('inscriptionStatus').setValue(inscriptionStatus);
form.findField('defaultProfile').setValue(inscriptionStatus !== "open" ? null : this._project.getDefaultProfile());
form.findField('defaultProfile').setDisabled(inscriptionStatus !== "open");
var site = this._project.getSite();
if (site != null)
{
this._form.down('#edit-site-help-text').update(site);
this._form.down('#edit-site-help-text').setVisible(true);
}
else
{
this._form.down('#edit-site-help-text').setVisible(false);
}
}
this._form.down('#create-help-text').setVisible(this._mode == 'new');
this._form.down('#create-help-text-name').setVisible(this._mode == 'new');
form.clearInvalid();
},
/**
* @private
* Compute project's name from title input
* @param {Ext.form.field.Field} titleField The title field of which the value just changed
*/
_updateNameOnTitleChange: function(titleField)
{
if (this._mode == 'new')
{
this._updateNameTask = this._updateNameTask || new Ext.util.DelayedTask(function() {
var form = this._form.getForm();
var nameField = form.findField('name');
var nameValue = this._titleToName(titleField.getValue());
nameField.setValue(nameValue);
}, this);
this._updateNameTask.delay(500); // 500ms delay
}
},
/**
* @private
* Transforms a title into a valid name
* @param {String} value The title to transform
* @return {String} The computed name value
*/
_titleToName: function(value)
{
// toLowerCase -> trim -> deemphasize -> all non valid characters become '-'
// -> multiple '-' replaced by one -> do not start with '-' -> do not end with '-'
return Ext.String.deemphasize(value.toLowerCase().trim())
.replace(/\W/g, '-')
.replace(/-+/g, '-')
.replace(/^-/, '')
.replace(/-$/, '');
},
/**
* @private
* Open the site config tool of the project workspace
*/
_openSiteConfig: function()
{
// Open site configuration tool to allow the user to configure the site
// of the project workspace
var site = this._project.getSite();
Ametys.tool.ToolsManager.openTool('uitool-admin-site-config', {id: site.name, siteId: site.id, siteName: site.name, siteTitle: site.title});
},
/**
* Triggered when the value of the combo is changed via the setValue method.
* @param {Ext.form.field.ComboBox} combo The combo box.
* @param {Object} newValue The new value.
* @param {Object} oldValue The old value.
* @param {Object} eOpts The event options.
* @private
*/
_onChangeInscriptionStatus: function(combo, newValue, oldValue, eOpts)
{
var defaultProfileField = this._form.getForm().findField('defaultProfile');
var isOpen = newValue === "open";
defaultProfileField.setDisabled(!isOpen);
defaultProfileField.allowBlank = !isOpen;
if (!isOpen)
{
defaultProfileField.setValue();
}
},
/**
* The cancel button handler
* @private
*/
_cancel: function()
{
this._box.close();
},
/**
* Callback on the ok button.
* @private
*/
_ok: function()
{
var form = this._form.getForm();
if (!form.isValid())
{
return;
}
var values = form.getValues(),
title = Ext.String.htmlEncode(values.title); // encode HTML chars, title do not accept html
var params = [
title,
values.description || null,
values['mailing-list'] || null,
values.inscriptionStatus,
values.defaultProfile || null
];
if (this._mode == 'edit')
{
// insert id as first parameter
params.unshift(this._project.getId());
Ametys.data.ServerComm.callMethod({
role: 'org.ametys.plugins.workspaces.project.ProjectManager',
methodName: 'editProject',
parameters: params,
callback: {
scope: this,
handler: this._editProjectCb
},
waitMessage: true,
errorMessage: true
});
}
else
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_CONFIRM}}",
message: Ext.String.format("{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_CONFIRM_MESSAGE}}", title),
width: 500,
buttons: Ext.Msg.OKCANCEL,
icon: Ext.Msg.INFO,
fn: this._okConfirmCreateCb,
scope: this,
fnArgs: {
params: params
}
});
// insert name
params.unshift(values.name);
}
},
/**
* Callback called after the user confirmed the project creation.
*/
_okConfirmCreateCb: function(btn, text, opt)
{
if (btn == 'ok')
{
var params = opt.fnArgs.params;
Ametys.data.ServerComm.callMethod({
role: 'org.ametys.plugins.workspaces.project.ProjectManager',
methodName: 'createProject',
parameters: params,
callback: {
scope: this,
handler: this._createProjectCb
},
waitMessage: true,
errorMessage: true
});
}
},
/**
* Callback function called after the edition
* @param {Object} response The response object.
* @private
*/
_editProjectCb: function(response)
{
// success callback
this._successCb(this._project.getId());
},
/**
* Callback function called after the creation
* @param {Object} response The response object.
* @private
*/
_createProjectCb: function(response)
{
if (response.error == 'project-exists')
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_ERROR}}",
message: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_ERROR_PROJECT_EXISTS}}",
buttons: Ext.Msg.OK,
icon: Ext.Msg.ERROR
});
}
else if (response.error == 'name-exists')
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_ERROR}}",
message: "{{i18n PLUGINS_WORKSPACES_PROJECT_CREATE_ERROR_NAME_EXISTS}}",
buttons: Ext.Msg.OK,
icon: Ext.Msg.ERROR
});
}
else
{
// success callback
this._successCb(response.id);
}
},
/**
* Success callback.
* Closes the dialog box and calls user callback.
* @param {String} id identifier of the project (which has been edited or created)
* @private
*/
_successCb: function(id)
{
this._box.close();
if (Ext.isFunction(this._cbFn))
{
this._cbFn.call(this._cbScope, id);
}
}
});