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

/**
 * Class managing the "add node" action.
 * @private
 */
Ext.define('Ametys.repository.actions.RefreshNode',
{
    singleton: true,
    
    /**
     * This function refreshes the currently selected node.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        var jcrTool = Ametys.tool.ToolsManager.getTool('uitool-repository-jcr');
        
        if (target != null && jcrTool != null)
        {
            var tree = jcrTool.getTree();
            var nodes = tree.getSelectionModel().getSelection();
            
            if (nodes.length > 0)
            {
                tree.getStore().load({
                    node: nodes[0],
                    callback: function(records, operation, success) {
                        if (success)
                        {
                            tree.getSelectionModel().select(nodes);
                            // FIXME In case the node is not already expanded, we have to force it or the expand sprite will disappear.
                            nodes[0].expand();
                        }
                    },
                    scope: this
                });
                
                var nodePath = nodes[0].get('path');
                var workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
                var id = 'uitool-repository-nodeproperties$' + nodePath + '$' + workspaceName;
                
                var nodeTool = Ametys.tool.ToolsManager.getTool(id);
                if (nodeTool != null)
                {
                    nodeTool.refresh();
                }
            }
        }
    }
});

/**
 * Class managing the "add node" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.AddNode',
{
    singleton: true,
    
    /**
     * @property {String} _parentPath The parent path.
     * @private
     */
    _parentPath: null,
    
    /**
     * @property {String} _workspaceName The workspace name.
     * @private
     */
    _workspaceName: null,
    
    /**
     * @property {Boolean} _initialized True when the dialog is initialized.
     * @private
     */
    _initialized: false,
    
    /**
     * @property {Ametys.window.DialogBox} box The action dialog box.
     * @private
     */
    box: null,
    
    /**
     * Launch the "add node" action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            this._parentPath = target.getParameters().path;
            this._workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
            
            if (!this.delayedInitialize())
            {
                return;
            }
            
            this.box.show();
            this._initForm();
        }
    },
    
    /**
     * Create the node creation dialog.
     * @return {Boolean} True if correctly initialized.
     * @private
     */
    delayedInitialize: function()
    {
        if (this._initialized)
        {
            return true;
        }
        
        this._formPanel = Ext.create('Ext.form.Panel', {
            
            defaultType: 'textfield',
            defaults: {
            	cls: 'ametys',
                msgTarget: 'side',
                labelAlign: 'right',
                labelSeparator: '',
                labelWidth: 60,
                width: '100%'
            },
            
            border: false,
            
            items: [{
                xtype: 'component',
                cls: 'a-text',
                html: "{{i18n PLUGINS_REPOSITORYAPP_ADD_NODE_BOX_HINT}}"
            }, {
                xtype: 'textfield',
                fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_ADD_NODE_BOX_NAME_LABEL}}",
                name: 'name',
                allowBlank: false
            }, {
                xtype: 'combobox',
                fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_ADD_NODE_BOX_TYPE_LABEL}}",
                name: 'type',
                forceSelection: true,
                triggerAction: 'all',
                displayField: 'type',
                valueField: 'type',
                queryMode: 'local',
                store: {
                    fields: ['type'],
                    sorters: [{
                        property: 'type'
                    }]
                },
                emptyText: "{{i18n PLUGINS_REPOSITORYAPP_ADD_NODE_BOX_TYPE_COMBO_EMPTY}}",
                allowBlank: false
            }]
        });
        
        this.box = Ext.create('Ametys.window.DialogBox', {
            title: "{{i18n PLUGINS_REPOSITORYAPP_ADD_NODE_BOX_TITLE}}",
            iconCls: "ametysicon-document112",
            
            width: 400,
            scrollable: true,
            
            items: [ this._formPanel ],
            defaultFocus: 'name',
            
            closeAction: 'hide',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons: [{
            	reference: 'validate',
            	text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_OK}}",
                handler: this.ok,
                scope: this
            }, {
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_CANCEL}}",
                handler: this.cancel,
                scope: this
            }]
        });
        
        this._initialized = true;
        
        return true;
    },
    
    /**
     * Initialize the form.
     * @private
     */
    _initForm: function()
    {
        var form = this._formPanel.getForm();
        
        form.findField('name').setValue('');
        form.findField('name').focus();
        form.findField('type').setValue('');
        form.clearInvalid();
        
        this._loadTypes();
    },
    
    /**
     * Load the available node types to fill the associated combobox.
     * @private
     */
    _loadTypes: function()
    {
        var path = this._parentPath.substring(1);
        
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'getChildrenTypes',
            parameters: [path, this._workspaceName],
            callback: {
                handler: this._loadTypesCb,
                scope: this
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_NODE_TYPES}}"
        });
    },
    
    /**
     * Fill the node types combobox.
     * @param {String[]} types The children types
     * @param {Array} args The callback arguments.
     * @private
     */
    _loadTypesCb: function(types, args)
    {
        var typeData = [];
        for (var i = 0; i < types.length; i++)
        {
            typeData.push({
                type: types[i]
            });
        }
        
        var store = this._formPanel.getForm().findField('type').getStore();
        store.loadData(typeData, false);
    },
    
    /**
     * Handle the OK button by submitting the form.
     * @private
     */
    ok: function()
    {
        var form = this._formPanel.getForm();
        
        if (!form.isValid())
        {
            return;
        }
        
        var parentPath = this._parentPath.substring(1);
        var nodeName = form.findField('name').getValue();
        var nodeType = form.findField('type').getValue();
        
        // Add node in the JCR repository.
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'addNode',
            parameters: [parentPath, nodeName, nodeType, this._workspaceName],
            callback: {
                handler: this._okCb,
                scope: this
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_ADD_NODE}}"
        });
    },
    
    /**
     * Callback fired when the node has been added
     * @param {Object} result The server response
     * @param {String} result.path The child path
     * @param {String} result.pathWithGroups The full path
     * @param {Array} args The callback arguments.
     * @private 
     */
    _okCb: function(result, args)
    {
        this.box.hide();
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.CREATED,
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [result.path],
                    workspaceName: this._workspaceName
                }
            }
        });
    },
    
    /**
     * Handle the cancel button by hiding the dialog box.
     * @private
     */
    cancel: function()
    {
        this.box.hide();
    }
});

