/*
 *  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 class provides a widget to select one or more contents.<br>
 * This embeds a drop down list with querying on title of contents and type-ahead support.<br>
 * Advanced search through a dialog box could be enable. See #cfg-allowSearch.<br>
 * Allow content creation using #cfg-allowCreation.<br>
 * 
 * This widget is the default widget registered for fields of type Ametys.form.WidgetManager#TYPE_CONTENT.<br>
 */
Ext.define('Ametys.cms.form.widget.SelectContent', {
    
    extend: 'Ametys.form.AbstractQueryableComboBox',
    
    /**
     * @cfg {String} [contentType] The content type to allow search on. If not null, the search will be restricted the search to this content type (and its sub-types).
     */
    contentType: null, 
    /**
     * @cfg {Number} [initWorkflowActionId=1] The id of the initialize workflow action to create the content. Only used if the create content button is displayed.
     */
    initWorkflowActionId: 1,
    /**
     * @cfg {Number} [initAndEditWorkflowActionId=11] The id of the initialize workflow action to create the content followed by an edition. Only used if the create content button is displayed.
     */
    initAndEditWorkflowActionId: 11,
    /**
     * @cfg {Number} [editWorkflowActionId=2] The id of the workflow action to edit the content. Only used if the create content button is displayed.
     */
    editWorkflowActionId: 2,
    /**
     * @cfg {String} [workflowName=content] The id of the workflow initialization function. Only used if the create content button is displayed.
     */
    workflowName: 'content',
    /**
     * @cfg {Boolean} [limitToContextLanguage=true] True to limit the search to the context language. Default value to true.
     */
    limitToContextLanguage: true,
    
    
    /**
     * @property {Ametys.cms.content.ContentType/Ametys.cms.content.ContentType[]} _contentTypes Array of content type displayed in the drop down list of the create content dialog box.
     * Only used if the create content button is displayed.
     * @protected
     */
    /**
     * @property {Boolean} _noValueOptionId The id of the 'No value' options.
     * @private
     * @readonly
     */
    _noValueOptionId: '__ametys_none',
    /**
     * @cfg {String} [boxTitle] The title of the dialog box.
     */
    boxTitle: "{{i18n PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_TITLE}}", 
    /**
     * @cfg {String} [boxIcon=null] The path of the icon of the dialog box.
     */
    boxIcon: null,
    
    /**
     * @cfg {String} [boxMinHeight=300] The minimum height of the dialog box.
     */
    boxMinHeight: 300,
    /**
     * @cfg {String} [boxMaxHeight=300] The maximum height of the dialog box.
     */
    boxMaxHeight: 600,
    
    /**
     * @cfg {String} [boxIconCls] The icon cls
     */
    boxIconCls: 'ametysicon-document209 decorator-ametysicon-magnifier12',
    
    /**
     * @cfg {String} [boxForceMode] The force mode for the dialog box.
     * @see Ametys.cms.uihelper.SelectContentBySearch.open
     */
    boxForceMode: 'disabled',
    
    /**
     * @cfg {String} [helpMsg1] The message displayed at the top of the first card of search dialog box
     */
    helpMsg1: "{{i18n PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_SEARCH_HINT}}",
    /**
     * @cfg {String} [helpMsg2] The message displayed at the top of the second card of search dialog box
     */
    helpMsg2: "{{i18n PLUGINS_CMS_HELPER_SELECTCONTENTBYSEARCH_RESULT_HINT_MULTIPLE}}",
    
    /**
     * @cfg {String} [searchButtonText=""] The text of the search button.
     * Note that if you supply a value for {@link #searchButtonConfig}, the searchButtonCfg.text
     * value will be used instead if available.
     */
    searchButtonText: '',
    /**
     * @cfg {String} [searchButtonIconCls="ametysicon-magnifier12"] One or more space separated CSS classes to be applied to the icon of search button. Only used if {@link #searchButtonIcon} is null.
     * Note that if you supply a value for {@link #searchButtonConfig}, the searchButtonCfg.iconCls
     * value will be used instead if available.
     */
    searchButtonIconCls: 'ametysicon-magnifier12',
    /**
     * @cfg {String} [searchButtonIcon] The button icon path for the search button.
     * Note that if you supply a value for {@link #searchButtonConfig}, the searchButtonCfg.icon
     * value will be used instead if available.
     */
    searchButtonIcon: null,
    /**
     * @cfg {String} searchButtonTooltip The button icon tooltip for the search button.
     * Note that if you supply a value for {@link #searchButtonConfig}, the searchButtonCfg.tooltip
     * value will be used instead if available.
     */
    searchButtonTooltip: "{{i18n WIDGET_SELECTCONTENT_SEARCHBUTTON_TOOLTIP}}",
    /**
     * @cfg {Object} searchButtonConfig The configuration object for the button to search contents
     */
    
    /**
     * @cfg {String} [createButtonText=""] The text of the create content button.
     * Note that if you supply a value for {@link #createButtonConfig}, the createButtonCfg.text
     * value will be used instead if available.
     */
    createButtonText: '',
    /**
     * @cfg {String} [createButtonIconCls="ametysicon-add64"] One or more space separated CSS classes to be applied to the icon of create button. Only used if {@link #createButtonIcon} is null.
     * Note that if you supply a value for {@link #createButtonConfig}, the createButtonConfig.iconCls
     * value will be used instead if available.
     */
    createButtonIconCls: 'ametysicon-add64',
    /**
     * @cfg {String} [createButtonIcon] The button icon path for the create content button.
     * Note that if you supply a value for {@link #createButtonConfig}, the createButtonCfg.icon
     * value will be used instead if available.
     */
    createButtonIcon: null,
    /**
     * @cfg {String} createButtonTooltip The button icon tooltip for the create button.
     * Note that if you supply a value for {@link #createButtonConfig}, the createButtonCfg.tooltip
     * value will be used instead if available.
     */
    createButtonTooltip: "{{i18n WIDGET_SELECTCONTENT_CREATEBUTTON_TOOLTIP}}",
    
    /**
     * @cfg {String} [creationBoxTitle] The title of dialog box of content creation
     * Note that if you supply a value for {@link #createButtonConfig}, the createButtonCfg.title
     * value will be used instead if available.
     */
    createBoxTitle: null,
    /**
     * @cfg {Object} createButtonConfig The configuration object for the button to create a content
     */
       
    /**
     * @cfg {String} [editButtonText=""] The text of the edit content button.
     * Note that if you supply a value for {@link #editButtonConfig}, the editButtonCfg.text
     * value will be used instead if available.
     */
    editButtonText: '',
    /**
     * @cfg {String} [editButtonIconCls="ametysicon-add64"] One or more space separated CSS classes to be applied to the icon of create button. Only used if {@link #createButtonIcon} is null.
     * Note that if you supply a value for {@link #editButtonConfig}, the editButtonConfig.iconCls
     * value will be used instead if available.
     */
    editButtonIconCls: 'ametysicon-art-pencil',
    /**
     * @cfg {String} [editButtonIcon] The button icon path for the create content button.
     * Note that if you supply a value for {@link #editButtonConfig}, the editButtonCfg.icon
     * value will be used instead if available.
     */
    editButtonIcon: null,
    /**
     * @cfg {String} editButtonTooltip The button icon tooltip for the create button.
     * Note that if you supply a value for {@link #editButtonConfig}, the editButtonConfig.tooltip
     * value will be used instead if available.
     */
    editButtonTooltip: "{{i18n WIDGET_SELECTCONTENT_EDITBUTTON_TOOLTIP}}",
    /**
     * @cfg {String} [editBoxTitle] The title of dialog box of content creation
     * Note that if you supply a value for {@link #editButtonConfig}, the editButtonConfig.title
     * value will be used instead if available.
     */
    editBoxTitle: null,
    /**
     * @cfg {Object} editButtonConfig The configuration object for the button to create a content
     */
    
    /**
     * @cfg {String} [loadingText] The message displayed when loading the store.
     */
    loadingText: "{{i18n WIDGET_SELECTCONTENT_LOADINGTEXT}}",
    
    /**
     * @cfg {String} [emptyText] The default text to place into an empty field.
     */
    noResultText: "{{i18n WIDGET_SELECTCONTENT_EMPTYTEXT}}",
    
    /**
     * @cfg {String} [activateNoneValue=false] Set to `true` to activate the "none" value in the field.
     */
    activateNoneValue: false,
    
    /**
     * @cfg {String} [noneValueText] The label of the optional "none" value to add in the field. Defaults to "WIDGET_SELECTCONTENT_NONEVALUETEXT"
     */
    noneValueDefaultText: "{{i18n WIDGET_SELECTCONTENT_NONEVALUETEXT}}",
    
    /**
     * @cfg {Boolean} [displayTypeIcon=true] Set to `true` to display the content type icon in the selection boxes.
     */
    displayTypeIcon: true,
    
    /**
     * @cfg {Number} [minChars=3] The minimum number of characters the user must type before autocomplete activates.
     */
    minChars: 3,
    /**
     * @cfg {Boolean} [allowSearch=true] Set to `false` to disable advanced search.
     */
    allowSearch: true,
    
    /**
     * @cfg {Boolean} [allowCreation=false] Set to `true` allow content creation. Note that if {@link #creatingContent} is set to 'true', creation will be always disallowed.
     */
    allowCreation: false,
    
    /**
     * @cfg {Boolean} [allowEdition] Set to `true` to display a button to edit the currently selected linked contents. Set to `false` to hide this button. Set to empty/null (default value) to display the button only when the widget is used in grids. 
     */
    allowEdition: null,
    /**
     * @cfg {String} [editionView] The attributes view of the content type to use for edition
     */
    editionView: null,
    
    /**
     * cfg {Boolean} [checkCreationRights=true] Set to `false` to not check rights on content types for content creation
     */
    checkCreationRights: true,
    
    /**
     * @cfg {Boolean} [creatingContent=false] Set to `true` if a content is already being creating. 
     */
    creatingContent: false,
    
    /**
     * @cfg {Boolean} [openOnClick=true] Set to `false` to disable the opening of selected contents
     */
    openOnClick: true,
    
    /**
     * @cfg {Boolean} [excludeSubContents=true] Set to `false` to search on subcontents
     */
    excludeSubContents: true,
    
    /**
     * @cfg {Boolean} [includePrivate=false] Set to `true` allow creation of private content types. 
     */
    includePrivate: false,
    
    /**
     * @cfg {String} [modelId=search-ui.select-content] The id of model to use for search
     */
    modelId: 'search-ui.select-content',
    /**
     * @cfg {String} solrRequest The solr request to select some contents only
     */
    /**
    
    /**
     * @cfg {String} [growMax=300] If not set to `false`, the max height in pixels of the box select
     */
    growMax: 300,
    
    /**
     * @cfg {String} [boxSize=default] If set to `large`, the select box width is the window width with a 0.8 ratio, otherwise `default` mode is for a width of 600 pixels
     */
    boxSize: 'default',
    
    /**
     * @property {Ext.button.Button} searchButton The search contents button, if any.
     * @protected
     */
    /**
     * @property {Ext.button.Button} createButton The create content button, if any.
     * @protected
     */
    
    /**
     * @cfg {Boolean} [triggerOnClick=false] Set to `true` to activate the trigger when clicking in empty space in the field.
     */
    triggerOnClick: false,
    
    valueField: 'id',
    displayField: 'title',
    
    maxResult: 50,
    nbResults: 0,
    totalResults: 0,
    
    listConfig: {
       // Override the render template of boundlist to add a result bar when the search limit is reached
       renderTpl: [
            '<div id="{id}-results" class="{baseCls}-result-bbar">',
                "<span>{{i18n WIDGET_SELECTCONTENT_RESULT_BAR_HINT_1}}</span> <span>{{i18n WIDGET_SELECTCONTENT_RESULT_BAR_HINT_2}}</span>",
            '</div>',
            '<div id="{id}-results2" class="{baseCls}-result-bbar">',
                "<span>{{i18n WIDGET_SELECTCONTENT_RESULT_BAR_HINT2_1}}</span><span>{{i18n WIDGET_SELECTCONTENT_RESULT_BAR_HINT2_2}}</span>",
            '</div>',
            '<div id="{id}-listWrap" data-ref="listWrap"',
                    ' class="{baseCls}-list-ct ', Ext.dom.Element.unselectableCls, '">',
                '<ul id="{id}-listEl" data-ref="listEl" class="', Ext.baseCSSPrefix, 'list-plain"',
                    '<tpl foreach="ariaAttributes"> {$}="{.}"</tpl>',
                '>',
                '</ul>',
            '</div>',
            '{%',
                'var pagingToolbar=values.$comp.pagingToolbar;',
                'if (pagingToolbar) {',
                    'Ext.DomHelper.generateMarkup(pagingToolbar.getRenderTree(), out);',
                '}',
            '%}',
            {
                disableFormats: true
            }
        ]
    },
    
    statics: {
        /**
         * Open the content tool on the given content.
         * @param {String} contentId The ID of the content to open.
         */
        openContent: function(contentId)
        {
            // Defer the call to let the focus happen before opening the tool.
            Ext.defer(Ametys.tool.ToolsManager.openTool, 1, Ametys.tool.ToolsManager, ['uitool-content', {id: contentId}]);
        }
    },
    
    constructor: function (config)
    {
        if (config.minChars)
        {
            config.minChars = Ext.isString(config.minChars) ? parseInt(config.minChars) : config.minChars; 
        }
        if (config.maxResult)
        {
            config.maxResult = Ext.isString(config.maxResult) ? parseInt(config.maxResult) : config.maxResult;
        }
        
        config.limitToContextLanguage = config.limitToContextLanguage !== false && config.limitToContextLanguage != 'false';
        this.callParent(arguments);
        
        // Must be called after the parent as it will this.contentInfo set by the parent constructor
        this.updateAdditionalWidgetsConf(config);
    },
    
    initComponent: function()
    {
        this.cls = 'x-form-selectcontent-widget';
        this.callParent(arguments);
    },
    
    onFocusEnter: function(e)
    {
        this.callParent(arguments);
        
        if (this.triggerDialogBoxOpened)
        {
            Ext.Function.defer(e.fromComponent.focus, 1, e.fromComponent);
        }
    },
    
    /**
     * @private
     * Event when the combobox itemlist is clicked to open the content if required
     * @param {Event} evt The click event
     * @param {Ext.Element} el The item list
     * @param {Object} o Options. Emtpy.
     */
    _onComboboxItemListClick: function(evt, el, o)
    {
        // Handle clickable entry, in order to open the corresponding content.
        var itemEl = evt.getTarget('.x-tagfield-item'),
            spanEl = itemEl ? evt.getTarget('span.clickable') : false,
            record;
            
        if (spanEl)
        {
            record = this.combobox.getRecordByListItemNode(itemEl);
            if (record)
            {
                Ametys.cms.form.widget.SelectContent.openContent(record.getId());
            }
        }
    },
    
    initEvents: function()
    {
        this.callParent(arguments);
        
        // Click listener on the combobox to deals with clickable entries.
        this.combobox.mon(this.combobox.itemList, 'click', this._onComboboxItemListClick, this);
        this.combobox.addListener('change', this._onComboBoxChange, this);
    },
    
    getItems: function()
    {
        var items = this.callParent(arguments);
        
        if (!this.readOnly)
        {
            var subitems = [];
            
            // Button that opens the search dialog box.
            if (this.allowSearch == true || this.allowSearch == 'true')
            {
                var searchButtonConfig = this.searchButtonConfig || {};
                Ext.applyIf(searchButtonConfig, {
                    text: this.searchButtonText,
                    iconCls: this.searchButtonIcon ? null : this.searchButtonIconCls,
                    icon: this.searchButtonIcon,
                    tooltip: this.searchButtonTooltip,
                    handler: this.selectContentsBySearch,
                    scope: this
                });
                this.searchButton = Ext.create('Ext.button.Button', searchButtonConfig);
                subitems.push(this.searchButton);
            }
            
            // Button that opens the create content dialog box. 
            if (this.contentType && (this.allowCreation == true || this.allowCreation == 'true') && !this.creatingContent)
            {
                // Get the content type and its sub-content types allowed to be created by the user
                var checkRights = this.checkCreationRights !== false;
                var contentTypesCollection = Ametys.cms.content.ContentTypeDAO.getContentType(this.contentType).getDescendantsOrSelf().createFiltered(function (contentType) {
                    return (contentType.hasRight() || !checkRights)
                        && (!contentType.isPrivate() || !(this.includePrivate == true || this.includePrivate == 'true'))
                        && !contentType.isMixin()
                        && !contentType.isAbstract();
                });
                if (contentTypesCollection.count() > 0)
                {
                    var createButtonConfig = this.createButtonConfig || {};
                    Ext.applyIf(createButtonConfig, {
                        text: this.createButtonText,
                        iconCls: this.createButtonIcon ? null : this.createButtonIconCls,
                        icon: this.createButtonIcon,
                        tooltip: this.createButtonTooltip,
                        handler: this.openCreateContentBox,
                        scope: this,
                        hidden: false
                    });
                    this.createButton = Ext.create('Ext.button.Button', createButtonConfig);
                    subitems.push(this.createButton);
                    this._contentTypes = contentTypesCollection.items;
                }
            }
            
            // Button that allow to edit the contents in a dialogbox
            if (this.contentType)
            {
                // At this time automatic edition mode cannot be determine. Let's insert the button as hidden and it will be display in #onAdded
                var editButtonConfig = this.editButtonConfig || {};
                Ext.applyIf(editButtonConfig, {
                    text: this.editButtonText,
                    iconCls: this.editButtonIcon ? null : this.editButtonIconCls,
                    icon: this.editButtonIcon,
                    tooltip: this.editButtonTooltip,
                    handler: this._startEdit,
                    scope: this,
                    disabled :true,
                    hidden: true
                });
                this.editionButton = Ext.create('Ext.button.Button', editButtonConfig);
                subitems.push(this.editionButton)
            }
            
            if (subitems.length > 0)
            {
                items.push({
                    xtype: 'container',
                    itemId: 'toolbar',
                    items: subitems
                });
            }
        }
        
        return items;
    },
    
    onResize: function(w, h)
    {
        this.callParent(arguments);
        
        var toolbar = this.getComponent("toolbar");
        if (toolbar)
        {
            var nbVisibleItems = toolbar.items.filter("hidden", false).length;
            var nbItemsSize = (nbVisibleItems * 24 + 2);
    
            if (nbVisibleItems > 1)
            {
                var labelHeight = 0;
                if (this.labelAlign == "top" && this.labelEl)
                {
                    labelHeight = this.labelEl.getHeight();
                }
                
                if (this.bodyEl.getWidth() < 300)
                {
                    toolbar.setWidth(27);
                    if (h < nbItemsSize)
                    {
                        this.setHeight(labelHeight + nbItemsSize);
                    }
                }
                else if (h >= nbItemsSize + labelHeight)
                {
                    toolbar.setWidth(27);
                }
                else
                {
                    toolbar.setWidth("auto");
                }
            }
        }
    },
    
    onAdded: function(container, pos, instanced)
    {
        this.callParent(arguments);
        
        if (this.editionButton)
        {
            if (this.allowEdition === true || this.allowEdition === "true")
            {
                this.allowEdition = true;
            }
            else if (this.allowEdition === false || this.allowEdition === "false")
            {
                this.allowEdition = false;
            }
            else
            {
                // Automatic mode
                this.allowEdition = container instanceof Ext.grid.CellEditor;
            }
            
            if (this.allowEdition)
            {
                this.editionButton.show();
            }
        }
    },
    
    getLabelTpl: function ()
    {
        var labelTpl = [];
        if (this.displayTypeIcon != false && this.displayTypeIcon != 'false')
        {
            labelTpl.push('<tpl if="values.iconGlyph != \'\'"><span class="x-tagfield-glyph {iconGlyph} {iconDecorator}"></span></tpl>');
            labelTpl.push('<tpl if="values.iconGlyph == \'\'"><img width="16" height="16" src="' + Ametys.CONTEXT_PATH + '{smallIcon}"/></tpl>');
        }
        
        if (this.openOnClick == true || this.openOnClick == 'true')
        {
            labelTpl.push('<tpl if="values.clickable == true"><span class="clickable">{[values.title]}</span></tpl>');
            labelTpl.push('<tpl if="values.clickable != true">{[values.title]}</tpl>');
        }
        else
        {
            labelTpl.push('{[values.title]}');
        }
     
        return labelTpl;
    },
    
    /**
     * Open the dialog box to search contents.
     */
    selectContentsBySearch: function()
    {
        this.triggerDialogBoxOpened = true;
        
        Ametys.cms.uihelper.SelectContentBySearch.open({
            icon: this.boxIcon,
            iconCls: this.boxIconCls,
            title: this.boxTitle, 
            boxSize: this.boxSize,
            
            minHeight: this.boxMinHeight,
            maxHeight: this.boxMaxHeight,

            helpmessage1: this.helpMsg1, 
            helpmessage2: this.helpMsg2, 
            
            callback: Ext.bind(this._selectContentsCb, this),
            
            multiple: this.multiple,
            
            modelId: this.modelId,
            contentType: this.contentType,
            searchModelParameters: this._getSearchModelParameters(),
            
            forceMode: this.boxForceMode,
            forceValues: this._getAdditionalLoadValues()
        });
    },
    
    /**
     * Get the search model parameters
     * @private
     */
    _getSearchModelParameters: function()
    {
        let solrRequest = this.solrRequest;
        if (solrRequest && this.contentInfo && this.contentInfo.contentId)
        {
            solrRequest = solrRequest.replace(/\$\{content.id\}/g, this.contentInfo.contentId);
        }
        return {
            solrRequest: solrRequest,
            restrictedContentType: this.contentType
        };
    },
    
    /**
     * This function is called after selecting contents in the search dialog box.
     * Sets the value of the field.
     * @param {String|String[]} contentIds The identifiers of selected content 
     * @private
     */
    _selectContentsCb: function(contentIds)
    {
        if (contentIds != null)
        {
            this.combobox.setValue(this.multiple ? Ext.Array.merge(this.combobox.getValue(), contentIds)
                                                 : contentIds);
            this._onComboBoxChange();
        }
        
        this.triggerDialogBoxOpened = false;
        this.focus();
    },
    
    /**
     * Open the dialog box to create a content.
     */
    openCreateContentBox: function()
    {
        var me = this;
        var openParams = Ext.apply({
            title: this.createBoxTitle,
            parentContentType: this.contentType,
            contentTypes: this._contentTypes,
            contentLanguage: this._getLanguage(),
            initWorkflowActionId: this.initWorkflowActionId,
            initAndEditWorkflowActionId: this.initAndEditWorkflowActionId,
            editWorkflowActionId: this.editWorkflowActionId,
            workflowName: this.workflowName,
            closeCbFn: function() { me.triggerDialogBoxOpened = false; me.focus(); }
        }, this._getAdditionalCreationParameters())
        
        this.triggerDialogBoxOpened = true;
        Ametys.cms.uihelper.CreateContent.open(openParams, this._openCreateContentBoxCb, this);
    },
    
    /**
     * @protected
     * Get additional parameters for creation
     * @return {Object} Non empty
     */
    _getAdditionalCreationParameters: function() 
    {
        return {};
    },
    
    updateAdditionalWidgetsConf: function(config)
    {
        this.contentInfo = config.contentInfo;
        
        // If a context is provided with a content id but without a lang
        // set the lang of the content as the lang for the context
        if (this.contentInfo && this.contentInfo.contentId != null && this.contentInfo.lang == null)
        {
            Ametys.cms.content.ContentDAO.getContent(this.contentInfo.contentId, c => {
                this.contentInfo.lang = c.getLang();
            })
        }
        
        // Delete lastQuery to force load of store next time the combo is expanded
        delete this.combobox.lastQuery;
    },
    
    /**
     * @private
     * Retrieve the context language, i.e. either the content language if a content is being edited,
     * or the current CMS language.
     * @return {String} The language.
     */
    _getLanguage: function()
    {
        var lang = null;
        
        if (this.contentInfo != null && this.contentInfo.lang)
        {
            lang = this.contentInfo.lang;
        }
        else
        {
            lang = Ametys.cms.language.LanguageDAO.getCurrentLanguage();
        }
        
        return lang;
    },
    
    /**
     * This function is called after a content has been created with the create content button.
     * Add the content to the select field and open the new content in the content tool (in view mode).
     * @param {String} contentId The identifier of created content 
     * @private
     */
    _openCreateContentBoxCb: function(contentId)
    {
        this.combobox.getStore().on('load', function() {
            // Wait for store to load, otherwise, the openCreateContent will blur the widget
            // in a grid, bluring the widget leads to the widget removal and saves its value but the value is not updated yet
            this.triggerDialogBoxOpened = false; 
            this.focus();
            
            Ext.defer(this._openCreatedContent, 1, this, [contentId]);
        }, this, { single: true } );

        var oldValue = this.combobox.getValue();
        //if not old value, or empty array, or the combobox is not multiselect, we set the value instead of adding it.
        if (!oldValue || (Ext.isArray(oldValue) && oldValue.length == 0) || !this.combobox.multiSelect)
        {
            this.combobox.setValue(contentId);
            this._onComboBoxChange();
        }
        else
        {
            this.combobox.addValue(contentId);
            this._onComboBoxChange();
        }
    },
    
    /**
     * @protected
     * After creation will open the content
     * @param {String} contentId The id to open
     */
    _openCreatedContent: function(contentId)
    {
        if (Ametys.tool.ToolsManager.getFactory('uitool-content'))
        {
            Ametys.tool.ToolsManager.openTool('uitool-content', {id: contentId, mode: 'view'});
        }
    },
    
    getStore: function()
    {
        return Ext.create('Ext.data.Store', {
            model: this._getModel(),
            proxy: {
                type: 'ametys',
                plugin: 'cms',
                url: 'select-content-search/list.json',
                reader: {
                    type: 'json',
                    rootProperty: 'contents'
                }
            },
            
            pageSize: this.maxResult,
            
            remoteSort: true,
            sortOnLoad: true,
            sorters: [{property: 'title', direction:'ASC'}],
            
            listeners: {
                beforeload: {fn: this._onStoreBeforeLoad, scope: this},
                load: {fn: this._onLoad, scope: this}
            }
        });
    },
    
    /**
     * @private
     * Get model for the store   
     */
    _getModel: function()
    {
        // Default implementation
        return 'Ametys.cms.form.widget.SelectContent.ContentEntry';  
    },
    
    /**
     * @private
     * Update the result bar if there is more results
     * @param {Ext.data.Store} store The store.
     */
    _onLoad: function(store)
    {
        // Update result bar
        let resultEl = Ext.get(this.combobox.id + '-picker-results');
        let resultEl2 = Ext.get(this.combobox.id + '-picker-results2');
        if (resultEl)
        {
            if (store.totalCount > this.maxResult)
            {
                if (this.editable || (this.allowSearch == true || this.allowSearch == 'true'))
                {
                    // Search limit is reached
                    resultEl.addCls("show");
                }
                else
                {
                    // Search limit is reached
                    resultEl2.addCls("show");
                }
            }
            else
            {
                // All results are visible
                resultEl.removeCls("show");
                resultEl2.removeCls("show");
            }
        }
        
        // If the field is not multiple and the noneValue is activated, we add the "none" value at the beginning of the combobox
        if (!this.multiple && (this.activateNoneValue == true || this.activateNoneValue == 'true'))
        {
            var noneValue = {};
            // Set the title given
            noneValue[this.displayField] = this.noneValueText || this.noneValueDefaultText;
            
            // Set other attributes
            noneValue[this.valueField] = this._noValueOptionId;
            noneValue["clickable"] = false;
            noneValue["iconGlyph"] = "ametysicon-sign-raw-forbidden";
            
            // Add the new none value to the combobox store
            store.insert(0, noneValue);
        }
    },
    
    /**
     * @private
     * Set the request parameters before loading the store.
     * @param {Ext.data.Store} store The store.
     * @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object that will be passed to the Proxy to load the Store.
     */
    _onStoreBeforeLoad: function(store, operation)
    {
        operation.setParams( operation.getParams() || {} );
        operation.setParams( Ext.apply(operation.getParams(), {
            model: this.modelId
        }));
        
        var lang = this._getLanguage();
        
        var values = {};
        
        // Title criterion
        var query = operation.getParams().query;
        if (query)
        {
            // TODO Provide standard IDs
            values['reference-title-like'] = '*' + query + '*';
        }
        
        values = Ext.apply(values, this._getAdditionalLoadValues());
        
        operation.setParams( Ext.apply(operation.getParams(), {
            'id': Ext.isEmpty(operation.getParams().id) ? null : operation.getParams().id.split(','),
            'excludeSubContents': this.excludeSubContents,
            'contextualParameters': {
                'language': lang,
                ...this._getSearchModelParameters()
            },
            values: values
        }));
        
    },
    
    /**
     * @protected
     * Get additional values used to load the combobox with values for search
     * @return {Object} Non empty
     */
    _getAdditionalLoadValues: function()
    {
        let values = {};
        // Language criterion
        // Only use the context language if there is a context
        if (this.limitToContextLanguage && this.contentInfo)
        {
            // TODO Provide standard IDs
            values['reference-contentLanguage-eq'] = this._getLanguage();
        }
        
        return values;
    },

    /**
     * @private
     * Listener of the edit button
     */
    _startEdit: function()
    {
        if (!this.triggerDialogBoxOpened)
        {
            this.triggerDialogBoxOpened = true;
            Ametys.cms.form.widget.SelectContent.EditContentsDialogBox.openDialog(this.getValue(), this.contentType, Ext.bind(this._afterEdit, this), {
                title: this.getFieldLabel(),
                view: this.editionView
            });
        }
    },
    
    /**
     * @private
     * Callback at the end of the edition process
     * @param {Object/Object[]} contents The contents (ids and titles) select in the dialog box. Can be null if edition was canceled
     */
    _afterEdit: function(contents)
    {
        var me = this;
        
        this.triggerDialogBoxOpened = false;
        
        if (contents)
        {
            // Some content might be missing due to right restriction.
            // Do not assume that all values of the widget are present.
            
            // Update title in the store of the underlying combobox
            Ext.Array.each(Ext.Array.from(contents), function(content) { 
                var record = me.combobox.getStore().getById(content.id);
                if (record)
                {
                    record.set("title", content.title);
                }
            });
        }
        
        this.focus();
    },
    
    /**
     * @private
     * Listener when the unerlying combobox value change in order to enable the edit button
     */
    _onComboBoxChange: function()
    {
        if (this.editionButton)
        {
            var finalValue = this.getValue();
            this.editionButton.setDisabled(!finalValue || this.multiple && finalValue.length == 0)
        }
    },
    
    setReadOnly: function(readOnly)
    {
        this.callParent(arguments);
        
        if (this.searchButton)
        {
            this.searchButton.setVisible(!readOnly);
        }
        
        if (this.createButton)
        {
            this.createButton.setVisible(!readOnly);
        }
    },
    
    setValue: function()
    {
        let a = this.callParent(arguments);
        this._onComboBoxChange();
        return a;
    }
});