/*
 *  Copyright 2013 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.
 */

/**
 * This helper provides a dialog box to create a content.
 */
Ext.define('Ametys.cms.uihelper.CreateContent', {
    singleton: true,
    
    /**
     * @property {String} [_parentContentType] The id of parent content type. Use for dialog box title. Can be null.
     * @private
     */
    /**
     * @property {Ametys.cms.content.ContentType/Ametys.cms.content.ContentType[]} _contentTypes The list of allowed content type objects. Can not be null.
     * @private
     */
    /**
     * @property {Ametys.window.DialogBox} _box The dialog box
     * @private
     */
    /**
     * @property {Ext.data.Store} _store The content types store
     * @private
     */
    /**
     * @property {Object} _creationCfg An object containing informations to create the content.
     * @private
     */
    /**
     * @property {Function} _cbFn The call back function to call after the creation of the content
     * @private
     */
    /**
     * @property {Boolean} _fullContents True to returns {@link Ametys.cms.content.Content}. If false only the content identifiers will be returned
     * @private
     */
    /**
     * @property {Function} _cbScope The scope for the #_cbFn callback function.
     * @private
     */
    /**
     * @property {Function} _closeCbFn The optional close callback function
     */
    /**
     * @property {Boolean} _hideTitle Set to true to hide the title
     */
    
    /**
     * Open a dialog box to create a new content
     * @param {Object} config The configuration options. Has the following parameters
     * @param {Ametys.cms.content.ContentType/Ametys.cms.content.ContentType[]} config.contentTypes (required) The content type to create or an array of content types to be proposed is a Combo box.
     * @param {String} config.contentLanguage The language of the content to create or null to select language among the supported languages
     * @param {String} [config.parentContentType] The id of parent content type, to use for dialog box default title.
     * @param {Number} [config.initWorkflowActionId=1] The id of the workflow initialization action
     * @param {Number} [config.initAndEditWorkflowActionId=11] The id of the workflow initialization action if edition is required
     * @param {String} [config.viewName=creation] The name of view to used for creation.
     * @param {String} [config.workflowName] The content workflow name. If empty the content type default workflow name will be used.
     * @param {Object} [config.additionalWorkflowParameters] Additional workflow parameters to be added to the server request. All parameters will be prefixed by "workflow-".
     * @param {String} [config.title] The title of the dialog box. If not provided, a default title will be displayed.
     * @param {String} [config.icon] The full path to icon (16x16 pixels) for the dialog box. If not provided, the icon of the first content types in the #config.contentTypes will be displayed.
     * @param {String} [config.helpmessage1] The message displayed at the top of the dialog box. If not provided, a default message will be displayed.
     * @param {String} [config.helpmessage2] The message displayed at the bottom the dialog box. If not provided, a default message will be displayed.
     * @param {String} [config.defaultContentTitle] The default content title to initialize the content title field. If not provided, a default title will be displayed.
     * @param {Boolean} [config.fullContents=false] If `true`, the callback function receives a Content object as argument. If `false`, only the content id is provided.
     * @param {String} [config.contentMessageTargetType=Ametys.message.MessageTarget#CONTENT] The message target factory role to use for the event of the bus thrown after content creation
     * @param {String} [config.rootContentPath] When creating a content under a specific root contents.
     * @param {Object} [config.initValues] A object with the initial values in creation form. These values only concerned the field of a creation view if exists.
     * @param {Object} [config.forceValues] A object with the values to force in creation form. These values only concerned the field of a creation view if exists.
     * @param {String} [config.forceMode=hide] If forceValues is used, a String which specifies how the forced field should appear : 'hide' to hide the fields, 'readOnly' to display the fields preveting the user from changing the fields or 'disabled' to disable the fields
     * @param {Function} [config.validateCallback] If provided, this function will be called when the user clicks on 'OK' instead of the default function, which creates the content.
     * @param {String} config.validateCallback.title The content title entered by the contributor.
     * @param {String} config.validateCallback.contentType The content type selected by the contributor.
     * @param {String} config.validateCallback.lang The lang of the content.
     * @param {Object} config.validateCallback.formValues the fiedls'form values. Can be empty.
     * @param {Function} config.closeCbFn The callback at the close moment
     * @param {Ametys.window.DialogBox} config.validateCallback.dialogbox The create content dialog box.
     * @param {Object} [config.validateCallbackScope] The scope of the validate callback.
     * @param {Function} callback The callback function invoked when the content has been created. The callback function will received the following parameters:
     * @param {String/Ametys.cms.content.Content} callback.content The created content as an id or a {@link Ametys.cms.content.Content}. See #config.fullContents
     * @param {Object} [scope=window] The callback scope, default to window.
     */
    open: function (config, callback, scope)
    {
        this._parentContentType = config.parentContentType;
        this._contentMessageTargetType = config.contentMessageTargetType || Ametys.message.MessageTarget.CONTENT;
        
        this._contentTypes = Ext.Array.from(config.contentTypes);
        
        // Content types are mandatory
        if (this._contentTypes.length == 0)
        {
            Ametys.log.ErrorDialog.display({
                title: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_CONFIGURATION_ERROR}}",
                text: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_CONTENTTYPES_ERROR_TEXT}}",
                category: Ext.getClassName(this)
            });
            return;
        }
        
        this._contentLanguage = config.contentLanguage;
        
        this._rootContentPath = config.rootContentPath;
        
        this._initWorkflowActionId = config.initWorkflowActionId || 1;
        this._initAndEditWorkflowActionId = config.initAndEditWorkflowActionId || 11;
        this._editWorkflowActionId = config.editWorkflowActionId || 2;
        this._viewName = config.viewName || 'creation';
        this._workflowName = config.workflowName;
        
        this._additionalWorkflowParameters = config.additionalWorkflowParameters || {};
        
        this._validateCb = config.validateCallback;
        this._validateCbScope = config.validateCallbackScope;
        this._cbFn = callback || Ext.emptyFn;
        this._cbScope = scope || window;
        this._closeCbFn = config.closeCbFn || Ext.emptyFn;
        
        this._fullContents = config.fullContents === true;
        
        this._defaultContentTitle = config.defaultContentTitle;
        this._hideTitle = config.hideTitle || false;
        
        config.icon = config.icon || "/plugins/cms/resources/img/content/content_16.png";
        
        if (!config.title)
        {
            if (this._parentContentType)
            {
                // Get dialog box's title and icon from parent content type
                var contentType = Ametys.cms.content.ContentTypeDAO.getContentType(this._parentContentType);
                
                config.title = "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_TITLE}}" + " " + contentType.getLabel() + "...";
                config.icon = contentType.getIconSmall();
            }
            else
            {
                config.title = "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_TITLE_UNKNOWN}}";
            }
        }
        
        this._initializeAndShowDialogBox(config);
    },
    
    /**
     * @private
     * Initialize and show dialog box
     * @param {Object} config The configuration
     */
    _initializeAndShowDialogBox : function (config)
    {
        var cTypeIconGlyph = this._contentTypes[0].getIconGlyph(); 
        if (cTypeIconGlyph && this._contentTypes[0].getIconDecorator())
        {
            cTypeIconGlyph = " " + this._contentTypes[0].getIconDecorator();
        }
        
        var iconCls = config.iconCls || cTypeIconGlyph;
        
        this._delayedInitialize (
            config.title,
            iconCls ? null : Ametys.CONTEXT_PATH + config.icon,
            iconCls,
            config.helpmessage1 || (this._contentTypes.length == 1 ? (this._contentLanguage ? "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_MESSAGE1}}" : "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_MULTI_LANGUAGES_MESSAGE1}}") : (this._contentLanguage ? "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_MULTI_CTYPES_MESSAGE1}}" : "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_MULTI_CTYPES_LANGUAGES_MESSAGE1}}")),
            config.helpmessage2 || "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_MESSAGE2}}"
        );

        this._form.destroyComponents();
        this._titleForm.destroyComponents();
        
        if (config.initValues || config.forceValues)
        {
            this._form.executeFormReady(Ext.bind(this._initValues, this, [config.initValues, config.forceValues, config.forceMode || 'hide']));
        }
        
        this._init();
    },
    
    /**
     * @private
     * Initialize the form creation with the initial values and forced values
     * @param {Object} initValues The initial value
     * @param {Object} forceValues The values to force
     * @param {String} forceMode The force mode : 'hide', 'readOnly' or 'disabled'
     */
    _initValues: function (initValues, forceValues, forceMode)
    {
        if (initValues)
        {
            var prefix = this._form.getFieldNamePrefix();
            for (key in initValues) 
            {
                var fd = this._form.getField(prefix + key);
                if (fd)
                {
                    fd.setValue(initValues[key]);
                }
            }
        }
        
        if (forceValues)
        {
            this._initForceValues(forceValues, forceMode);
        }
    },
    
    /**
     * @private
     * Initialize the form creation with the forced values
     * @param {Object} values The values to force
     * @param {String} forceMode The force mode : 'hide', 'readOnly' or 'disabled'
     */
    _initForceValues: function (values, forceMode)
    {
        var prefix = this._form.getFieldNamePrefix();
        for (key in values) 
        {
            var fd = this._form.getField(prefix + key);
            if (fd)
            {
                fd.setValue(values[key]);
                
                if (forceMode == 'readOnly')
                {
                    fd.setReadOnly(true)
                }
                else if (forceMode == 'disabled')
                {
                    fd.setDisabled(true)
                }
                else
                {
                    fd.setVisible(false);
                }
            }
        }
    },
    
    /**
     * Initialize the dialog box fields
     * @private
     */
    _init: function ()
    {
        var langFd = this._box.down('#content-languages');
        if (this._contentLanguage != null)
        {
            langFd.setValue(this._contentLanguage);
            langFd.hide();
        }
        else
        {
            langFd.show();
        }
        
        this._store.removeAll();
        this._loadContentTypes();
        
        var cTypeFd = this._box.down('#content-types');
        
        var firstRecord = this._store.first();
        cTypeFd.setValue(firstRecord);
        this._onSelectContentTypes (cTypeFd, firstRecord != null ? [firstRecord] : []);
        
        if (this._contentTypes.length == 1)
        {
            cTypeFd.hide();
        }
        else
        {
            cTypeFd.show();
        }
        
        this._box.show();
    },

    /**
     * Creates or update the dialog box
     * @param {String} title The title of the dialog box.
     * @param {String} icon The full path to icon (16x16) for the dialog box
     * @param {String} iconCls The css classname to display a glyph
     * @param {String} helpMsg1 The message displayed at the top of the dialog box.
     * @param {String} helpMsg2 The message displayed at the bottom of the dialog box.
     * @private
     */
    _delayedInitialize: function (title, icon, iconCls, helpMsg1, helpMsg2)
    {
        if (!this._box)
        {
            // Title form creation
            this._titleForm = this._createConfigurableFormPanel();
        
            // Form creation (can be empty)
            this._form = this._createConfigurableFormPanel();
        
            this._box = Ext.create('Ametys.window.DialogBox', {
                title: title,
                icon: iconCls ? null : icon,
                iconCls: iconCls,
                
                width: 500,
                maxHeight: window.innerHeight*0.8,
                scrollable: true,
        
                layout: 'anchor',
                defaultType: 'textfield',
                defaults: {
                    cls: 'ametys',
                    labelAlign: 'top',
                    labelSeparator: '',
                    anchor: '100%',
                    style: 'margin-right:' + (Ametys.form.ConfigurableFormPanel.OFFSET_FIELDSET + 10 + Ametys.form.ConfigurableFormPanel.PADDING_TAB) + 'px'
                },
                
                items: [{
                            xtype: 'component',
                            itemId: 'helpmessage1',
                            cls: 'a-text',
                            html: helpMsg1
                        }, {
                            xtype: 'combobox',
                            name: 'contentTypes',
                            itemId: 'content-types',
                            fieldLabel: "* " + "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_TYPESFIELD_LABEL}}",
                            ametysDescription: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_TYPESFIELD_DESC}}",
                            store: this._getContentTypesStore(),
                            queryMode: 'local',
                            displayField: 'label',
                            valueField: 'value',
                            typeAhead: true,
                            editable: true,
                            forceSelection: true,
                            triggerAction: 'all',
                            lastQuery: '',
                            allowBlank: false,
                            listeners: {
                                select: {fn: this._onSelectContentTypes, scope: this}
                            }
                        },
                        this._titleForm, 
                        Ametys.cms.language.LanguageDAO.createComboBox({
                            name: 'contentLanguage',
                            itemId: 'content-languages',
                            fieldLabel: "* " + "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_LANGUAGE_LABEL}}",
                            ametysDescription: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_LANGUAGE_DESC}}",
                            anchor: '100%',
                            style: 'margin-right:' + (Ametys.form.ConfigurableFormPanel.OFFSET_FIELDSET + 10 + 5) + 'px'
                        }),
                        this._form,
                        {
                            xtype: 'component',
                            itemId: 'helpmessage2',
                            cls: 'a-text',
                            html: helpMsg2
                        }
                ],
            
                selectDefaultFocus: true,
                closeAction: 'hide',
                
                referenceHolder: true,
                defaultButton: 'validate',
                
                buttons : [{
                    reference: 'validate',
                    text :"{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_OK}}",
                    handler: this._validate,
                    scope: this
                }, {
                    text :"{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_CANCEL}}",
                    handler: Ext.bind(function() {this._box.close();}, this) 
                }],
            
                listeners: {
                    close: Ext.bind(this._closeCbFn, this)
                }
            });
        }
        else
        {
            this._box.setTitle(title);
            this._box.setIcon(iconCls ? null : icon);
            this._box.setIconCls(iconCls);
            this._box.down('#helpmessage1').update(helpMsg1);
            this._box.down('#helpmessage2').update(helpMsg2);
        }
    },
    
    /**
     * Create a configurable form panel.
     * @return {Object} The configurable form panel
     * @private
     */
    _createConfigurableFormPanel: function()
    {
        return Ext.create ('Ametys.form.ConfigurableFormPanel', {
            border: false,
            scrollable: false,
            
            defaultFieldConfig: {
                anchor: '100%'
            },
            
            bodyStyle: {
                padding: 0
            },
            
            additionalWidgetsConfFromParams: {
                contentType: 'contentType' // some widgets requires the contentType configuration  
            },
            
            fieldNamePrefix: 'content.input.',
            displayGroupsDescriptions: false
        });
    },
    
    /**
     * Create or retrieves the store containing the content types data.
     * @return {Object[]} The items
     * @private
     */
    _getContentTypesStore: function()
    {
        if (!this._store)
        {
            this._store = Ext.create('Ext.data.Store', {
                fields: [
                    'value',
                    {name: 'label', type: 'string'}
                ],
                autoLoad: false,
                sorters: {property: 'label'}
            });
        }
        
        return this._store;
    },
    
    /**
     * @private
     * Listener on the change event of the content types combobox
     * @param {Ext.form.field.ComboBox} combobox the combobox field
     * @param {Array} records The selected records
     */
    _onSelectContentTypes: function(combobox, records)
    {
        this._form.destroyComponents();
        this._titleForm.destroyComponents();
        
        var contentTypeId = combobox.getValue();
        if (!Ext.isEmpty(contentTypeId))
        {
            Ametys.data.ServerComm.callMethod({
                role: 'org.ametys.cms.contenttype.ContentTypesHelper',
                methodName: 'getTitleViewAsJSON',
                parameters: [contentTypeId, true],
                callback: {
                    scope: this,
                    handler: Ext.bind(this._drawTitleForm, this, [contentTypeId], 2)
                },
                waitMessage: true, // The tool is refreshing
                errorMessage: {
                    msg: "{{i18n plugin.cms:PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_FORMDEFINITION_ERROR}}",
                    category: Ext.getClassName(this)
                }
            });
            
            Ametys.data.ServerComm.callMethod({
	            role: 'org.ametys.cms.contenttype.ContentTypesHelper',
	            methodName: 'getViewAsJSON',
	            parameters: [contentTypeId, this._viewName, true],
	            callback: {
	                scope: this,
	                handler: this._drawCreationForm
	            },
	            waitMessage: true, // The tool is refreshing
	            errorMessage: {
	                msg: "{{i18n plugin.cms:PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_FORMDEFINITION_ERROR}}",
	                category: Ext.getClassName(this)
	            }
	        });
        }
    },
    
    /**
     * Callback function called after retrieving title information from the content type.
     * This draws the title form for content edition
     * @param {Object} response The XML response provided by the {@link Ametys.data.ServerComm}
     * @param {Object} args The callback parameters passed to the {@link Ametys.data.ServerComm#send} method
     * @param {String} contentTypeId The content type id
     * @private
     */
    _drawTitleForm: function (response, args, contentTypeId)
    {
        if (response.view)
        {
            this._titleForm.configure(response.view.elements);
            this._titleForm.setValues(); // setValues must always be called for configurable form panel in order to complete its initialization
            
            // Set the default title
            var defaultTitle = this._defaultContentTitle || response.contentType.defaultTitle || "{{i18n plugin.cms:PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_DEFAULT_NAME}}";
            var titleField = this._titleForm.getField("content.input.title");
            var languageField = this._box.down('#content-languages');
            if (response.view.elements.title.type != "multilingual-string")
            {
                titleField.setValue(defaultTitle);
                if (!this._contentLanguage)
                {
                    languageField.show();
                }
            }
            else
            {
                // It's a multilingual title, so hide the language comboBox and transform default title to object
                var lang = languageField.getValue();
                
                var multiLangTitle = {};
                multiLangTitle[lang] = defaultTitle;
                titleField.setValue(multiLangTitle);
                
                languageField.hide();
            }
            
            titleField.setVisible(!this._hideTitle);
            
            titleField.focus(true, 100);
        }
    },
    
    /**
     * Callback function called after retrieving metadata-set from the content type.
     * This draws the form for content edition
     * @param {Object} response The XML response provided by the {@link Ametys.data.ServerComm}
     * @param {Object} args The callback parameters passed to the {@link Ametys.data.ServerComm#send} method
     * @private
     */
    _drawCreationForm: function (response, args)
    {
        this._hasTitleInCreationView = false;
        if (response.view)
        {
            var elements = response.view.elements;
            var formJson = {};
            for (let key in elements)
            {
                if (key != 'title')
                {
                    formJson[key] = elements[key];
                }
                else
                {
                    this._hasTitleInCreationView = true;
                }
            }
        
            // Draw form
            this._disableAdditionalCreation(response.view.elements);
            this._form.configure(formJson);
            this._form.setValues(); // setValues must always be called for configurable form panel in order to complete its initialization
        }
    },
    
    /**
     * @private
     * Disable additional creation during this creation
     * @param {Object} viewElements The elements of the view for creation
     */
    _disableAdditionalCreation: function (viewElements)
    {
        for (var i in viewElements)
        {
            var element = viewElements[i];
            var type = element.type ? element.type : null;
            
            if (!type || type == 'composite' || type == 'repeater')
            {
                this._disableAdditionalCreation (element.elements);
            }
            else
            {
                element['widget-params'] = element['widget-params'] || {};
                element['widget-params'].creatingContent = true;
            }
        }
    },
    
    /**
     * Load the content types data into the store
     * @private
     */
    _loadContentTypes: function()
    {
        var data = [];
        
        // Populate data array with content type informations/
        Ext.Array.forEach(this._contentTypes, function(contentType) {
            data.push({
                label: contentType.getLabel(),
                value: contentType.getId()
            });
        });
        
        this._store.loadData(data);
    },
    
    /**
     * This function is called when validating the dialog box.
     * @private
     */
    _validate: function()
    {
        var validateFn = this._validateCb ? this._validateCb : this._create;
        var validateScope = this._validateCb && this._validateCbScope ? this._validateCbScope : this;
        
        var titleField = this._titleForm.getField("content.input.title");
        var typesField = this._box.down('#content-types');
        var langsField = this._box.down('#content-languages');
         
        if (this._titleForm.getInvalidFields().length > 0 || !typesField.isValid() || !langsField.isValid() || this._form.getInvalidFields().length > 0)
        {
            return null;
        }
        
        // For the dialog box, title is displayed just once but can be used for edit content function, so add it
        var titleValue = titleField.getValue();
        var values = this._form.getJsonValues();
        if (this._hasTitleInCreationView)
        {
            values["content.input.title"] = titleValue;
        }
        
        validateFn.call(validateScope, titleValue, typesField.getValue(), langsField.getValue(), values, this._box);
    },
    
    /**
     * Creates the content
     * @param {String} title The content title entered by the contributor.
     * @param {String} contentType The content type selected by the contributor.
     * @param {String} contentLanguage The content language code
     * @param {Object} formValues the fiedls' form values. Can be empty.
     * @private
     */
    _create: function (title, contentType, contentLanguage, formValues)
    {
        var params = {
            contentType: contentType,
            contentTitle: title,
            contentLanguage: contentLanguage,
            rootContentPath: this._rootContentPath,
            initWorkflowActionId: this._initWorkflowActionId,
            initAndEditWorkflowActionId: this._initAndEditWorkflowActionId,
            editWorkflowActionId: this._editWorkflowActionId,
            workflowName: this._workflowName,
            additionalWorkflowParameters: this._additionalWorkflowParameters,
            viewName: this._viewName,
            editFormValues: formValues
        };
        
        // Create the content, and manage callback.
        // Hide the box at the end.
        
        Ametys.cms.content.ContentDAO.createContent(
            params,
            this._createCb,
            this, // scope
            this._fullContents,
            this._contentMessageTargetType, 
            null, 
            this._box
        );
    },
    
    /**
     * Callback function called after creation process
     * @param {Ametys.cms.content.Content} content The created content. Can be null if the creation failed
     * @param {Object} fieldsInError Fields of the creation form which contain errors
     * @param {Object} workflowErrors The workflow errors
     * @private
     */
    _createCb: function (content, fieldsInError, workflowErrors)
    {
        if (content != null)
        {
            // Success
            this._box.hide();
            this._cbFn.call(this._cbScope, content);
        }
        else
        {
            var invalidFormFields = {};
            
            if (workflowErrors && Ext.isArray(workflowErrors) && workflowErrors.length > 0)
            {
                var detailedMsg = '<ul>';
                for (var i=0; i < workflowErrors.length; i++)
                {
                    detailedMsg += '<li>' + workflowErrors[i] + '</li>';
                }
                detailedMsg += '</ul>';
                
                Ametys.Msg.show({
                    title: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_ERROR_TITLE}}",
                    msg: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_ERROR_WORKFLOW}}" + detailedMsg,
                    buttons: Ext.Msg.OK,
                    icon: Ext.Msg.ERROR
                });
                return;
            }
            
            var detailedMsg = '<ul>';
            if (fieldsInError && Ext.isObject(fieldsInError))
            {
                for (var fdName in fieldsInError)
                {
                    if (fdName == '_global')
                    {
                        detailedMsg += '<li>' + fieldsInError[fdName] + '</li>';
                    }
                    else
                    {
                        var fd = this._form.getForm().findField('content.input.' + fdName);
                        detailedMsg += '<li><b>' + (fd != null ? fd.getFieldLabel() : fdName) + '</b>: ' + fieldsInError[fdName] + '</li>';
                        invalidFormFields['content.input.' + fdName] = fieldsInError[fdName];
                    }
                }
            }
            detailedMsg += '</ul>';
            
            this._form.markFieldsInvalid (invalidFormFields);
            
            Ametys.Msg.show({
                title: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_ERROR_TITLE}}",
                msg: "{{i18n PLUGINS_CMS_CONTENT_UIHELPER_CREATECONTENT_ERROR_FORM}}" + detailedMsg,
                buttons: Ext.Msg.OK,
                icon: Ext.Msg.ERROR
            });
        }
    }
});