//------------------ RENAME NODE -----------------------//
/**
 * Class managin the "rename node" action
 * @private 
 */
Ext.define('Ametys.workspace.repository.actions.RenameNode',
{
    singleton: true,
    
    /**
     * Launch the rename node action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var me = this;
        
        // Search for the first explorer target (explorer node or resource).
        var target = Ametys.message.MessageTargetHelper.findTarget(controller.getMatchingTargets(), this._targetFilter);
        if (target)
        {
            var target = controller.getMatchingTargets()[0];
            
            if (target != null)
            {
                var path = target.getParameters().path;
                var oldName =target.getParameters().name;
                var workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
                
                Ametys.Msg.prompt(
                    "{{i18n PLUGINS_REPOSITORYAPP_RENAME_TITLE}}", 
                    "{{i18n PLUGINS_REPOSITORYAPP_RENAME}}", 
                    function(btn, newName) {
                        if (btn == 'ok' && oldName != newName)
                        {
                            me._doRename(path, workspaceName, newName);
                        }
                    },
                    this,
                    false,
                    oldName
                );
            }
        }
    },
    
    /**
     * Function called after validating the prompt dialog box
     * @param {String} path The absolute path of the node to rename
     * @param {String} workspaceName The current workspace name
     * @param {String} newName The new node name
     * @private
     */
    _doRename: function(path, workspaceName, newName)
    {
        var me = this;
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.DELETING,
            
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [path],
                    workspaceName: workspaceName
                }
            },
            callback: function(deletingMsg) {
                Ametys.data.ServerComm.callMethod({
                    role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
                    methodName: 'renameNode',
                    parameters: [path, workspaceName, newName],
                    callback: {
                        handler: me._doRenameCb,
                        scope: me,
                        arguments: {
                            path: path,
                            workspaceName: workspaceName,
                            targets: deletingMsg.getTargets()
                        }
                    },
                    errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_RENAME_NODE}}"
                });
            }
        });
    },
        
    /**
     * Callback fired when the node has been removed.
     * @param {Object} result The server response
     * @param {String} result.path The child path
     * @param {String} result.pathWithGroups The full path
     * @param {Array} args The callback arguments.
     * @private 
     */
    _doRenameCb: function(result, args)
    {
        // Send the deleted message with stored targets
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.DELETED,
            targets: args.targets
        });
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.CREATED,
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [result.path],
                    workspaceName: args.workspaceName
                }
            }
        });
    }  
});

//------------------ REMOVE NODE -----------------------//
/**
 * Class managing the "remove node" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.RemoveNode',
{
    singleton: true,
    
    /**
     * Launch the remove node action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var path = target.getParameters().path;
            var workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
            
            Ext.MessageBox.confirm(
                "{{i18n PLUGINS_REPOSITORYAPP_REMOVE_NODE_BOX_TITLE}}",
                "{{i18n PLUGINS_REPOSITORYAPP_REMOVE_NODE_BOX_HINT}} <br/>" + path,
                function(button) {
                    if (button == 'yes') {
                        this._doAct(path, workspaceName);
                    }
                },
                this
            );
        }
    },
    
    /**
     * Send the remove node message to the server.
     * @private
     */
    _doAct: function(path, workspaceName)
    {
        var me = this;
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.DELETING,
            
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [path],
                    workspaceName: workspaceName
                }
            },
            callback: function(deletingMsg) {
                Ametys.data.ServerComm.callMethod({
                    role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
                    methodName: 'removeNode',
                    parameters: [path, workspaceName],
                    callback: {
                        handler: me._doActCb,
                        scope: me,
                        arguments: {
                            path: path,
                            workspaceName: workspaceName,
                            targets: deletingMsg.getTargets()
                        }
                    },
                    errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_REMOVE_NODE}}"
                });
            }
        });
    },
    
    /**
     * Callback fired when the node has been removed.
     * @param {Object} result The action result.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _doActCb: function(result, args)
    {
        // Send the deleted message with stored targets
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.DELETED,
            targets: args.targets
        });
    }
});

//------------------ CHECKIN-CHECKOUT -----------------------//
/**
 * Class managing the "checkout node" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.CheckoutNode',
{
    singleton: true,
    
    /**
     * Launch the checkout node action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var path = target.getParameters().path;
            var workspaceName = target.getParameters().workspaceName;
            
            this._checkoutNode(path, workspaceName);
        }
    },
    
    /**
     * Send the checkout node message to the server.
     * @private
     */
    _checkoutNode: function(path, workspaceName)
    {
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'checkoutNode',
            parameters: [path, workspaceName],
            callback: {
                handler: this._checkoutNodeCb,
                scope: this,
                arguments: {
                    path: path,
                    workspaceName: workspaceName
                }
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_CHECKOUT_NODE}}"
        });
    },
    
    /**
     * Callback fired when the node has been checked out.
     * @param {Object} result The action result.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _checkoutNodeCb: function(result, args)
    {
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.MODIFYING,
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [args.path],
                    workspaceName: args.workspaceName
                }
            }
        });
    }
});

//------------------ UNLOCK -----------------------//
/**
 * Class managing the "unlock node" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.UnlockNode',
{
    singleton: true,
    
    /**
     * Launch the unlock node action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var path = target.getParameters().path;
            var workspaceName = target.getParameters().workspaceName;
            
            this._doAct(path, workspaceName);
        }
    },
    
    /**
     * Send the unlock node message to the server.
     * @param {String} path The node path.
     * @param {String} workspaceName The workspace name.
     * @private
     */
    _doAct: function(path, workspaceName)
    {
        Ametys.data.ServerComm.callMethod({
            role: "org.ametys.workspaces.repository.jcr.RepositoryDao",
            methodName: "unlockNode", 
            parameters: [path, workspaceName],
            callback: {
                handler: this._doActCb,
                scope: this,
                arguments: { path: path, workspaceName: workspaceName },
                ignoreOnError : false
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_UNLOCK_NODE}}"
        });
    },
    
    /**
     * Callback fired when the node has been unlocked.
     * @param {Object} response The server response
     * @param {Object} args The callback arguments.
     * @param {String} args.path The path
     * @param {String} args.workspaceName The workspace
     * @private 
     */
    _doActCb: function(response, args)
    {
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.MODIFIED,
            targets: [{
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [ args.path ],
                    workspaceName: args.workspaceName
                }
            }]
        });
        
        
    }
});

//------------------ ADD PROPERTY -----------------------//
/**
 * Class managing the "add property" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.AddProperty',
{
    singleton: true,
    
    /**
     * @property {Ext.data.Model} _nodePath The node path.
     * @private
     */
    _nodePath: null,
    
    /**
     * @property {String} _workspaceName The workspace name.
     * @private
     */
    _workspaceName: null,
    
    /**
     * @property {Boolean} _initialized True when the dialog is initialized.
     * @private
     */
    _initialized: false,
    
    /**
     * Launch the "add property" action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            this._nodePath = target.getParameters().path;
            this._workspaceName = target.getParameters().workspaceName;
            
            if (!this.delayedInitialize())
            {
                return;
            }
            
            this.box.show();
            this._initForm();
        }
    },
    
    /**
     * Create the property creation dialog box.
     * @return {Boolean} True if correctly initialized.
     * @private
     */
    delayedInitialize: function()
    {
        if (this._initialized)
        {
            return true;
        }
        
        this._formPanel = Ext.create('Ext.form.Panel', {
            defaultType: 'textfield',
            defaults: {
            	cls: 'ametys',
                msgTarget: 'side',
                labelAlign: 'right',
                labelSeparator: '',
                labelWidth: 70,
                width: '100%'
            },
            
            border: false,
            
            items: [{
                xtype: 'component',
                cls: 'a-text',
                html: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_HINT}}"
            }, {
            	itemId: 'name',
                xtype: 'textfield',
                fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_NAME_LABEL}}",
                name: 'name',
                allowBlank: false
            }, {
                xtype: 'combobox',
                fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_TYPE_LABEL}}",
                name: 'type',
                
                editable: false,
                triggerAction: 'all',
                
                // TODO Put a display value?
                // TODO Add WeakRef and URI?
                queryMode: 'local',
                store: ['String','Date','Long','Double','Boolean','Binary','Name','Path','Reference'],
                
                emptyText: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_TYPE_COMBO_EMPTY}}",
                allowBlank: false,
                listeners: {
                    select: {fn: this._drawValueField, scope: this}
                }
            }]
        });
        
        this.box = Ext.create('Ametys.window.DialogBox', {
            title: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_TITLE}}",
            iconCls: 'ametysicon-list6',
            
            width: 400,
            maxHeight: 300,
            scrollable: true,
            
            items: [ this._formPanel ],
            
            defaultFocus: 'name',
            closeAction: 'hide',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons: [{
            	reference: 'validate',
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_OK}}",
                handler: this.ok,
                scope: this
            }, {
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_CANCEL}}",
                handler: this.cancel,
                scope: this
            }]
        });
        
        this._initialized = true;
        
        return true;
    },
    
    /**
     * Listens for value combobox change and draw the value input field accordingly.
     * @param {Ext.form.field.ComboBox} combo The type combobox.
     * @param {Array} records The selected records.
     * @private
     */
    _drawValueField: function(combo, records)
    {
        var form = this._formPanel.getForm();
        
        var valueFd = form.findField('fieldValue');
        if (valueFd != null)
        {
            this._formPanel.remove(valueFd);
        }
        
        var multipleOldValue = false;
        var multipleFd = form.findField('multiple');
        if (multipleFd != null)
        {
            multipleOldValue = multipleFd.getValue();
            this._formPanel.remove(multipleFd);
        }
        
        this._formPanel.items.each(function(item, index, len) {
            if (item.name == 'container')
            {
                this._formPanel.remove(item);
            }
        }, this);
        
        var drawMultiple = true;
        var type = form.findField('type').getValue();
        switch (type) 
        {
            case 'Date':
                var valueFd = Ext.create('Ametys.form.field.DateTime', {
                    fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    name: 'fieldValue',
                    allowBlank: false
                });
                
                this._formPanel.add(valueFd);
                
                drawMultiple = false;
                break;
                
            case 'Boolean':
                var valueFd = Ext.create('Ext.form.field.ComboBox', {
                    fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    editable: false,
                    name: 'fieldValue',
                    triggerAction: 'all',
                    queryMode: 'local',
                    store: ['true', 'false'],
                    allowBlank: false
                });
                drawMultiple = false;
                this._formPanel.add(valueFd);
                break;
                
            case 'Long':
                var valueFd = Ext.create('Ext.form.field.Number', {
                    fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    name: 'fieldValue',
                    allowBlank: false,
                    allowDecimals: false
                });
                this._formPanel.add(valueFd);
                break;
                
            case 'Double':
                var valueFd = Ext.create('Ext.form.field.Number', {
                    fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    name: 'fieldValue',
                    allowBlank: false
                });
                this._formPanel.add(valueFd);
                break;
                
            case 'Binary':
                var valueFd = Ext.create('Ext.form.field.File', {
                    fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    name: 'fieldValue',
                    allowBlank: false
                });
                this._formPanel.add(valueFd);
                drawMultiple = false;
                break;
                
            default:
                var valueFd = Ext.create('Ext.form.field.Text', {
                    fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_VALUE_LABEL}}",
                    name: 'fieldValue',
                    allowBlank: false
                });
                this._formPanel.add(valueFd);
                break;
        }
        
        if (drawMultiple)
        {
            this._formPanel.add({
                xtype: 'checkbox',
                fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_MULTIPLE_LABEL}}",
                desc: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_BOX_MULTIPLE_HELP}}",
                name: 'multiple',
                inputValue: 'true',
                uncheckedValue: 'false',
                inputWidth: 13,
                checked: multipleOldValue
            });
        }
    },
    
    /**
     * Initialize the form.
     * @private
     */
    _initForm: function()
    {
        var form = this._formPanel.getForm();
        
        form.findField('name').setValue('');
        form.findField('type').setValue('');
        
        form.clearInvalid();
        
        var valueFd = form.findField('fieldValue');
        if (valueFd != null)
        {
            this._formPanel.remove(valueFd);
        }   
        var multipleFd = form.findField('multiple');
        if (multipleFd != null)
        {
            this._formPanel.remove(multipleFd);
        }
    },
    
    /**
     * Handle the OK button by submitting the form.
     * @private
     */
    ok: function()
    {
        var form = this._formPanel.getForm();
        
        if (!form.isValid())
        {
            return;
        }
        
        var name = form.findField('name').getValue();
        var type = form.findField('type').getValue();
        var valueField = form.findField('fieldValue');
        var value = valueField.fileInput != null ? valueField.fileInput.getValue() : valueField.getSubmitValue();
        
        var multiple = 'false';
        if (form.findField('multiple') != null)
        {
            multiple = form.findField('multiple').getValue().toString();
        }
        
        var nodePath = this._nodePath.substring(1);
        
        form.submit({
            url: Ametys.getPluginDirectPrefix('repositoryapp') + '/repository/set-property',
            params: {
                workspace: this._workspaceName,
                path: nodePath,
                name: name,
                value: value,
                type: type,
                multiple: multiple
            },
            success: this._success,
            failure: this._failure,
            scope: this
        });
    },
    
    /**
     * Callback fired when the property has been successfully added.
     * @param {Ext.form.Basic} form The form that requested the action.
     * @param {Ext.form.action.Action} action The Action class.
     * @private
     */
    _success: function(form, action)
    {
        this.box.hide();
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.MODIFYING,
            parameters: {
                major: true
            },
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [this._nodePath],
                    workspaceName: this._workspaceName
                }
            }
        });
    },
    
    /**
     * Callback fired when the property creation has failed.
     * @param {Ext.form.Basic} form The form that requested the action.
     * @param {Ext.form.action.Action} action The Action class.
     * @private
     */
    _failure: function(form, action)
    {
        Ametys.log.ErrorDialog.display({
            title: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_ERROR_TITLE}}",
            text: "{{i18n PLUGINS_REPOSITORYAPP_CREATE_PROP_ERROR_TEXT}}",
            details: action.result ? action.result.message : ''
        });
    },
    
    /**
     * Handle the cancel button by hiding the dialog box.
     * @private
     */
    cancel: function()
    {
        this.box.hide();
    }
});

//------------------ REMOVE PROPERTY -----------------------//
/**
 * Class managing the "remove property" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.RemoveProperty',
{
    singleton: true,
    
    /**
     * Launch the remove node action.
     * @param {String} path The node path.
     * @param {String} workspaceName The workspace name.
     * @param {String} propertyName The property name.
     * @param {Function} [callback] The callback function to invoke after deletion success
     */
    act: function(path, workspaceName, propertyName, callback)
    {
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao',
            methodName: 'removeProperty',
            parameters: [path, workspaceName, propertyName],
            callback: {
                handler: this._doActCb,
                scope: this,
                arguments: {
                    path: path,
                    workspaceName: workspaceName,
                    propertyName: propertyName,
                    callback : callback
                }
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_REMOVE_PROPERTY}}"
        });
    },
    
    /**
     * Callback fired when the property has been removed.
     * @param {Object} result The action result.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _doActCb: function(result, args)
    {
        if (Ext.isFunction(args.callback))
        {
            args.callback();
        }
        
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.MODIFYING,
            parameters: {
                major: true
            },
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                parameters: {
                    paths: [args.path],
                    workspaceName: args.workspaceName
                }
            }
        });
    }
});

//------------------ SAVE SESSION -----------------------//
/**
 * Class managing the "save session" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.SaveSession',
{
    singleton: true,
    
    /**
     * Launch the save session action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var workspaceName = target.getParameters().workspaceName;
            this._doAct(workspaceName);
        }
    },
    
    /**
     * Send the session save message to the server.
     * @private
     */
    _doAct: function(workspaceName)
    {
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'saveSession',
            parameters: [workspaceName],
            callback: {
                handler: this._doActCb,
                scope: this,
                arguments: {
                    workspaceName: workspaceName
                }
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_SAVE_SESSION}}"
        });
    },
    
    /**
     * Callback fired when the session has been saved.
     * @param {Object} result The action result.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _doActCb: function(result, args)
    {
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.MODIFIED,
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_SESSION,
                parameters: {
                    workspaceName: args.workspaceName
                }
            }
        });
    }
});

//------------------ ROLLBACK SESSION -----------------------//
/**
 * Class managing the "rollback session" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.RollbackSession',
{
    singleton: true,
    
    /**
     * Launch the rollback session action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var workspaceName = target.getParameters().workspaceName;
            Ametys.Msg.confirm(
                "{{i18n PLUGINS_REPOSITORYAPP_ROLLBACK_SESSION_CONFIRM_TITLE}}",
                "{{i18n PLUGINS_REPOSITORYAPP_ROLLBACK_SESSION_CONFIRM_MSG}}",
                function(button) {
                    if (button == 'yes')
                    {
                        this._doAct(workspaceName);
                    }
                },
                this
            );            
        }
    },
    
    /**
     * Send the session rollback message to the server.
     * @private
     */
    _doAct: function(workspaceName)
    {
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'rollbackSession',
            parameters: [workspaceName],
            callback: {
                handler: this._doActCb,
                scope: this,
                arguments: {
                    workspaceName: workspaceName
                }
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_ROLLBACK_SESSION}}"
        });
    },
    
    /**
     * Callback fired when the session has been rolled back.
     * @param {Object} response The action response.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _doActCb: function(response, args)
    {
        Ext.create('Ametys.message.Message', {
            type: Ametys.message.Message.REVERTED,
            targets: {
                id: Ametys.message.MessageTarget.REPOSITORY_SESSION,
                parameters: {
                    workspaceName: args.workspaceName
                }
            }
        });
    }
});

//------------------ SORT NODE -----------------------//
/**
 * Class managing the "sort child nodes" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.SortNode',
{
	singleton: true,

    /**
     * The action for sorting child nodes of current selection
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller sending the action
     */
	act: function(controller)
	{
        var target = controller.getMatchingTargets()[0];
        
        if (target != null)
        {
            var nodePath = target.getParameters().path;
            var workspaceName = target.getParameters().workspaceName;
            var order = controller.getInitialConfig('order');
            
            Ext.create('Ametys.message.Message', {
                type: Ametys.message.Message.SORTED,
                targets: {
                    id: Ametys.message.MessageTarget.REPOSITORY_NODE,
                    parameters: {
                        paths: [nodePath],
                        workspaceName: workspaceName
                    }
                },
                parameters: {
                    order: order
                }
            });
        }
	}
});

//------------------ EXECUTE QUERY -----------------------//
/**
 * Class managing the "execute query" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.ExecuteQuery',
{
    singleton: true,
    
    /**
     * @property {String} _workspaceName The workspace name.
     * @private
     */
    _workspaceName: null,
    
    /**
     * @property {Ext.form.Panel} _formPanel The dialog box form panel.
     * @private
     */
    _formPanel: null,
    
    /**
     * @property {Boolean} _initialized True when the dialog is initialized.
     * @private
     */
    _initialized: false,
    
    /**
     * @property {Ametys.window.DialogBox} box The action dialog box.
     * @private
     */
    box: null,
    
    /**
     * Launch the "execute query" action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        this._workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
        
        if (!this.delayedInitialize())
        {
            return;
        }
        
        this.box.show();
    },
    
    /**
     * Create the query dialog box.
     * @private
     */
    delayedInitialize: function()
    {
        if (this._initialized)
        {
            return true;
        }
        
        this._formPanel = Ext.create('Ext.form.Panel', {
            defaultType: 'textfield',
            fieldDefaults: {
            	cls: 'ametys',
                labelAlign: 'right',
                labelWidth: 60,
                width: '100%',
                labelSeparator: '',
                msgTarget: 'side'
            },
            
            border: false,
            scrollable: true,
            items: [{
                xtype: 'radiogroup',
                name: 'language-group',
                fieldLabel: "{{i18n PLUGINS_REPOSITORYAPP_QUERY_LANGUAGE_LABEL}}",
                items: [
                    {boxLabel: "{{i18n PLUGINS_REPOSITORYAPP_QUERY_LANGUAGE_XPATH}}", name: 'language', inputValue: 'xpath', checked: true},
                    {boxLabel: "{{i18n PLUGINS_REPOSITORYAPP_QUERY_LANGUAGE_SQL}}", name: 'language', inputValue: 'sql'},
                    {boxLabel: "{{i18n PLUGINS_REPOSITORYAPP_QUERY_LANGUAGE_JCRSQL2}}", name: 'language', inputValue: 'JCR-SQL2'}
                ]
            }, {
            	itemId: 'query',
                xtype: 'textarea',
                fieldLabel : "{{i18n PLUGINS_REPOSITORYAPP_QUERY_PROMPT_LABEL}}",
                name: 'query',
                height: 60,
                allowBlank: false,
                maskRe: /[^\r\n]/,
                stripCharsRe: /[\r\n]/
            }, {
                xtype: 'component',
                cls: 'a-text-light',
                html: "<u>{{i18n PLUGINS_REPOSITORYAPP_QUERY_EXAMPLE_ABBREV}}</u><br/><strong>XPath: </strong>//element(*, nt:resource)[@jcr:mimeType = 'image/jpeg']<br/><strong>SQL: </strong>SELECT * FROM nt:resource WHERE jcr:mimeType = 'image/jpeg'"
            }]
        });
        
        this.box = Ext.create('Ametys.window.DialogBox', {
            title: "{{i18n PLUGINS_REPOSITORYAPP_QUERY_PROMPT_TITLE}}",
            iconCls: 'ametysicon-magnifier41',
            
            width: 600,
            
            items: [this._formPanel],
            
            closeAction: 'hide',
            defaultFocus: 'query',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons: [{
            	reference: 'validate',
                text:"{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_OK}}",
                handler: this.ok,
                scope: this
            }, {
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_CANCEL}}",
                handler: this.cancel,
                scope: this
            }]
        });
        
        this._initialized = true;
        
        return true;
    },
    
    /**
     * Handle the OK button by submitting the form.
     * @private
     */
    ok: function()
    {
        var form = this._formPanel.getForm();
        
        if (!form.isValid())
        {
            return;
        }
        
        var values = form.getValues();
        
        // Open the repository JCR tree with the search query
        Ametys.tool.ToolsManager.openTool("uitool-repository-jcr", {query: values.query, queryLanguage: values.language, workspaceName: Ametys.repository.RepositoryApp.getCurrentWorkspace()});
        
        this.box.hide();
    },
    
    /**
     * Handle the cancel button by hiding the dialog box.
     * @private
     */
    cancel: function()
    {
        this.box.hide();
    }
});

//------------------ RESOLVE IDENTIFIER -----------------------//
/**
 * Class managing the "resolve node by identifier" action.
 * @private
 */
Ext.define('Ametys.workspace.repository.actions.ResolveIdentifier',
{
    singleton: true,
    
    /**
     * @property {String} _workspaceName The workspace name.
     * @private
     */
    _workspaceName: null,
    
    /**
     * @property {Function} _callbackFn A callback function.
     * @private
     */
    _callbackFn: null,
    
    /**
     * @property {Ext.form.Panel} _formPanel The dialog box form panel.
     * @private
     */
    _formPanel: null,
    
    /**
     * @property {Boolean} _initialized True when the dialog is initialized.
     * @private
     */
    _initialized: false,
    
    /**
     * @property {Ametys.window.DialogBox} box The action dialog box.
     * @private
     */
    box: null,
    
    /**
     * Launch the "resolve identifier" action.
     * @param {Ametys.ribbon.element.ui.ButtonController} controller The controller calling this function.
     */
    act: function(controller)
    {
        this._workspaceName = Ametys.repository.RepositoryApp.getCurrentWorkspace();
        
        if (!this.delayedInitialize())
        {
            return;
        }
        
        this.box.show();
        this._initForm();
    },
    
    /**
     * Initialize the form.
     * @private
     */
    _initForm: function()
    {
        var idField = this.box.getComponent('identifier');
        idField.setValue('');
        idField.clearInvalid();
    },
    
    /**
     * Create the dialog.
     * @private
     */
    delayedInitialize: function()
    {
        if (this._initialized)
        {
            return true;
        }
        
        this.box = Ext.create('Ametys.window.DialogBox', {
            title: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_DIALOG_TITLE}}",
            iconCls: 'ametysicon-search16',
            
            layout: {
                type: 'vbox',
                align: 'stretch'
            },
            scrollable: true,
            width: 330,
            
            items: [{
	                xtype: 'component',
	                cls: 'a-text',
	                html: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_PROMPT_TEXT}}"
	                
	            },{
                    xtype: 'textfield',
	                cls: 'ametys',
                    hideLabel: true,
	                name: 'identifier',
	                itemId: 'identifier',
	                allowBlank: false,
	                msgTarget: 'side'
	            }],

	        defaultFocus: 'identifier',
            closeAction: 'hide',
            
            referenceHolder: true,
            defaultButton: 'validate',
            
            buttons: [{
            	reference: 'validate',
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_OK}}",
                handler: this.ok,
                scope: this
            }, {
                text: "{{i18n PLUGINS_REPOSITORYAPP_DIALOG_BOX_CANCEL}}",
                handler: this.cancel,
                scope: this
            }]
        });
        
        this._initialized = true;
        
        return true;
    },
    
    /**
     * Handle the OK button by submitting the form.
     * @private
     */
    ok: function()
    {
        var idField = this.box.getComponent('identifier');
        if (!idField.isValid())
        {
            return;
        }
        
        var identifier = idField.getValue();
        
        // Get the node path from its identifier.
        Ametys.data.ServerComm.callMethod({
            role: 'org.ametys.workspaces.repository.jcr.RepositoryDao', 
            methodName: 'getNodeByIdentifier',
            parameters: [identifier, this._workspaceName],
            callback: {
                handler: this._resolveIdentifierCb,
                scope: this
            },
            errorMessage: "{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_SOLVE_IDENTIFIER}}"
        });
    },
    
    /**
     * Callback fired when the request has ended.
     * @param {Object} nodeInfo The node info, if found.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _resolveIdentifierCb: function(nodeInfo, args)
    {
        if (nodeInfo == null)
        {
            Ametys.Msg.show({
                title: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_DIALOG_TITLE}}",
                msg: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_UNKNOWN_ITEM_TEXT}}",
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            });
            return;
        }
        
        this.box.hide();
        
        // Open node properties.
        Ametys.repository.tool.NodePropertiesTool.open(nodeInfo.path, this._workspaceName);
    },
    
    /**
     * Callback fired when the request has ended.
     * @param {HTMLElement} response The XML document.
     * @param {Array} args The callback arguments.
     * @private 
     */
    _okCb: function(response, args)
    {
        if (Ametys.data.ServerComm.handleBadResponse("{{i18n PLUGINS_REPOSITORYAPP_ERROR_MSG_SOLVE_IDENTIFIER}}", response, 'Ametys.workspace.repository.actions.SolveIdentifier'))
        {
            return;
        }
        
        // Unknown identifier?
        var unknown = Ext.dom.Query.selectNode('unknown', response);
        if (unknown != null)
        {
            Ametys.Msg.show({
                title: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_DIALOG_TITLE}}",
                msg: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_UNKNOWN_ITEM_TEXT}}",
                buttons: Ext.Msg.OK,
                icon: Ext.MessageBox.INFO
            });
            return;
        }
        
        // Repository error?
        var errorMessage = Ext.dom.Query.selectValue('error', response, null);
        if (errorMessage != null)
        {
            Ametys.log.ErrorDialog.display({
                title: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_DIALOG_TITLE}}",
                text: "{{i18n PLUGINS_REPOSITORYAPP_RESOLVE_IDENTIFIER_ERROR_TEXT}}",
                details: errorMessage
            });
            
            return;
        }
        
        this.box.hide();
        
        if (typeof this._callbackFn == 'function')
        {
            var path = Ext.dom.Query.selectValue('path', response, null);
            this._callbackFn(path);
        }
    },
    
    /**
     * Handle the cancel button by hiding the dialog box.
     * @private
     */
    cancel: function()
    {
        this.box.hide();
    }
});