/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
CQ.Ext.namespace("CQ.security");
CQ.Ext.namespace("CQ.security.data");/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * @class CQ.Ext.security.data.AclStore
 * @extends CQ.Ext.data.Store
 * convenience to Access ACL as AclRecords
 * @constructor
 * Creates a new Store.
 * @param {Object} config A config object containing the objects needed for the Store to access data,
 * and read the data into Records.
 */
CQ.security.data.AclStore = function(config) {
    var def = {
        reader:new CQ.Ext.data.JsonReader({root:"acl",
            id:"principal",
            totalProperty: "aces"},
                CQ.security.data.AclRecord.create())
    };
    if (!config.reader) {
        config.reader = new CQ.Ext.data.JsonReader({root:"acl",
            id: config.recId ? config.recId : "principal",
            totalProperty: "aces"},
                CQ.security.data.AclRecord.create())
    }

    CQ.Ext.applyIf(config, def);
    CQ.security.data.AclStore.superclass.constructor.call(this, config);
};

CQ.Ext.extend(CQ.security.data.AclStore, CQ.Ext.data.Store);/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * Convenience to create a Record for ACL's.
 * Allows to access the known Fileds for this Record.
 * Thus it can be used in an interface like manner
 * 
 * @class CQ.security.data.AclRecord
 * @extends CQ.Ext.data.Record
 * @constructor
 */
CQ.security.data.AclRecord = function(data, id){
    CQ.security.data.AclRecord.superclass.constructor.call(this, data, id); 
};

CQ.security.data.AclRecord.create = function() {
    var f = CQ.Ext.extend(CQ.security.data.AclRecord, {});
    var p = f.prototype;
    p.fields = new CQ.Ext.util.MixedCollection(false, function(field) {
        return field.name;
    });
    var o = CQ.security.data.AclRecord.FIELDS;
    for (var i = 0, len = o.length; i < len; i++) {
        p.fields.add(new CQ.Ext.data.Field(o[i]));
    }
    f.getField = function(name) {
        return p.fields.get(name);
    };
    return f;
}

CQ.Ext.extend(CQ.security.data.AclRecord, CQ.Ext.data.Record, {

    get:function(key) {
        if (key.indexOf("/")>-1) {
            var privs = this.data.privileges;
            return privs? privs.get(key) : null;
        } else {
            return this.data[key];
        }
    },
    
    /**
     * Serialize the record in following format
     * [name>]:[value],
     * Thus a store can be transpoted with an parameter for each row, containing
     * the Record in this serial format 
     */
    toParam:function() {
        var res = "";
        var fs = CQ.security.data.AclRecord.FIELDS;
        for (var i=0;i<fs.length;i++) {
            var f = fs[i];
            var val = this.get(f.name);
            if (val) {
                var name = f.mapping ? f.mapping : f.name;
                res += encodeURIComponent(name) +":" + encodeURIComponent(val)+",";
            }
        }
        return res;
    }
});

/**
 * Field definition for ACLs
 * @static
 * @final
 * @private
 * @type Array
 */
CQ.security.data.AclRecord.FIELDS = [
    {"name":"type"},
    {"name":"authorizable"},
    {"name":"principal"},
    {"name":"read"},
    {"name":"create","mapping":"add_node"},
    {"name":"update","mapping":"set_property"},
    {"name":"delete","mapping":"remove"},
    {"name":"read ACL","mapping":"acl_read"},
    {"name":"write ACL","mapping":"acl_edit"},
    {"name":"path"},
    {"name":"privileges", "convert": function(v){
        var r = new CQ.Ext.util.MixedCollection(false, function(o){return o.id});
        r.addAll(v);
        return r;
    }
    }
];



/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */


/**
 * Convenience to create a Reader for for Authorizables's.
 * Allows to access the known Fileds for this Record.
 * Thus it can be used in an interface like manner
 *
 * @class CQ.security.data.AuthRecord
 * @extends CQ.Ext.data.Record
 * @constructor
 */
CQ.security.data.AuthReader = CQ.Ext.extend(CQ.Ext.data.JsonReader, {

    constructor: function (config){
        config = CQ.Util.applyDefaults(config, {
            totalProperty:"results",
            root:"authorizables", id:"id"
            });
        CQ.security.data.AuthReader.superclass.constructor.call(this, config,
                CQ.security.data.AuthRecord.create(CQ.security.data.AuthRecord.FIELDS));
    }
});


/**
 * Convenience to create a Record for Authorizables's.
 * Allows to access the known Fileds for this Record.
 * Thus it can be used in an interface like manner
 *
 * @class CQ.security.data.AuthRecord
 * @extends CQ.Ext.data.Record
 * @constructor
 */
CQ.security.data.AuthRecord = function(data, id){
    CQ.security.data.AuthRecord .superclass.constructor.call(this, data, id);
};

CQ.security.data.AuthRecord.create = function() {
    var f = CQ.Ext.extend(CQ.security.data.AuthRecord, {});
    var p = f.prototype;
    p.fields = new CQ.Ext.util.MixedCollection(false, function(field) {
        return field.name;
    });
    var o = CQ.security.data.AuthRecord.FIELDS;
    for (var i = 0, len = o.length; i < len; i++) {
        p.fields.add(new CQ.Ext.data.Field(o[i]));
    }
    f.getField = function(name) {
        return p.fields.get(name);
    };
    return f;
}

CQ.Ext.extend(CQ.security.data.AuthRecord, CQ.Ext.data.Record, {

    /**
     * Serialize the record in following format
     * [name>]:[value],
     * Thus a store can be transpoted with an parameter for each row, containing
     * the Record in this serial format
     */
    toParam:function() {
        var res = "";
        var fs = CQ.security.data.AuthRecord.FIELDS;
        for (var i=0;i<fs.length;i++) {
            var f = fs[i];
            var val = this.get(f.name);
            if (val) {
                var name = f.mapping ? f.mapping : f.name;
                res += encodeURIComponent(name) +":" + encodeURIComponent(val)+",";
            }
        }
        return res;
    }
});

CQ.security.data.AuthRecord.arrayConverter = function(value) {
    if (CQ.Ext.isArray(value)) {
        var res = new Array();
        for (var i=0;i<value.length;i++){
            var rel = value[i]
            if (!rel.id) {
                continue;
            }
            var rec = new CQ.security.data.AuthRecord({}, rel.id);
            for (var j=0;j<CQ.security.data.AuthRecord.FIELDS.length;j++) {
                var f = CQ.security.data.AuthRecord.FIELDS[j];
                var val = rel[f.name];
                if (val) {
                    rec.set(f.name, val);
                }
            }
            res.push(rec);
        }
        return res;
    }
}

/**
 * Field Name containing the Privilege
 * @static
 * @final
 * @private
 * @type String
 */
CQ.security.data.AuthRecord.PRIVILEGE_FIELD = "cq:privileges";

/**
 * Field definition for Authorizables
 * @static
 * @final
 * @private
 * @type Array
 */
CQ.security.data.AuthRecord.FIELDS = [
    {"name": "id"},
    {"name": "type"},
    {"name": "name"},
    {"name": "email"},
    {"name": "home"},
    {"name": "givenName"},
    {"name": "familyName"},
    {"name": "aboutMe"},
    {"name": "rep:userId"},
    {"name": "replication"},
    {"name": "modification"},
    {"name": "memberOf", convert: CQ.security.data.AuthRecord.arrayConverter},                //todo: add converter array to rec-array
    {"name": "members", convert: CQ.security.data.AuthRecord.arrayConverter},                 //todo: add converter
    {"name": "sudoers", convert: CQ.security.data.AuthRecord.arrayConverter},                //todo: add converter array to rec-array
    {"name": CQ.security.data.AuthRecord.PRIVILEGE_FIELD}
];
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * Spezialized store the access current Users access reights to given pathes.
 * Store allowes to load Acls on demand. E.g if permissions are checked while browsing
 * through a tree.
  *
 * @class CQ.Ext.security.data.UserAclStore
 * @extends CQ.secuzrity.data.Store
 * convenience to Access ACL as AclRecords
 * @constructor
 * Creates a new Store.
 * @param {Object} config A config object containing the objects needed for the Store to access data,
 * and read the data into Records.
 */
CQ.security.data.UserAclStore = CQ.Ext.extend(CQ.security.data.AclStore,{

    user: null,

    url: null,

    constructor:function(config) {
        if (!config) {
            config = {};
        }
        if (!config.reader) {
            config.reader = new CQ.Ext.data.JsonReader({
                root:"acl",
                id: "path",
                totalProperty: "aces"},
                    CQ.security.data.AclRecord.create())
        }
        this.user = CQ.User.getUserID();
        this.url = config.dataUrl ? config.dataUrl : this.user.getHome() + ".permissions" + CQ.HTTP.EXTENSION_JSON;
        CQ.security.data.AclStore.superclass.constructor.call(this, config);
    },

    /**
     * Overrides the Sotrees implemenation, in order to load acls for pathes
     * by demand.
     *
     * @param path
     * @see CQ.Ext.data.Store#getById()
     */
    getById: function(path) {
        var rec = this.data.key(path);
        if (!rec) {
            //todo: remember misses to avoid mutliple requests for empty reponses
            var data = this.requestData(path);
            if (data) {
                this.loadData(data, true);
                return this.data.key(path);
            }
        }
        return rec;
    },

    /**
     * @private
     */
    requestData:function(contentPath) {
        var idx = contentPath.lastIndexOf("/");
        if (idx<0) {
            return;
        }
        var parent;
        if (idx==0) {
            parent = "/";
        } else {
            parent = contentPath.substring(0,idx);
        }
        var url = CQ.HTTP.addParameter(this.url, "path", parent);
        url = CQ.HTTP.noCaching(url);
        var res = CQ.HTTP.get(url);
        if (CQ.HTTP.isOkStatus(res.status)) {
            return CQ.Util.eval(res);
        }
    }
});/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.UserAdmin</code> class provides the admin console for 
 * user administration.
 * @class
 * @extends CQ.Ext.Viewport
 */
CQ.security.FilterField = CQ.Ext.extend(CQ.Ext.form.TwinTriggerField, {
    width:200,

    hideTrigger1:true,

    validationEvent:false,

    validateOnBlur:false,

    trigger1Class:"x-form-clear-trigger",

    trigger2Class:"x-form-search-trigger",

    emptyText:CQ.I18n.getMessage("Enter filter query"),

    paramName:"filter",

    hasFilter:false,

    loadParams:{},


    constructor: function(config) {
       config = CQ.Util.applyDefaults(config, {"loadParams":{}});
       this.loadParams = config.loadParams;
       CQ.security.FilterField.superclass.constructor.call(this, config); 
    },

    initComponent: function() {
        CQ.security.FilterField.superclass.initComponent.call(this);

        if (!this.store.baseParams) {
            this.store.baseParams = {};
        }
        this.store.baseParams[this.paramName] = "";

        this.on("specialkey", function(f, e) {
            if (e.getKey() == e.ENTER) {
                this.onTrigger2Click();
            }
        }, this);
    },

    onTrigger1Click: function() {
        if (this.hasFilter) {
            this.store.baseParams[this.paramName] = "";
            this.store.reload({"params":this.loadParams});
            this.el.dom.value = "";
            this.triggers[0].hide();
            this.hasFilter = false;
            this.focus();
        }
    },

    onTrigger2Click: function() {
        var value = this.getRawValue();
        if (value.length < 1) {
            this.onTrigger1Click();
            return;
        }
        this.store.baseParams[this.paramName] = value;
        this.store.reload({"params":this.loadParams});
        this.hasFilter = true;
        this.triggers[0].show();
        this.focus();
    }
});
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.security.UserAdminPanel</code> defines the Interface to tbe used
 * for communication between the UserAdmin and its dependent Widgets.
 * It is dependent on the Authorizables selected in UserAdmin.
 * While the UserAmin is dependent on the changes made by this Widget.
 * Changes to this dependenceis are communicated via Events:
 * The Admin notifies about changes to the selection on calling the
 * {@link #onSelectionChange} function.<br>
 * This Widgest sends events when a change happend and when the change has been
 * persisted.<br>
 * The Events communicate via {@link AuthRecords}. Thus providing decalartion
 * of known properties.
 *
 * @class
 */

CQ.security.UserAdminPanel = {

    /**
     * @cfg {Store} store containing all Authroizables selected in the UserAdmin
     */
    selectionStore: null,

    /**
     * @cfg {String} either CQ.security.UserAdminPanel.TYPE_GROUP or CQ.security.UserAdminPanel.TYPE_USER.
     * @final 
     */
    authType: null,

    /**
     * Creates a new <code>CQ.security.UserAdminPanel</code> instance.
     *
     * @constructor
     * @param {Object} config The config object
     */
    constructor: function(config) {
        this.authType = config.authType;
        CQ.security.UserAdminPanel.constructor.superclass.constructor.call(this, config);
    },

    /**
     * @return {Store} get the store currently in use, contains AuthRecord
     * @see AuthRecord
     */
    getSelectionStore: function() {
        return this.selectionStore ? this.selectionStore : this.initialConfig.selectionStore;
    },

    /**
     * Initializes the editor.
     * @see CQ.Ext.Component#initComponent
     */
    initComponent: function() {
        CQ.security.UserAdminPanel.constructor.superclass.initComponent.call(this);

        this.addEvents(
            /**
             * @event authChanged
             * Fires to notify listeners that at least one of the
             * Authorizable set as selected, has been edited.
             * @param {UserAdminPanel} this
             * @param {AuthRecord} rec
             */
             "authChanged",

            /**
             * @event authSaved
             * Fires to notify listeners that an Authorizable has been saved.
             * @param {UserAdminPanel} this
             * @param {AuthRecord} rec
             * @param {String} field
             */
              "authSaved"
            );

        this.on({'activate':{fn:this.activationHandler}})
    },

    /**
     * Abstract method to be implemented.
     * Will be called if the UserAdmin's selection changed. 
     * @param {Store} store containing all currently selected Records 
     * @param rec
     */
    onSelectionChanged:function(store,  rec) {
    },

    /**
     * Abstract method to be implemented.
     * Will be called if the UserAdmin's selection changed.
     * @param {Store} store containing all currently selected Records
     * @param {AuthRecord / Array} of the records which have been modified while
     */
    onSelectionModfied:function(rec) {
    },

    /**
     * Abstract method to be implemented.
     * Will be called if this Widget has been activated
     * todo: check if registration to parent could be done in intComponent
     * @param {Store} store containing all currently selected Records
     * @param the containing panel
     */
    onActivate:function(selection, panel){
    },

    activationHandler:function(panel) {
      this.onActivate(this.getSelectionStore(), panel);  
    },

    selectionHandler:function(store, rec) {
        this.selectionStore = store;
        this.onSelectionChanged(store, rec);
    }
};

/**
 * @final
 */
CQ.security.UserAdminPanel.TYPE_GROUP="group";

/**
 * @final
 */
CQ.security.UserAdminPanel.TYPE_USER="user"
/*
 * Copyright 1997-2009 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
/**
 * @class CQ.security.AuthorizableSelection
 * @extends CQ.Ext.form.ComboBox
 *
 * A specialized {@link CQ.Ext.form.ComboBox ComboBox} to select and search
 * Authorizables.<p>
 * Initalizes with a default set-up of an {@link CQ.Ext.data.Store Store}
 * to access CQ.
 * The Store can be filtered by the type of Authorizables. The filter can be used
 * to let the Store either onyl conain Users or only Groups. The filter can be
 * set up via configuration.
 *
 * @constructor
 * @param {Object} config The configuration object
 *
 * @cfg {Object} storeConfig config for a store {@link CQ.Ext.data.Store} (defaults
 *               to store reading authorizables with an {@link CQ.security.data.AuthReader Authorizable Reader})
 * @cfg {String} filter can have either the value "groups" or "users".
 *               If set to groups only, Groups are searched, "users" only
 *               searches for Users. (defaults to none which means both are searched)
 * @cfg {Boolean} stateful if true the Component can make use of a State managed
 *                by {@link CQ.Ext.state.Manager Ext} (defaults to false)
 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels
 *              (defaults to 200, will be ignored if listWidth has a higher value)
 * @cfg {String} queryParam Name of the query as it will be passed on the querystring
 *              (defaults to 'filter')
 */
CQ.security.AuthorizableSelection = CQ.Ext.extend(CQ.Ext.form.ComboBox, {

    filterButtons: false,

    storeUrl: "/bin/security/authorizables.json",

    storeLimit: 25,

    filter: null,

    constructor:function(config) {

        CQ.Util.applyDefaults(config,{
            "displayField":"name",
            "stateful":false,
            "minChars":0,
            "minListWidth":200,
            "queryParam":"filter",
            "tpl" :new CQ.Ext.XTemplate(
                    '<tpl for=".">',
                        '<div class="cq-auth-list">',
                            '<div class="cq-auth-list-entry {[values.type=="group"? "cq-group-icon": "cq-user-icon"]}">{[values.name==""? values.id: values.name]}</div>',
                        '</div>',
                    '</tpl>'),
            "itemSelector" :"div.cq-auth-list",
            "storeConfig":{
                "autoLoad":false,
                "proxy": new CQ.Ext.data.HttpProxy({
                    "url":this.storeUrl,
                    "method":"GET"
                }),
                "baseParams": {
                    "limit":this.storeLimit
                },
                "reader": new CQ.security.data.AuthReader()
            }
        });
        if (config.filter) {
            if ("groups"==config.filter) {
                config.filter = "hideUsers";
            } else if ("users"==config.filter) {
                config.filter = "hideUsers";
            } else if ("manual"==config.filter) {
                this.filterButtons = true;
            }
        }
        this.authStore = new CQ.Ext.data.Store(config.storeConfig);
        config.store = this.authStore;
        CQ.security.AuthorizableSelection.superclass.constructor.call(this, config);
    },

    /**
     * @method initComponent
     */
    initComponent: function() {
        CQ.security.AuthorizableSelection.superclass.initComponent.call(this);
        //todo: private: experimaental: add filter buttons
        if (this.filterButtons) {
            var combo = this;
            var toggle = function(button, pressed) {
                var storeFilter = combo.authStore.baseParams;
                var f = button.initialConfig.filter;
                if (f) {
                    storeFilter[f] = pressed;
                }
                delete combo.lastQuery;
            };
            this.hideUsers = new CQ.Ext.Button({
                "text":CQ.I18n.getMessage("Exclude Users"),
                "enableToggle":true,
                "toggleGroup":"authExclude-"+this.getId(),
                "pressed":true,
                "filter":"hideUsers",
                "tooltip":{
                    "title":CQ.I18n.getMessage("Exclude Users"),
                    "text":CQ.I18n.getMessage("Press to prevent users from being found"),
                    "autoHide":true
                },
                "toggleHandler": toggle
            });
            this.hideGroups = new CQ.Ext.Button({
                "text":CQ.I18n.getMessage("Exclude Groups"),
                "enableToggle":true,
                "toggleGroup":"authExclude-"+this.getId(),
                "filter":"hideGroups",
                "tooltip":{
                    "title":CQ.I18n.getMessage("Exclude Groups"),
                    "text":CQ.I18n.getMessage("Press to prevent groups from being found"),
                    "autoHide":true
                },
                "toggleHandler": toggle
            });
        }
        //todo: end of private: experimaental
        if (this.filter) {
            this.authStore.baseParams[this.filter] = "true";
        }
    },

    /**
     * @private
     */
    onRender: function(ct, pos) {
        CQ.security.AuthorizableSelection.superclass.onRender.call(this, ct, pos);

        //todo: private: experimaental
        if (this.filterButtons) {
            var b = new CQ.Ext.Toolbar({"hidden":true, "hideMode":"visibility"});
            b.render(this.wrap);
            var but = b.addButton([this.hideGroups, this.hideUsers]);
            var w = 0;
            for(var i=0;i<but.length;i++) {
                w+=but[i].getEl().getWidth();
            }
            b.setWidth(w);
            b.getEl().alignTo(b.getEl().prev(), "tr");
            this.wrap.setHeight(this.el.getHeight());
            b.show.defer(200, b);

            this.authStore.baseParams[this.hideUsers.initialConfig.filter] = true;
        }
        //todo: end of private: experimaental
    }
});

CQ.Ext.reg("authselection", CQ.security.AuthorizableSelection);
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.security.UserAdminGridPanel</code> implements a GridPanel, that
 * that implements the UserAdminPanel.
 *
 * @class
 * @extends GridPanel
 * @extends UserAdminPanel
 */
CQ.security.UserAdminGridPanel = CQ.Ext.extend(CQ.Ext.grid.GridPanel, CQ.security.UserAdminPanel);

/**
 * This Panel is to edit the Authorizables and a Set of Authorizables it has
 * a Relation to.
 * This kind of relations may be Group-Membership, Members, Sudoers.
 * The Grid allows drag&drop from the Autorhizable List.<br>
 * Posts changes to the Authorizables home path.
 *
 * @class
 * @extedns UserAdminGridPanel
 * todo: context-menus, add via button, click to open..., multi editing
 */
CQ.security.AuthRelationPanel = CQ.Ext.extend(CQ.security.UserAdminGridPanel , {

    /**
     * Defaults to multiple, changes actions-state according selection 
     * @cfg {config}
     * @see SelectionModel
     */
    sm:null,

    /**
     * Defaults to name and id hidden id column
     * @cfg {ColumnModule} cm ColumnModule to use for display defaults
     */
    cm:null,

    /**
     * @cfg {string} field name of the AuthRecord to take for the current grid
     */
    field:null,

    /**
     * defaults to false
     * @cfg {boolean} allowUserAdd if users may be added to the relation,
     */
    allowUserAdd:false,
    
    /**
     * {boolean} indicates that the panel is in sync with the selection of the UserAdmin
     * @private
     */
    loaded:false,

    /**
     * Sumbmit action for Grid 
     * @private
     * @type Action
     */
    saveAction:null,

    /**
     * Action to remove an entry from the list
     * @private
     * @type Action
     */
    removeAction:null,

    /**
     * @constructor
     */
    constructor: function(config)  {

        this.saveAction = new CQ.Ext.Action({
                text:CQ.I18n.getMessage("Save"),
                disabled:true,
                handler:this.saveHandler,
                scope:this,
                tooltip: {
                    title:CQ.I18n.getMessage("Save Changes"),
                    text:CQ.I18n.getMessage("Save changes of the profile"),
                    autoHide:true
                }
        });
        
        this.removeAction = new CQ.Ext.Action({
            text:CQ.I18n.getMessage("Remove"),
            handler:this.removeHandler,
            disabled:true,
            scope:this,
            tooltip: {
                title:CQ.I18n.getMessage("Remove"),
                text:CQ.I18n.getMessage("Removes selected items from the list"),
                autoHide:true
            }
        });

        var selCfg = CQ.Util.applyDefaults(config.sm,{
            singleSelect:false,
            listeners: {
                'rowselect':{
                  fn:function() {this.removeAction.enable()},
                  scope:this
                },
                'rowdeselect': {
                  fn:function() {this.removeAction.disable()},
                  scope:this
                }
            }
        });

        config.cm = new CQ.Ext.grid.ColumnModel((config.cm) ? config.cm :[
            {header:CQ.I18n.getMessage("Name"),dataIndex:"name", sortable:true},
            {header:CQ.I18n.getMessage("ID"),dataIndex:"id", sortable:true, hidden:true}
            ]);

        var def = {
            field:"memberOf",
            viewConfig:{ forceFit:true },
            tbar: [this.saveAction, "-", this.removeAction ]
        };
        config = CQ.Util.applyDefaults(config, def);

        var strCfg = CQ.Util.applyDefaults(config.relationStore, {
            reader: new CQ.Ext.data.ArrayReader({id:"id"},
                    CQ.security.data.AuthRecord.create()),
            autoload:false,
            listeners: {
               'add': {fn:this.onStoreChanged, scope:this},
               'remove': {fn:this.onStoreChanged, scope:this},
               'update': {fn:this.onStoreChanged, scope:this}
            }
        });

        config.store = new CQ.Ext.data.Store(strCfg);
        config.sm = new CQ.Ext.grid.RowSelectionModel(selCfg);

        CQ.security.AuthRelationPanel.superclass.constructor.call(this, config);
    },

    initComponent: function() {
        this.on("render", function(grid) {
            new CQ.security.AuthRelationPanel.DropTarget(grid.body, {
                store:this.getStore(),
                allowUser: this.initialConfig.allowUserAdd});
        });
        CQ.security.AuthRelationPanel.superclass.initComponent.call(this);
    },

    onActivate: function(store) {
        if(!this.loaded && store.getCount()>0) {
            this.reloadData(store);
        }
    },

    getField: function() {
        return this.initialConfig.field;
    },

    onSelectionChanged:function(store,  row) {
        this.loaded = false;
        var ownStore = this.getStore();
        ownStore.suspendEvents();
        ownStore.removeAll();
        ownStore.resumeEvents();
        if (!this.selectionStore) {
            this.selectionStore = store;
        }
    },

    onSelectionModified:function(rec) {
       this.getView().refresh(); 
    },

    onStoreChanged: function(store, action) {
        if (store.getCount()==0) {
            this.removeAction.disable();
        }
        if (CQ.Ext.data.Record.COMMIT!=action) {
            this.saveAction.enable();
        } else {
            this.saveAction.disable();
        }

        //notfiy the selected record changed
        this.fireEvent("authChanged", this, this.getSelectionStore().getAt(0));
    },

    //todo: other panels have to update.
    //todo: adapt on multi-selection
    saveHandler:function() {
        var curRec = this.getSelectionStore().getAt(0);
        var url = curRec.get("home");
        if (!url) {
            var id = curRec.get("id");
            if (!id) {
                CQ.MessageBox.alert(CQ.I18n.getMessage("Error"), CQ.I18n.getMessage("No Authorizable selected"));
                return;
            }
            url = CQ.Util.externalize("/bin/security/authorizables/POST");
            url = CQ.HTTP.addParameter(url, "Authorizable", id);
        } else {
            url = CQ.Util.externalize(url);
        }
        var all = new Array();
        var allRecs = new Array();
        var params = {
            "_charset_":"utf-8",
            "memberAction": this.getField(),
            "memberEntry":all};

        var st = this.getStore();
        var cnt = st.getCount();
        for (var i=0;i<cnt;i++) {
            var rec = st.getAt(i);
            all.push(encodeURIComponent(rec.id));
            allRecs.push(rec);
        }
        var pan = this;
        curRec.beginEdit();
        curRec.set(this.getField(), allRecs);
        CQ.HTTP.post(url, function(opt, succ){
            curRec.endEdit();
            if (succ) {
                pan.commit(curRec)
            } },
                params, this);
    },

    removeHandler: function() {
        var recs = this.getSelectionModel().getSelections();
        for(var i=0;i<recs.length;i++) {
            this.getStore().remove(recs[i]);
        }
    },

    reloadData: function(store) {
        var ownStore = this.getStore();
        for (var i=0;i<store.getCount();i++) {
            var rec = store.getAt(i);
            var rel = rec.get(this.getField());
            if (rel && CQ.Ext.isArray(rel)) {
                ownStore.suspendEvents();
                ownStore.removeAll();
                ownStore.add(rel);
                ownStore.resumeEvents();
            }
        }
        this.onSelectionModified();
        this.loaded=true;
    },

    commit: function(rec) {
        this.getStore().commitChanges();
        this.saveAction.disable();
        var selStore = this.getSelectionStore();
        if (selStore.getCount()>1) {
            selStore.reload();
        }
        this.fireEvent("authSaved", this, rec, this.getField());
    }
})

/**
 * The <code>CQ.security.Membership.DropTarget</code> class represents a drop
 * target for <code>CQ.security.Membership</code>.
 *
 * @class CQ.security.Membership.DropTarget
 * @extends CQ.Ext.dd.DropTarget
 */
CQ.security.AuthRelationPanel.DropTarget = CQ.Ext.extend(CQ.Ext.dd.DropTarget, {
    store: null,
    ddGroup:"AuthorizableDD",
    dropAllowed:"x-dd-drop-ok",
    copy:false,
    allowUser:false,

    constructor: function(el, config) {
        this.store= config.store;
        this.allowUser = config.allowUser;
        CQ.security.AuthRelationPanel.DropTarget.superclass.constructor.call(this, el, config);
    },

    /**
     * Handles when the source is dropped on this drop target.
     *
     * @see CQ.Ext.dd.DropTarget#notifyDrop
     * @public
     */
    notifyDrop: function(dragSource, e, data) {
        if (!this.isAllowed(data)) {
            return false;
        }
        var sel = data.selections;
        if (sel.length) {
            var added = [];
            for(var i=0;i<sel.length;i++) {
                var rec = sel[i];
                added.push(new CQ.security.data.AuthRecord(
                {name:rec.get("name"),id:rec.id},
                        rec.id))
            }
            this.store.add(added);
        }
        return true;
    },

    /**
     * Handles when the source is over this drop target.
     *
     * @see CQ.Ext.dd.DropTarget#notifyOver
     * @public
     */
    notifyOver : function(dragSource, e, data) {
        return this.isAllowed(data) ? this.dropAllowed : this.dropNotAllowed;
    },

    isAllowed:function(data) {
       var allowed = true;
        var recs = data.selections;
        for (var i=0;i<recs.length && allowed;i++) {
            var type= recs[i].get("type");
            allowed = type=="group" || this.allowUser;
        }
        return allowed;
    }
});


/*
 * Copyright 1997-2009 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
CQ.security.AuthorizableList = CQ.Ext.extend(CQ.Ext.grid.GridPanel, {

    authStore:null,

    constructor: function(config) {

        var storeConfig = CQ.Util.applyDefaults(config.store, {
            "storeId":"cq-useradmin-authstore",
            "autoLoad":true,
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":"/libs/security/components/authquery.json",
                "method":"GET"
            }),
            "baseParams": {
                "limit":25
            },
            "reader": new CQ.security.data.AuthReader()
        });
        var authStore = new CQ.Ext.data.Store(storeConfig);
        this.authStore = authStore;
        // actions
        var actions = [];
        var disabledActions = [];
        var ctxActions = [];

        var hideUsers = new CQ.Ext.Action({
            "id":"cq-useradmin-hideUsers",
            "text":CQ.I18n.getMessage("Hide Users"),
            "enableToggle":true,
            "toggleGroup":"hide",
            "tooltip":{
                "title":CQ.I18n.getMessage("Hide Users"),
                "text":CQ.I18n.getMessage("Press to prevent users from being listed"),
                "autoHide":true
            },
            "toggleHandler": function(button, pressed) {
                authStore.baseParams["hideUsers"] = pressed;
                authStore.reload();
            }
        });
        var hideGroups = new CQ.Ext.Action({
            "id":"cq-useradmin-hideGroups",
            "text":CQ.I18n.getMessage("Hide Groups"),
            "enableToggle":true,
            "toggleGroup":"hide",
            "tooltip":{
                "title":CQ.I18n.getMessage("Hide Groups"),
                "text":CQ.I18n.getMessage("Press to prevent groups from being listed"),
                "autoHide":true
            },
            "toggleHandler": function(button, pressed) {
                authStore.baseParams["hideGroups"] = pressed;
                authStore.reload();
            }
        });
        var filter = new CQ.security.FilterField({
            "store":authStore,
            "loadParams":{"start":0}
        });
        actions.push(filter);
        if (!config.hideFilter) {
            actions.push(" ");
            actions.push(hideUsers);
            actions.push(" ");
            actions.push(hideGroups);

            actions.push("->");
            actions = actions.concat(
                    this.formatActions(config.actions, disabledActions, ctxActions));
        }

        // authorizable list (grid) config
        var cm = config.columnModel ? config.columnModel : new CQ.Ext.grid.ColumnModel([{
            "header":CQ.I18n.getMessage("Type"),
            "dataIndex":"type",
            "width":30,
            "fixed":true,
            "resizable":false,
            "hideable":false,
            "renderer":CQ.security.AuthorizableList.renderIcon
        },{
            "header":CQ.I18n.getMessage("Id"),
            "width":150,
            "dataIndex":"id"
        },{
            "header":CQ.I18n.getMessage("Name"),
            "dataIndex":"name"
        }, {
            "width":48,
            "header":CQ.I18n.getMessage("Pub.", null, "Abbreviation of the word published"),
            "renderer": function(v, params, record) {
                var clazz = "";
                var title = " title=\"";
                var repl = record.get("replication");
                if (repl && repl.published) {
                    if (repl.numQueued > 0) {
                        clazz = "status-pending";
                        if (repl.action == "ACTIVATE") {
                            title += CQ.I18n.getMessage("Activation pending. #{0} in Queue.", repl.numQueued);
                        } else {
                            title += CQ.I18n.getMessage("Deactivation pending. #{0} in Queue.", repl.numQueued);
                        }
                    } else {
                        title += CQ.Ext.util.Format.date(new Date(repl.published));
                        title += " (" + repl.publishedBy + ")";
                        if (repl.action == "ACTIVATE") {
                            clazz = "status-activated";
                        } else {
                            clazz = "status-deactivated";
                        }
                    }
                }
                title += "\"";
                return "<div" + title + " class=\"status " + clazz + "\">&nbsp;</div>";
            }
        },{
            "width":48,
            "header":CQ.I18n.getMessage("Mod.", null, "Abbreviation of the word modified"),
            "renderer": function(v, params, record) {
                var repl = record.get("replication");
                var lastMod = record.get("modification");
                var title = " title=\"";
                var clazz = "";
                if (lastMod.lastModified) {
                    title += CQ.Ext.util.Format.date(new Date(lastMod.lastModified));
                    title += " (" + lastMod.lastModifiedBy + ")";
                    clazz = "status-localmodified";
                }
                if (repl && repl.published) {
                    if (repl.action == "ACTIVATE") {
                        if (lastMod.lastModified > repl.published) {
                            clazz = "status-modified";
                        }
                    }
                }
                title += "\"";
                return "<div "+ title +" class=\"status " + clazz + "\">&nbsp;</div>";
            }
        }
        ])
        cm.defaultSortable = true;

        var sm = config.selectionModel ? config.selectionModel : new CQ.Ext.grid.RowSelectionModel({
            "singleSelect":false,
            "listeners": {
                "selectionchange": function(sm) {
                    for (var i = 0; i < disabledActions.length; i++) {
                        var disabled = !sm.hasSelection();
                        var act = disabledActions[i];
                        act.setDisabled(disabled);
                        if (!disabled && act instanceof CQ.PrivilegedAction) {
                            var sels = sm.getSelections();
                            for (var j = 0; j < sels.length; j++) {
                                act.setPath(sels[j].get("home"));
                                if (act.isDisabled()) {
                                    break; // starts disabled so at least one check
                                }
                            }
                        }
                    }
                }
            }
        });

        var listeners = CQ.Util.applyDefaults(config.listeners, {
            "rowcontextmenu": function(grid, index, e) {
                if (!this.contextMenu && (ctxActions.length > 0)) {
                    this.contextMenu = new CQ.Ext.menu.Menu({
                        items:ctxActions
                    });
                }
                var xy = e.getXY();
                this.contextMenu.showAt(xy);
                e.stopEvent();
            },
            "keypress": function(e) {
                if (e.getCharCode() == e.DELETE) {
                    if (this.getSelections() && this.getSelections().length > 0) {
                        this.removeHandler()
                        e.stopEvent();
                    }
                }
            }
        });

        config = CQ.Ext.applyIf(config, {
            "autoExpandColumn":"id",
            "region":"west",
            "margins":"5 0 5 5",
            "collapsible":true,
            "collapseMode":"mini",
            "width":400,
            "minWidth":380,
            "split":true,
            "loadMask":true,
            "enableDragDrop":true,
            "ddGroup":"AuthorizableDD",
            "ddText":"{0} selected Authorizable(s)",
            "tbar":actions,
            "store":this.authStore,
            "cm":cm,
            "sm":sm,
            "viewConfig": {
                "forceFit":true
            },
            "listeners":listeners,
            "bbar": new CQ.Ext.PagingToolbar({
                "store":authStore,
                "pageSize":25,
                "stateful":false,
                "displayMsg": CQ.I18n.getMessage("Page {0} of {1}")
            })})

        // init component by calling super constructor
        CQ.security.AuthorizableList.superclass.constructor.call(this, config);
    },

    initComponent : function() {
        CQ.security.AuthorizableList.superclass.initComponent.call(this);

        this.addEvents({
            /**
             * @event authremoved
             * Fires to notify listeners that an Authorizable was deleted.
             * @param {CQ.security.AuthorizableList} This List
             * @param {CQ.Ext.data.Record} The deleted Record representing the Authorizable
             * @param {Number} the formar index of the record
             */
            "authremoved":true
        })

        this.authStore.on("remove", this.fireAuthRemoved, this);
    },

    updateRelation: function(rec, field) {
        var newVal = rec.get(field);
        if (newVal && CQ.Ext.isArray(newVal)) {
            var str = this.getStore();
            var rel = (field == "members") ? "memberOf" : "members";
            str.each(function(curRec/*, scope*/) {
                var shouldCont = false;
                for (var i = 0; i < newVal.length; i++) {
                    if (newVal[i].id == curRec.id) {
                        shouldCont = true;
                        break;
                    }
                }
                var val = curRec.get(rel);
                if (val && CQ.Ext.isArray(val)) {
                    for (var j = 0; j < val.length; j++) {
                        if (val[j].id == rec.id) {
                            if (!shouldCont) {
                                val.splice(j, 1);
                            }
                            return;
                        }
                    }
                }
                if (shouldCont) {
                    if (!val) {
                        val = new Array();
                        curRec.set(rel, val);
                    }
                    val.push(rec);
                }
            });
        }
    },

    // private
    formatActions: function(actionCfgs, disabledActions, ctxActions) {
        var actions = [];
        for (var a in actionCfgs) {
            if (typeof(actionCfgs[a]) != "object") {
                continue;
            }
            // check for separators, splits, ...
            if (actionCfgs[a].xtype == "separator") {
                actions.push(actionCfgs[a].value);
                if (actionCfgs[a].ctx) {
                    ctxActions.push(actionCfgs[a].value);
                }
            } else {
                if (actionCfgs[a].menu) {
                    actionCfgs[a].menu = new CQ.Ext.menu.Menu({
                        items:this.formatActions(actionCfgs[a].menu,
                                disabledActions, ctxActions)
                    });
                }
                var actionCfg = this.formatActionConfig(actionCfgs[a]);
                var action;
                if (actionCfg.privileges || actionCfg.conditions) {
                    action = new CQ.PrivilegedAction(actionCfg);
                } else {
                    action = new CQ.Ext.Action(actionCfg);
                }
                actions.push(action);

                if (actionCfg.disabled) {
                    disabledActions.push(action);
                }
                if (actionCfg.ctx) {
                    ctxActions.push(action);
                }
            }
        }
        return actions;
    },

    activationHandler:function() {
        var sm = this.getSelectionModel()
        var recs = sm.getSelections();
        var store = this.getStore();
        var msg = "<ul>";
        for (var i = 0; i < recs.length; i++) {
            msg = msg + "<li>" + recs[i].get("name");
        }
        msg += "</ul>";
        var actFunc = function(but) {
            if (but == "yes") {
                var path = new Array();
                for (var i = 0; i < recs.length; i++) {
                    var rec = recs[i];
                    var home = rec.get("home");
                    path.push(home);
                    path.push(home + "/cq:authorizable");
                }
                var cb = function() {
                    sm.clearSelections();
                    CQ.Notification.notify(CQ.I18n.getMessage("Activated"), msg);
                    store.reload.defer(1000, store);
                };
                this.requestReplication(path, "Activate", cb);
            }
        }
        CQ.Ext.MessageBox.confirm(CQ.I18n.getMessage("Activate"),
                CQ.I18n.getMessage("Do you really want to activate: {0}", msg),
                actFunc,
                this)
    },

    deactivationHandler:function() {
        var sm = this.getSelectionModel()
        var recs = sm.getSelections();
        var store = this.getStore();
        var msg = "<ul>";
        for (var i = 0; i < recs.length; i++) {
            msg = msg + "<li>" + recs[i].get("name");
        }
        msg += "</ul>";
        var actFunc = function(but) {
            if (but == "yes") {
                var path = new Array();
                for (var i = 0; i < recs.length; i++) {
                    var rec = recs[i];
                    var home = rec.get("home");
                    path.push(home);
                    path.push(home + "/cq:authorizable");
                }
                var cb = function() {
                    sm.clearSelections();
                    CQ.Notification.notify(CQ.I18n.getMessage("Deactivated"), msg);
                    store.reload.defer(1000, store);
                }
                this.requestReplication(path, "DeActivate", cb);
            }
        }
        CQ.Ext.MessageBox.confirm(CQ.I18n.getMessage("Deactivate"),
                CQ.I18n.getMessage("Do you really want to deactivate: {0}", msg),
                actFunc,
                this)
    },

    removeHandler:function() {
        var st = this.getStore();
        var sm = this.getSelectionModel()
        var recs = sm.getSelections();
        var msg = "<p>";
        var list = this;
        for (var i = 0; i < recs.length; i++) {
            msg = msg + recs[i].get("name") + "<br>"
        }
        var delFunc = function(but) {
            if (but == "yes") {
                for (var i = 0; i < recs.length; i++) {
                    var rec = recs[i];
                    var params = {
                        "_charset_":"utf-8",
                        "deleteAuthorizable": rec.id};
                    CQ.HTTP.post(rec.get("home"),
                            function(options, success, xhr) {
                                var response = CQ.HTTP.buildPostResponseFromHTML(xhr.responseText);
                                if (CQ.utils.HTTP.isOk(response)) {
                                    list.requestReplication(rec.get("home"), "DeActivate");
                                    st.remove(rec);
                                    st.commitChanges();
                                    CQ.Notification.notify(CQ.I18n.getMessage("Deleted"), rec.get("name"));
                                }
                            },
                            params,
                            rec);
                }
            }
        }
        CQ.Ext.MessageBox.confirm(CQ.I18n.getMessage("Delete"),
                CQ.I18n.getMessage("Do you really want to delete: {0}", msg),
                delFunc,
                this)
    },

    requestReplication: function(path, cmd, callback) {
        var params = {
            "_charset_":"utf-8",
            "path": path,
            "cmd":cmd
        };
        CQ.HTTP.post("/bin/replicate.json",
                function(options, success, xhr) {
                    var response = CQ.HTTP.buildPostResponseFromHTML(xhr.responseText);
                    if (CQ.HTTP.isOk(response) && callback) {
                        callback.call(this);
                    }
                },
                params,
                this);
    },

    // private
    formatActionConfig: function(config) {
        if (!config.scope) {
            config.scope = this;
        }
        if (typeof(config.handler) == "string") {
            config.handler = eval(config.handler);
        }
        if (config.text) {
            config.text = CQ.I18n.getMessage(config.text);
        }
        if (config.tooltip && config.tooltip.text) {
            config.tooltip.text = CQ.I18n.getMessage(config.tooltip.text);
        }
        if (config.tooltip && config.tooltip.title) {
            config.tooltip.title = CQ.I18n.getMessage(config.tooltip.title);
        }
        return config;
    },

    fireAuthRemoved: function(store, record, index) {
        this.fireEvent("authremoved", this, record, index)
    }
});

CQ.security.AuthorizableList.renderIcon = function(value) {
    if (value == "user") {
        return '<div class="userIcon">&nbsp;</div>';
    } else if (value == "group") {
        return '<div class="groupIcon">&nbsp;</div>';
    }
}

CQ.Ext.reg("authorizablelist", CQ.security.AuthorizableList);/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.PrivilegeEditor = CQ.Ext.extend(CQ.Ext.Panel, {

    authorizableId:null,
        
    removed:new Array(),
    
    constructor: function(config) {
        //--------------------------------------------< config >----------------
        if (!config) {
            config={};
        }

        var cfg = CQ.Util.applyDefaults(config, {
            "layout":"border",
            "bodyStyle":"padding:0px;",
            "labelWidth":75,
            "anchor":"100% 100%",
            "autoShow":true,
            "privilege":"wcm/privileges/replicate",
            "title": CQ.I18n.getMessage("Replication Privilege"),
            "dataPath":CQ.Util.externalize('/bin/security/privileges'),
            "treePath":"/bin/tree/ext",
            "treeRoot": {
                "name":"",
                "text":"CQ",
                "draggable":false,
                "expanded":true,
                "singleClickExpand":true
             }});

        //--------------------------------------------< acl grid / store>-------
       this.aclStore=  new CQ.Ext.data.Store({
            "reader":new CQ.Ext.data.JsonReader({
                    "root":"acl",
                    "id":"path",
                    "totalProperty": "aces"
                },
                CQ.security.PrivilegeEditor.RECORD),
            "autoload":false,
            "listeners": {
               'add': {fn:this.storeChanged, scope:this},
               'remove': {fn:this.storeChanged, scope:this},
               'update': {fn:this.storeChanged, scope:this}
            },
            "baseParams":{"_charset_":"utf-8"},
            "proxy": new CQ.Ext.data.HttpProxy({
                    "url":cfg.dataPath+CQ.HTTP.EXTENSION_JSON+"/"+cfg.privilege,
                    "method":"GET"})});
        this.grid = new CQ.Ext.grid.EditorGridPanel( {
            "tbar":[{
                    "text":CQ.I18n.getMessage("Add") ,
                    "tooltip":CQ.I18n.getMessage("Add Permission"),
                    "handler":this.addACE,
                    "scope":this
                },{
                    "id":"cq-useadmin-acleditor-remove",
                    "text":CQ.I18n.getMessage("Remove"),
                    "tooltip":CQ.I18n.getMessage("Remove Permission"),
                    "handler":this.removeACE,
                    "disabled":true,
                    "scope":this
                },"-", {
                    "id":"cq-useadmin-acleditor-save",
                    "text":CQ.I18n.getMessage("Save"),
                    "handler":this.saveHandler,
                    "disabled":true,
                    "title":CQ.I18n.getMessage("Save Changes"),
                    "tooltip":CQ.I18n.getMessage("Save changes of the profile"),
                    "scope":this
                }
            ],   //todo: context-menu
            "autoExpandColumn":"Id",
            "region":"center",
            "store":this.aclStore,
            "loadMask":true,
            "cm":new CQ.Ext.grid.ColumnModel([
                {
                    "header":CQ.I18n.getMessage("Path"),
                    "dataIndex":"path"
                },{
                "header":CQ.I18n.getMessage("Allowed"),
                "dataIndex":"deny",
                "editor": new CQ.Ext.form.ComboBox({
                    "allowBlank": false,
                    "mode":'local',
                    "triggerAction": 'all',
                    "selectOnFocus":true,
                    "store": [
                        ["allow",CQ.I18n.getMessage("allow")],
                        ["deny",CQ.I18n.getMessage("deny")]
                    ]
                })
            },{
                "header":CQ.I18n.getMessage("Authorizable"),
                "dataIndex":"authorizable"
            }
            ]),
            "viewConfig": {
                "forceFit":true
             },
            "sm": new CQ.Ext.grid.RowSelectionModel({
                "singleSelect":true,
                "listeners": {
                    'rowselect': {"fn":this.rowSelection, "scope":this},
                    'rowdeselect': {"fn":this.rowDeselection, "scope":this}
                }
            }),
            "frame":false,
            "autoExpand":true,
            "autoShow":true
        });

        this.tree = new CQ.Ext.tree.TreePanel({
            "region":"west",
            "title":" ",
            "collapsible":true,
            "collapseMode":"mini",
            "collapsed":true,
            "autoScroll":true,
            "width":200,
            "stateful":false,
            "listeners": {
                "dblclick": {
                    "fn": this.selectionAction,
                    "scope":this
                }
            },
            "root":new CQ.Ext.tree.AsyncTreeNode(cfg.treeRoot),
            "loader":new CQ.Ext.tree.TreeLoader({
                "baseParams": { "predicate":"folder" },
                "requestMethod":"GET",
                "dataUrl": CQ.Util.externalize(cfg.treePath)+CQ.HTTP.EXTENSION_JSON+"?predicate=hierarchy",
                "baseAttrs": { "singleClickExpand":true },
                "listeners": {
                    "beforeload": function(loader, node) {
                        this.baseParams.path = node.getPath();
                    }
                }
            })
        });


        //--------------------------------------------< combination >-----------
        //|------------------------------|
        //| tree  |   acl view           |
        //|------------------------------|

        cfg.items= [this.tree, this.grid];
        CQ.security.PrivilegeEditor.superclass.constructor.call(this, cfg);
    },

    /**
     * Handler for Tree-Node selection:
     * Creates a new Record and inserts at the end of the grid.
     * Closes the tree.
     *
     * @param {TreeNode} node selected
     * @private
     */
    selectionAction:  function(node) {
        var path = node.getPath();
        this.grid.stopEditing();
        var rec = this.aclStore.getById(path);
        var idx;
        if (rec) {
            idx = this.aclStore.indexOf(rec);
        } else {
            rec = new CQ.security.PrivilegeEditor.RECORD({path:path, deny:"allow", authorizable:" "});
            rec.set("authorizable",this.authorizableId);
            idx = this.aclStore.getCount();
            this.aclStore.insert(idx, [rec]);
        }
        this.grid.startEditing.defer(150,this.grid,[idx,1]);
        this.tree.collapse(false);
    },

    saveHandler: function(){
        var mod = new Array();
        var params = {
            "_charset_":"utf-8",
            "Authorizable":this.authorizableId,
            "modifiedPath":[],
            "deletedPath":[]
        };

        var st = this.aclStore;
        var cnt = st.getCount();
        for (var i=0;i<cnt;i++) {
            var rec = st.getAt(i);
            if (rec.dirty) {
                mod.push(rec);
                var val =rec.get("path")+":"+rec.get("deny");
                params.modifiedPath.push(encodeURIComponent(val));
            }
        };
        for (var j=0;j<this.removed.length;j++) {
            var rmRec = this.removed[j];
            var rm =rmRec.get("path")+":"+rmRec.get("deny");
            params.deletedPath.push(encodeURIComponent(rm));
        }

        var sBut = CQ.Ext.getCmp("cq-useadmin-acleditor-save");
        var url = this.initialConfig.dataPath + CQ.HTTP.EXTENSION_JSON + "/"+ this.initialConfig.privilege
        CQ.HTTP.post(url,
                function(){
                    sBut.disable();
                    for(var j=0;j<mod.length;j++) {
                        mod[j].commit();
                    }
                },
                params,
                this);
    },

    loadRecord: function(rec) {
        if (rec && rec.id) {
            this.authorizableId = rec.id;
            this.aclStore.baseParams.Authorizable = rec.id;
            this.aclStore.reload();
        } else {
            this.aclStore.removeAll();
        }
    },

    /**
     * remove all stored Permissions
     * @param rec to unload from the selection
     */
    unloadRecord:function(rec) {
        this.authorizableId = null;
        this.loadRecord(null);
    },

    rowSelection: function() {
      var rBut = CQ.Ext.getCmp("cq-useadmin-acleditor-remove");
      if (rBut) {
          rBut.enable();
      }
    },

    rowDeselection: function() {
      var rBut = CQ.Ext.getCmp("cq-useadmin-acleditor-remove");
      if (rBut) {
          rBut.disable();
      }
    },

    storeChanged: function(strore, rec, action) {
        var sBut = CQ.Ext.getCmp("cq-useadmin-acleditor-save");
        if (sBut && CQ.Ext.data.Record.COMMIT!=action) {
            sBut.enable();
        }
    },

    addACE: function() {
        this.tree.expand(true);
    },

    removeACE: function() {
        var rec = this.grid.getSelectionModel().getSelected();
        this.aclStore.remove(rec);
        this.removed.push(rec);
        if (this.aclStore.getCount()==0) {
            this.rowDeselection();
        }
    }
});

CQ.security.PrivilegeEditor.RECORD = CQ.Ext.data.Record.create([
    {name:"path"},
    {name:"deny"},
    {name:"authorizable"}]);
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.Privileges = CQ.Ext.extend(CQ.Ext.Panel, {

    userForm: null,

    record: null,

    constructor: function(config) {
        this.userForm = new CQ.Ext.form.FormPanel({
            "baseParams": {
                "_charset_":"utf-8",
                "privilege": CQ.security.Privileges.MODIFY_HIERARCHY
            },
            "border":false,
            "bodyStyle":"padding:5px;",
            "labelWidth":180,
            "anchor":"100% 80%",
            "hidden":false,
            "items":[{
                "fieldLabel": CQ.I18n.getMessage("Hierarchy Modification"),
                "hiddenName":CQ.security.Privileges.MODIFY_HIERARCHY,
                "xtype":"combo",
                "allowBlank":true,
                "mode":"local",
                "width":100,
                "triggerAction":"all",
                "selectOnFocus":true,
                "store": [
                    [ "allow", CQ.I18n.getMessage("Grant")],
                    [ "deny", CQ.I18n.getMessage("Deny")]
                ]
            }],
            "tbar":[]
        });

        var tb = this.userForm.getTopToolbar();
        tb.push({
            "text":CQ.I18n.getMessage("Save"),
            "tooltip": {
                "title":CQ.I18n.getMessage("Save Changes"),
                "text":CQ.I18n.getMessage("Save changes of the profile"),
                "autoHide":true
            },
            "handler":this.saveHandler,
            "scope":this
        });
        CQ.Util.applyDefaults(config, {
            "layout":"form",
            "bodyStyle":"padding:0px;",
            "title":CQ.I18n.getMessage("Privileges"),
            "items": [this.userForm]
        });
        CQ.security.UserProperties.superclass.constructor.call(this, config);
    },

    loadRecord: function(rec) {
        if (this.userForm.rendered) {
            //search for the privilege and set to form
            var privs = rec.get(CQ.security.data.AuthRecord.PRIVILEGE_FIELD);
            var allowed = "deny";
            for (var i=0;i<privs.length;i++) {
                if (privs[i]==CQ.security.Privileges.MODIFY_HIERARCHY) {
                    allowed="allow"; break;
                }
            }
            var frm = this.userForm.getForm();
            frm.setValues([
                {id:CQ.security.Privileges.MODIFY_HIERARCHY, value:allowed}]);
            frm.baseParams.Authorizable = rec.get("id");
            this.record = rec;

        // defer setting of value untill form is completely rendered    
        } else {
            var privPanel=this;
            this.userForm.on("afterlayout", function() {
                privPanel.loadRecord(rec);
            })
        }
    },

    /**
     * @param rec to unload
     */
    unloadRecord: function(rec) {
        var frm = this.userForm.getForm();
        frm.reset();
        frm.baseParams.Authorizable = null;
    },

    saveHandler: function() {
        var frm = this.userForm.getForm();
        if (!frm.baseParams.Authorizable) {
            return;
        }
        var url = CQ.Util.externalize("/libs/security/privileges/simple")
        var action = new CQ.form.Action.Submit(frm, {
            "clientValidation":false,
            "url":url,
            "success":function(form) {
                if (this.record) {
                    var currVals = form.getValues(false);
                    if (currVals) {
                        var newVal = new Array();
                        var savedVal = this.record.get(CQ.security.data.AuthRecord.PRIVILEGE_FIELD);
                        for (var i=0;i<savedVal.length;i++) {
                            if(savedVal[i]!=CQ.security.Privileges.MODIFY_HIERARCHY) {
                                newVal.push(savedVal[i]);
                            }
                        }
                        if (currVals[CQ.security.Privileges.MODIFY_HIERARCHY]=="allow") {
                            newVal.push(CQ.security.Privileges.MODIFY_HIERARCHY);
                        }
                        this.record.set(CQ.security.data.AuthRecord.PRIVILEGE_FIELD, newVal);
                    }
                }
                CQ.Notification.notify(CQ.I18n.getMessage("OK"),CQ.I18n.getMessage("changes saved"));
            },
            "failure":function(form, action) {
                CQ.Notification.notify(CQ.I18n.getMessage("Failure"),
                        action.response.statusText);
            },
            "scope":this
        });
        frm.doAction(action);
    }
});

CQ.security.Privileges.getUrl = function(frm){
        var f = frm.findField("home");
        if (f) {
            var url = f.getValue();
            if (!url) {
                url = CQ.Util.externalize("/bin/security/authorizables/POST");
                url = CQ.HTTP.addParameter(url, "Authorizable", frm.findField("id").getValue());
            } else {
                url = CQ.Util.externalize(url);
            }
            return url;
        }
    };

CQ.security.Privileges.MODIFY_HIERARCHY = "wcm/privileges/modifyhierarchy";
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.PermissionEditor = CQ.Ext.extend(CQ.Ext.Panel, {

    /**
    * Id of the current selcted Authorizable as set via load
     * @private
     * @type String
     */
    authorizableId:null,

    constructor: function(config) {
        if (!config) {
            config={};
        }

        var cfg = CQ.Util.applyDefaults(config, {
            "layout":"accordion",
            "bodyStyle":"padding:0px;",
            "layoutConfig": {
	            "autoShow":true
            },
            "title": CQ.I18n.getMessage("Replication Privilege"),
            "dataPath":CQ.Util.externalize('/bin/security/authorization/acltable'),
            "treePath":"/bin/tree/ext",
            "treeRoot": {
                "name":"",
                "text":"CQ",
                "draggable":false,
                "expanded":true,
                "singleClickExpand": true
             }});

        //--------------------------------------------< acl grid / store>-------
       this.aclStore=  new CQ.security.data.AclStore({
            "recId":"path",
            "autoload":false,
            "listeners": {   //register for enabling of toolbar
               'add': {fn:this.storeChanged, scope:this},
               'remove': {fn:this.storeChanged, scope:this},
               'update': {fn:this.storeChanged, scope:this}
            },
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":cfg.dataPath+".effective"+CQ.HTTP.EXTENSION_JSON,
                "method":"GET"
            })
		});

        this.grid = new CQ.Ext.grid.EditorGridPanel( {
            "region":"center",
            "store":this.aclStore,
            "loadMask":true,
            "tbar":[{
                    "id":"cq-useadmin-permissioneditor-save",
                    "text":CQ.I18n.getMessage("Save"),
                    "handler":this.saveHandler,
                    "disabled":true,
                    "title":CQ.I18n.getMessage("Save Changes"),
                    "tooltip":CQ.I18n.getMessage("Save changes to Permissions"),
                    "scope":this
                }
            ],   //todo: context-menu
            "cm":new CQ.Ext.grid.ColumnModel(CQ.security.PermissionEditor.COLUMNS),
            "viewConfig": {
                "forceFit":true
            },
            "sm": new CQ.Ext.grid.RowSelectionModel({
                "singleSelect":true,
                "listeners": {   //register to trigger editing
                    'rowdblclick': {"fn":this.editACE, "scope":this}
                }
            })
        });

        this.tree = new CQ.Ext.tree.TreePanel({
            "region":"west",
            "title":" ",
            "collapsible":true,
            "collapseMode":"mini",
            "autoScroll":true,
            "width":200,
            "stateful":false,
            "listeners": {
                "click" :{ //change path
                    "fn": this.loadTable,
                    "scope":this
                }
            },
            "root":new CQ.Ext.tree.AsyncTreeNode(cfg.treeRoot),
            "loader":new CQ.Ext.tree.TreeLoader({
                "baseParams": { "predicate":"hierarchy" },
                "requestMethod":"GET",
                "dataUrl": CQ.Util.externalize(cfg.treePath)+CQ.HTTP.EXTENSION_JSON+"?predicate=folder",
                "baseAttrs": { "singleClickExpand":true },
                "listeners": {
                    "beforeload": function(loader, node) {
                        this.baseParams.path = node.getPath();
                    }
                }
            })
        });
        var treePanel = new CQ.Ext.Panel({
        	"title": CQ.I18n.getMessage("Edit"),
        	"layout":"border",
        	"items": [this.tree, this.grid]        	
        })

        var cols = new Array();
        cols.push({
            "header":CQ.I18n.getMessage("Path"),
            "dataIndex":"path",
            "menuDisabled":true});
        for (var i=0;i<CQ.security.PermissionEditor.COLUMNS.length;i++) {
        	cols.push(CQ.security.PermissionEditor.COLUMNS[i]);
        }
        this.viewStore = new CQ.security.data.AclStore({
            "recId":"path",
            "autoload":false,
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":cfg.dataPath+".permission.overview"+CQ.HTTP.EXTENSION_JSON,
                "method":"GET"
            })
    	})
        var edit = new CQ.Ext.grid.EditorGridPanel({
        		  "title": CQ.I18n.getMessage("List View"),          
                  "viewConfig": {
                	  "forceFit":true
        		   },
        		  "store":this.viewStore,
	              "loadMask":true,
	              "cm":new CQ.Ext.grid.ColumnModel(cols),
	              "sm": new CQ.Ext.grid.RowSelectionModel({
	                  "singleSelect":true,
	                  "listeners": {   //register to trigger editing
	                      'rowdblclick': {"fn":this.editACE, "scope":this}
	                  }
	              })
        });
        	          
        cfg.items= [treePanel,edit];
        CQ.security.PrivilegeEditor.superclass.constructor.call(this, cfg);
    },

    /**
     * Loads the Tab with permission of the given Record:
     * Closes the tree, saves the authorizable's Id
     * @param rec
     */
    loadRecord: function(rec) {
        this.aclStore.removeAll();
        if (this.tree.root.isLoaded()) {
            this.tree.collapseAll();
        }
        if (rec && rec.id) {
            this.viewStore.proxy.conn.url = rec.get("home")+".permission.overview"+CQ.HTTP.EXTENSION_JSON;
            this.viewStore.reload();
        	this.authorizableId = rec.id;
            this.aclStore.baseParams.Authorizable = rec.id;
        }
    },

    /**
     * remove all stored Permissions 
     * @param rec to unload from the selection
     */
    unloadRecord: function(rec) {
        this.authorizableId = null;
        this.aclStore.baseParams.Authorizable = null;
        this.loadRecord(null);
    },

    /**
     * Handler for Tree-Node selection:
     * Reloads the table with the view of the effective ACL at the selected Path
     * Closes the tree.
     *
     * @param {TreeNode} node selected
     * @private
     */
    loadTable:  function(node) {
        var path = node.getPath();
        this.grid.stopEditing();

        //change path parameter and reload store
        this.aclStore.baseParams.path = path;
        this.aclStore.reload();
    },

    /**
     * Handler for Grid (=ACE) selection
     * starts editing the View,
     * Creates a new Record if non existed sofar
     *
     * @private
     */
    editACE: function() {
        if (this.aclStore.getCount()<1) {
            var selModel = this.tree.getSelectionModel().getSelectionModel();
            var path = selModel.getSelectedNode().getPath(); 
            var rec = new CQ.security.PrivilegeEditor.RECORD({path:path});
            rec.set("authorizable",this.authorizableId); //edit to mark modified
            this.aclStore.insert(0, [rec]);
        }
        this.grid.startEditing(0,0);
    },

    /**
     * Sends the changed ACE
     * @private
     */
    saveHandler: function(){
        var mod = new Array();
        var params = {
            "_charset_":"utf-8",
            "Authorizable":this.authorizableId,
            "modified":new Array(),
            "path":this.getSelectedPath()
        };

        var st = this.aclStore;
        var cnt = st.getCount();
        for (var i=0;i<cnt;i++) {
            var rec = st.getAt(i);
            if (rec.dirty) {
                rec.set("path", this.getSelectedPath());
                rec.set("authorizable", this.authorizableId); //while the acls work with principals,
                                                           // this editor is only aware of authorizables. To avoid missmatches set to authorizable
                mod.push(rec);
                params.modified.push(rec.toParam());
            }
        };

        var sBut = CQ.Ext.getCmp("cq-useadmin-permissioneditor-save");
        var url = this.initialConfig.dataPath;
        CQ.HTTP.post(url,
                function(){
                    sBut.disable();
                    for(var j=0;j<mod.length;j++) {
                        mod[j].commit();
                    }
                },
                params, this);
    },

    /**
     * Modifciation listener registered at the Store:
     * Acitvates the ToolBar buttons 
     * @param strore
     * @param rec
     * @param action
     * @private
     */
    storeChanged: function(strore, rec, action) {
        var sBut = CQ.Ext.getCmp("cq-useadmin-permissioneditor-save");
        if (sBut && CQ.Ext.data.Record.COMMIT!=action) {
            sBut.enable();
        }
    },

    /**
     * shortcut to get the Path selected within the tree 
     * @private
     */
    getSelectedPath: function() {
        var sm = this.tree.getSelectionModel();
        var node = sm.getSelectedNode();
        if (node) {
            return node.getPath();
        } else {
            return null;
        }
    }
});

/**
 * Config for the Column Editor
 * @static
 * @final
 * @private
 * @type CQ.Ext.data.Record
 */
CQ.security.PermissionEditor.COLUMN_EDITOR = {
    "allowBlank":true,
    "mode":"local",
    "triggerAction":"all",
    "selectOnFocus":true,
    "store": [
        [ " ", CQ.I18n.getMessage("inherit")],
        [ "allow", CQ.I18n.getMessage("allow")],
        [ "deny", CQ.I18n.getMessage("deny")]
    ]
};

//todo: format
CQ.security.PermissionEditor.COLUMNS_RENDERER = function(v) {
    if ("deny" == v) {
        return "<font color=\"red\">" + v + "</font>";
    } else if ("allow" == v) {
        return "<font color=\"green\">" + v + "</font>";
    } else if (!v || v==" ") {
    	return "<font color=\"red\">" + CQ.I18n.getMessage("deny") + "</font>";
    } else {
    	return "<font color=\"green\">" + CQ.I18n.getMessage("allow") + "</font>";
    }
};

/**
 * Header of the Grid
 * @static
 * @final
 * @private
 * @type Array
 */
CQ.security.PermissionEditor.COLUMNS = [{
    "header":CQ.I18n.getMessage("Read"),
    "dataIndex":"read",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    "header":CQ.I18n.getMessage("Modify"),
    "dataIndex":"update",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    "header":CQ.I18n.getMessage("Create"),
    "dataIndex":"create",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    "header":CQ.I18n.getMessage("Delete"),
    "dataIndex":"delete",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    "header":CQ.I18n.getMessage("Read ACL"),
    "dataIndex":"read ACL",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    "header":CQ.I18n.getMessage("Write ACL"),
    "dataIndex":"write ACL",
    "editor":new CQ.Ext.form.ComboBox(CQ.security.PermissionEditor.COLUMN_EDITOR),
    "menuDisabled":true,
    "renderer":CQ.security.PermissionEditor.COLUMNS_RENDERER
},{
    // dummy column to fix auto-widths of columns on resizing
    "header":"",
    "menuDisabled":true,
    // use existing field to avoid data access errors, but display nothing
    "dataIndex":"read",
    "renderer": function(v) {
        return "";
    }
}
];
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.security.AclEditor</code> class represents an editor management of ACLs.
 * @class
 * @extends CQ.Ext.Panel
 */
CQ.security.AclEditor = CQ.Ext.extend(CQ.Ext.Panel, {

    /**
     * Grid of this combined widget
     * @private
     * @type CQ.Ext.grid.EditorGridPanel
     */
    aclList: null,

    /**
     * Store of the editable ACL grid
     * @private
     */
    aclStore: null,

    /**
     * Grid of this combined widget
     * @private
     * @type CQ.Ext.grid.EditorGridPanel
     */
    effectiveAclList: null,

    /**
     * Store of the effective ACL grid
     * @private
     */
    effectiveAclStore: null,

    /**
     * Tree displaying authorizables
     * @private
     * @type CQ.security.AuthorizableList
     */
    authSelection: null,

    /**
     * removed Records to be sent back on the server after commit
     * @private
     * @type CQ.Ext.tree.TreePanel
     */
    removed: [],

    constructor: function(config) {
        // config defaults
        config = CQ.Util.applyDefaults(config, {
            "id":"cq-useradmin-wrapper",
            "layout":"border",
            "stateful":false,
            "title":CQ.I18n.getMessage("Permissions / ACL"),
            "dataPath":"/bin/security/authorization/acltable"
        });

        // ACL list
        this.aclStore = new CQ.security.data.AclStore({
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":config.dataPath + ".acl" + CQ.HTTP.EXTENSION_JSON,
                "method":"GET"
            })
        });

        this.aclList = new CQ.Ext.grid.EditorGridPanel({
            "autoExpandColumn":"Id",
            "region":"center",
            "store":this.aclStore,
            "viewConfig":{ forceFit:true },
            "cm":new CQ.Ext.grid.ColumnModel(CQ.security.AclEditor.COLUMNS),
            "sm":new CQ.Ext.grid.RowSelectionModel({ singleSelect:true }),
            "frame":false,
            "autoExpand":true,
            "autoShow":true
        });

        var authListCfg = {
            "width":200,
            "enableDragDrop":false,
            "hideFilter":true,
            "selectionModel": new CQ.Ext.grid.RowSelectionModel({ "singleSelect":true }),
            "anchor":"30%",
            "columnModel":new CQ.Ext.grid.ColumnModel([{
                "header":CQ.I18n.getMessage("Type"),
                "dataIndex":"type",
                "width":30,
                "fixed":true,
                "resizable":false,
                "hideable":false,
                "renderer":CQ.security.AuthorizableList.renderIcon
            },{
                "header":CQ.I18n.getMessage("Name"),
                "dataIndex":"name"
            }
            ]),
            "listeners":{
                "rowdblclick": {
                    "fn":this.selectionAction,
                    "scope":this
                }
            }
        }
        this.authSelection = new CQ.security.AuthorizableList(authListCfg);
        
        // effective ACL list
        this.effectiveAclStore = new CQ.security.data.AclStore({
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":config.dataPath + ".effective" + CQ.HTTP.EXTENSION_JSON,
                "method":"GET"
            })
        });

        this.effectiveAclList = new CQ.Ext.grid.GridPanel({
            "title":CQ.I18n.getMessage("Effective Permissions"),
            "statefull":false,
            "autoExpandColumn":"Id",
            "region":"north",
            "height":240,
            "minHeigt":240,
            "store":this.effectiveAclStore,
            "viewConfig":{ "forceFit":true },
            "cm":new CQ.Ext.grid.ColumnModel(CQ.security.AclEditor.COLUMNS),
            "sm":new CQ.Ext.grid.RowSelectionModel({ "singleSelect":true }),
            "frame":false,
            "autoExpand":true,
            "autoShow":true,
            "split":true
        });

        config.items = [
            this.effectiveAclList,
            new CQ.Ext.Panel({
                "id":"editor",
                "region":"center",
                "stateful":false,
                "layout":"border",
                "tbar":[{
                        "text":CQ.I18n.getMessage("Remove"),
                        "qtip":CQ.I18n.getMessage("Remove Permission"),
                        "handler":this.removeACE,
                        "scope":this
                    }
                ],
                "items":[
                    this.aclList,
                    this.authSelection
                ]
            })
        ];
        CQ.security.AclEditor.superclass.constructor.call(this, config);
    },

    /**
     * Initializes the editor.
     * @see CQ.Ext.Component#initComponent
     */
    initComponent: function() {
        CQ.security.AclEditor.superclass.initComponent.call(this);

        this.addEvents(
            /**
             * @event aclchanged
             * Fires to notify listeners that an ACL was edited.
             * @param {CQ.security.AclEditor} this
             */
                "aclchanged",
            /**
             * @event aclsaved
             * Fires to notify listeners that an ACL was saved.
             * @param {CQ.security.AclEditor} this
             * @param {boolean} success
             */
                "aclsaved"
                );
        // register for internal change events
        var editor = this;
        var fireChangeEvent = function() {
            editor.fireEvent("aclchanged", editor);
        }
        this.aclStore.on("add", fireChangeEvent, this);
        this.aclStore.on("update", fireChangeEvent, this);
        this.aclStore.on("remove", fireChangeEvent, this);
    },

    load: function(path) {
        if (typeof(path) == "object") {
            this.effectiveAclStore.loadData(path);
            this.aclStore.loadData(path);
        } else {
            this.aclStore.baseParams.path = path;
            this.aclStore.reload();
            this.effectiveAclStore.baseParams.path = path;
            this.effectiveAclStore.reload();
        }
    },

    reload: function() {
        this.aclStore.reload();
        this.effectiveAclStore.reload();
    },

    save: function() {
        var params = {
            modified:[],
            removed:[],
            path:this.aclStore.baseParams.path
        };
        var mod = this.aclStore.getModifiedRecords();
        for (var i = 0; i < mod.length; i++) {
            var ser = this.serializeRec(mod[i]);
            params.modified.push(ser)
        }
        var rem = this.removed;
        for (i = 0; i < rem.length; i++) {
            params.removed.push(this.serializeRec(rem[i]));
        }
        var editor = this;
        CQ.HTTP.post(
                this.dataPath,
                function(options, success, response) {
                    editor.aclStore.commitChanges();
                    editor.removed = [];

                    editor.fireEvent("aclsaved", editor, success);
                },
                params, this
                );
    },

    removeACE: function() {
        var rec = this.aclList.getSelectionModel().getSelected();
        if (!rec) {
            CQ.Ext.MessageBox.alert(CQ.I18n.getMessage("Please select an entry to remove"));
        } else {
            var pr = rec.get("principal");
            if (CQ.Ext.MessageBox.confirm(CQ.I18n.getMessage("Do you really want to delete entry for {0}", pr))) {
                this.aclList.stopEditing();
                this.aclList.getStore().remove(rec);
                this.removed.push(rec);
            }
        }
    },

    /**
     * Creates a new Record and inserts at the end of the grid.
     * 
     * @param {CQ.Ext.grid.GridPanel} Grid selected
     * @param {Number} index
     * @private
     */
    selectionAction: function(grid) {
        var rec = grid.getSelectionModel().getSelected()
        var type = rec.get("type");
        if (type) {
            if (this.aclStore.getById(rec.id)) {
                CQ.Ext.Msg.alert('Note', 'Changes saved successfully.');
            }
            var added = new CQ.security.data.AclRecord(
                    {"type":type},
                    rec.id
                    );
            this.aclList.stopEditing();

            var idx = this.aclStore.getCount();
            this.aclStore.insert(idx, [added]);
            added.set("authorizable", rec.id);

            var fields = CQ.security.data.AclRecord.FIELDS;
            for (var i = 2; i < fields.length; i++) {
                added.set(fields[i].name, " ");
            }
            this.aclList.startEditing(idx, 1);
        }
    },

    /**
     * Create a JsonString object of an {CQ.security.AclEditor.RECORD}
     * @private 
     * @param record
     */
    serializeRec: function(record) {
        var fields = CQ.security.data.AclRecord.FIELDS;
        var ser = '';
        for (var t = 0; t < fields.length; t++) {
            var field = fields[t];
            var name = field.name;
            var val = record.get(name);
            if (field.mapping) {
                name = field.mapping;
            }
            ser += name + ':' + encodeURIComponent(val) + ',';
        }
        return ser;
    }
});

/**
 * Config for the Column Editor
 * @static
 * @final
 * @private
 * @type CQ.Ext.data.Record
 */
CQ.security.AclEditor.COLUMN_EDITOR = {
    allowBlank:true,
    mode:"local",
    triggerAction:"all",
    selectOnFocus:true,
    store: [
        [ " ", CQ.I18n.getMessage("inherit")],
        [ "allow", CQ.I18n.getMessage("allow")],
        [ "deny", CQ.I18n.getMessage("deny")]
    ]
};

CQ.security.AclEditor.COLUMNS_RENDERER = function(v) {
    if ("allow" == v) {
        return "<font color=\"green\">" + v + "</font>";
    } else if ("deny" == v) {
        return "<font color=\"red\">" + v + "</font>";
    }
    return v;
};

/**
 * Header of the Grid
 * @static
 * @final
 * @private
 * @type Array
 */
CQ.security.AclEditor.COLUMNS = [
    {
        id:"Id",
        header:CQ.I18n.getMessage("Principal"),
        dataIndex:"authorizable",
        width:200,
        sortable:true,
        menuDisabled:true
    },{
    header:CQ.I18n.getMessage("Read"),
    dataIndex:"read",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
},{
    header:CQ.I18n.getMessage("Update"),
    dataIndex:"update",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
},{
    header:CQ.I18n.getMessage("Create"),
    dataIndex:"create",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
},{
    header:CQ.I18n.getMessage("Delete"),
    dataIndex:"delete",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
},{
    header:CQ.I18n.getMessage("Read ACL"),
    dataIndex:"read ACL",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
},{
    header:CQ.I18n.getMessage("Write ACL"),
    dataIndex:"write ACL",
    width:80,
    editor:new CQ.Ext.form.ComboBox(CQ.security.AclEditor.COLUMN_EDITOR),
    resizable:false,
    menuDisabled:true,
    renderer:CQ.security.AclEditor.COLUMNS_RENDERER
}
];/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.UserProperties = CQ.Ext.extend(CQ.Ext.Panel, {

    userForm: null,

    groupForm: null,

    currentRecord:  null,
    
    constructor: function(config) {

        this.userForm = new CQ.Ext.form.FormPanel({
            "baseParams": {
                "_charset_":"utf-8"
            },
            "border":false,
            "bodyStyle":"padding:5px;",
            "anchor":"100% 80%",
            "hidden":true,
            "items":[{
                    "xtype":"hidden",
                    "name":"home"
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("Login"),
                    "anchor":"100%",
                    "disabled":true,
                    "allowBlank":false,
                    "name":"rep:userId"
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("First Name"),
                    "anchor":"100%",
                    "name":"givenName"
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("Last Name"),
                    "anchor":"100%",
                    "name":"familyName",
                    "allowBlank":false
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("Mail"),
                    "anchor":"100%",
                    "vtype":"email",
                    "msgTarget":"under",
                    "name":"email"
                },{
                    "xtype":"textarea",
                    "fieldLabel":CQ.I18n.getMessage("About"),
                    "anchor":"100% -155",
                    "name":"aboutMe"
            }],
            "tbar":[]
        });

        this.pwdButtons = new CQ.Ext.util.MixedCollection();
        this.pwdButtons.addAll([new CQ.Ext.Toolbar.Button({
            	"text":CQ.I18n.getMessage("Set Password"),
                "tooltip": CQ.I18n.getMessage("Set Password"),
                "handler":this.setPassword,
                "scope":this.userForm
            }),  
            new CQ.Ext.Toolbar.Separator()]);
            	
        var tb = this.userForm.getTopToolbar();
        for (var i=0;i<this.pwdButtons.length;i++) {
        	tb.push(this.pwdButtons.get(i));
        }
        tb.push({
            "text":CQ.I18n.getMessage("Save"),
            "tooltip": {
                "title":CQ.I18n.getMessage("Save Changes"),
                "text":CQ.I18n.getMessage("Save changes of the profile"),
                "autoHide":true
            },
            "handler":this.saveHandler,
            "scope":this.userForm
        });

        this.groupForm = new CQ.Ext.form.FormPanel({
            "baseParams": {
                "_charset_":"utf-8"
            },
            "border":false,
            "bodyStyle":"padding:5px;",
            "anchor":"100% 80%",
            "hidden":true,
            "items":[{
                    "xtype":"hidden",
                    "name":"home"
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("Name"),
                    "anchor":"100%",
                    "disabled":true,
                    "allowBlank":false,
                    "name":"name"
                },{
                    "xtype":"textfield",
                    "fieldLabel":CQ.I18n.getMessage("Mail"),
                    "anchor":"100%",
                    "vtype":"email",
                    "msgTarget":"under",
                    "name":"emails/primary/value"
                },{
                    "xtype":"hidden",
                    "name":"emails/primary/primary",
                    "value":true
                },{
                    "xtype":"textarea",
                    "fieldLabel":CQ.I18n.getMessage("About"),
                    "anchor":"100% -155",
                    "name":"aboutMe"
                }],
            "tbar":[]
            });

        tb = this.groupForm.getTopToolbar();
        tb.push({
            text:CQ.I18n.getMessage("Save"),
            tooltip: {
                title:CQ.I18n.getMessage("Save Changes"),
                text:CQ.I18n.getMessage("Save changes of the profile"),
                autoHide:true
            },
            handler:this.saveHandler,
            scope:this.groupForm
        });

        CQ.Util.applyDefaults(config, {
            layout:"form",
            bodyStyle:"padding:0px;",
            labelWidth:75,
            title:CQ.I18n.getMessage("Properties"),
            items: [this.userForm, this.groupForm]
        });
        CQ.security.UserProperties.superclass.constructor.call(this, config);
    },

    loadRecord: function(rec) {
        var type = rec.get("type");
        var active, hidden;
        if (type=="user") {
            active = this.userForm;
            hidden = this.groupForm;
    		if (rec.id==CQ.security.UserProperties.ADMIN_ID) {
    			this.pwdButtons.each(function(bt) {bt.hide(); return true;} )
    		} else { 
    			this.pwdButtons.each(function(bt) {bt.show(); return true;} )
    		}
        } else {
            active = this.groupForm;
            hidden = this.userForm;
        }
        hidden.hide();
        active.getForm().loadRecord(rec);
        active.show();
        active.currentRecord = rec;
    },
    
    unloadRecord: function(rec) {
        if (!this.groupForm.hidden) {
            this.groupForm.hide();
            this.groupForm.currentRecord = null;
        } else {
            this.userForm.hide();
            this.userForm.currentRecord = null;
        }
    },

    saveHandler: function() {
        var frm = this.getForm();
        var url = CQ.security.UserProperties.getUrl(frm);
        var action = new CQ.form.Action.Submit(frm, {
            clientValidation:false,
            url:url,
            success:function(form) {
                form.updateRecord(this.currentRecord);
                CQ.Notification.notify(CQ.I18n.getMessage("OK"),CQ.I18n.getMessage("changes saved"));
            },
            failure:function(form, action) {
                CQ.Notification.notify(CQ.I18n.getMessage("Failure"),
                        action.response.statusText);
            },
            scope:this
        });
        frm.doAction(action);
    },

    setPassword : function() {
        var dialogCfg = {
            "width":400,
            "height":200,
            "jcr:primaryType": "cq:Dialog",
            "title":CQ.I18n.getMessage("Set Password"),
            "formUrl":CQ.security.UserProperties.getUrl(this.getForm()),
            "params": {
                "_charset_":"utf-8"
            },
            "items": {
                "jcr:primaryType": "cq:Panel",
                "items": {
                    "jcr:primaryType": "cq:WidgetCollection",
                    "password": {
                        "inputType":"password",
                        "fieldLabel":CQ.I18n.getMessage("Password"),
                        "name":"rep:password",
                        "allowBlank":false,
                        "msgTarget":"under"
                    },
                    "password2": {
                        "inputType":"password",
                        "fieldLabel":CQ.I18n.getMessage("Confirm Password"),
                        "name":"rep:password",
                        "allowBlank":false,
                        "msgTarget":"under",
                        "validator":function(value) {
                            var pwd = this.ownerCt.items.get(0).getRawValue();
                            if (pwd == value) {
                                return true;
                            }
                            return CQ.I18n.getMessage("Provided passwords do not match.");
                        }
                    }
                }
            },
            "okText":CQ.I18n.getMessage("Set"),
            "buttons": CQ.Dialog.OKCANCEL
        };
        var rec = this.currentRecord;
        var dialog = CQ.Util.build(dialogCfg);
        dialog.success = function(form) {
            form.updateRecord(rec);
            CQ.Notification.notify(CQ.I18n.getMessage("OK"),CQ.I18n.getMessage("changes saved"));
        };
        dialog.failure = function() {
            CQ.Notification.notify(CQ.I18n.getMessage("Failure"),CQ.I18n.getMessage("Could not set Password"));
        };
        dialog.show();
    }
});

CQ.security.UserProperties.getUrl = function(frm){
        var f = frm.findField("home");
        if (f) {
            var url = f.getValue();
            if (!url) {
                url = CQ.Util.externalize("/bin/security/authorizables/POST");
                url = CQ.HTTP.addParameter(url, "Authorizable", frm.findField("id").getValue());
            } else {
                url = CQ.Util.externalize(url);
            }
            return url;
        }
    };

CQ.security.UserProperties.ADMIN_ID = "admin";/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.Preferences = CQ.Ext.extend(CQ.Ext.Panel, {

    form: null,

    currentRecord:  null,

    constructor: function(config) {

        this.form = new CQ.Ext.form.FormPanel({
            "baseParams": {
                "_charset_":"utf-8"
            },
            "border":false,
            "bodyStyle":"padding:5px;",
            "anchor":"100% 80%",
            "items": [{
                "xtype":"combo",
                "allowBlank": true,
                "mode":'local',
                "triggerAction": 'all',
                "selectOnFocus":true,
                "listeners": {
                    "select": function() {
                        var sB = CQ.Ext.getCmp("cq-useradmin-preferences-save");
                        if (sB){
                            sB.enable();
                        }
                    }
                },
                "store": [
                    ["en",CQ.I18n.getMessage("English")],
                    ["de",CQ.I18n.getMessage("German")],
                    ["fr",CQ.I18n.getMessage("French")],
                    ["es",CQ.I18n.getMessage("Spanish")],
                    ["it",CQ.I18n.getMessage("Italian")],
                    ["zh-cn",CQ.I18n.getMessage("Chinese")],
                    ["ja",CQ.I18n.getMessage("Japanese")]
                ],
                "hiddenName":CQ.security.Preferences.PREFERNCE_LANGAUGE,
                "name":CQ.security.Preferences.SELNAME_LANGAUGE,
                "fieldLabel":CQ.I18n.getMessage("Language")
            },{
                "xtype":"hidden",
                "name":"home"
            }],
            "tbar":[{
                "id":"cq-useradmin-preferences-save",
                "disabled":true,
                "text":CQ.I18n.getMessage("Save"),
                "tooltip": CQ.I18n.getMessage("Save Changes"),
                "handler":this.saveHandler,
                "scope":this
            }]});

        CQ.Util.applyDefaults(config, {
            "layout":"form",
            "bodyStyle":"padding:0px;",
            "labelWidth":75,
            "title":CQ.I18n.getMessage("Preferences"),
            "items": [this.form]
        });
        CQ.security.Preferences.superclass.constructor.call(this, config);
    },

    loadRecord: function(rec) {
        var combo = this.form.find("name", CQ.security.Preferences.SELNAME_LANGAUGE);
        if (!combo || combo.length<1) {
            return;
        }
        var val = "";
        var path = rec.get("home");
        var url = path + ".preferences" + CQ.HTTP.EXTENSION_JSON;
        url = CQ.HTTP.noCaching(url);
        var req = CQ.HTTP.get(url);
        if (CQ.HTTP.isOkStatus(req.status)) {
            var d = CQ.Util.eval(req);
            this.currentRecord = new CQ.data.SlingRecord(d);
            //hack to avoid errors on access of unexisting values
            if (this.currentRecord.data && this.currentRecord.data.platform) {
                val = this.currentRecord.get(CQ.security.Preferences.PREFERNCE_LANGAUGE);
            }
        }
        combo[0].setValue(val);
    },

    /**
     * @param rec to remove
     */
    unloadRecord: function(rec) {
        this.currentRecord = null;
    },
    
    saveHandler: function() {
        if (this.currentRecord) {
            var uri = this.currentRecord.get("path");
            if (uri) {
                uri = CQ.Util.externalize(uri);
                var frm = this.form.getForm();
                var action = new CQ.form.Action.Submit(frm, {
                    "clientValidation":false,
                    "url":uri,
                    "success":function(form) {
                        var sB = CQ.Ext.getCmp("cq-useradmin-preferences-save");
                        if (sB){
                            sB.disable();
                        }
                        CQ.Notification.notify(CQ.I18n.getMessage("OK"),CQ.I18n.getMessage("Language saved"));
                    },
                    "failure":function(form, action) {
                        CQ.Notification.notify(CQ.I18n.getMessage("Failure"),
                                action.response.statusText);
                    },
                    "scope":this
                });
                frm.doAction(action);
                return;
            }
        }
        CQ.Notification.notify(CQ.I18n.getMessage("Failure"),CQ.I18n.getMessage("No User selected"));
    }
});

// property-path
CQ.security.Preferences.PREFERNCE_LANGAUGE = "./platform/language";

//identifier, for the form.
CQ.security.Preferences.SELNAME_LANGAUGE = "platformLanguage";/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.security.UserAdmin</code> class provides the admin console for 
 * user administration.
 * @class
 * @extends CQ.Ext.Viewport
 */
CQ.security.UserAdmin = CQ.Ext.extend(CQ.Ext.Viewport, {

    tabs:[],
    
    /**
     * Creates a new <code>CQ.security.UserAdmin</code> instance.
     * 
     * Example:
     * <pre><code>
     var admin = new CQ.security.UserAdmin({
     });
     </pre></code>
     * todo: paginate users
     * @constructor
     * @param {Object} config The config object
     */
    constructor: function(config) {
        var selectCfg = CQ.Util.applyDefaults(config, {
            "proxy": new CQ.Ext.data.HttpProxy({
                "url":"/bin/security/authorizables.json",
                "method":"GET"
            }),
            "baseParams": {
                "hideGroups":false,
                "hideUsers":false
            }
        });
        this.selectionStore = new CQ.Ext.data.Store(selectCfg);
        this.debug = config.debug;
        this.selectionBar = new CQ.Ext.Toolbar({
            "region":"north",
            "minHeight":21,
            "style":"height:21px",
            "items":[ new CQ.Ext.DataView({
                "store": this.selectionStore,
                "tpl": new CQ.Ext.XTemplate(
                        '<tpl for=".">',
                            '<div class="{[values.type=="user" ? "userIcon" : "groupIcon"]}">',
                            '{[values.name==""? values.id: values.name]}',
                            '</div>',
                        '</tpl>'
                        ),
                "id":"user-selection",
                "autoHeight":true,
                "multiSelect": true,
                "itemSelector":':first-child'
            }), "->", CQ.wcm.HelpBrowser.createHelpButton()
            ]
        });

        this.userProperties = new CQ.security.UserProperties({"hidden":true});
        this.tabs.push(this.userProperties);

        this.membership = new CQ.security.AuthRelationPanel({
            "listeners":{
                'authSaved':{
                    "fn":this.dispatchAuthSaved,
                    "scope":this
                }
            },
            "disabled":true,
            "field":"memberOf",
            "title":CQ.I18n.getMessage("Groups")
        });
        this.tabs.push(this.membership);
        this.members = new CQ.security.AuthRelationPanel({
            "authType": CQ.security.UserAdminPanel.TYPE_GROUP,
            "listeners":{
                'authSaved':{
                    "fn":this.dispatchAuthSaved,
                    "scope":this
                }
            },
            "disabled":true,
            "field":"members",
            "allowUserAdd":true,
            "title":CQ.I18n.getMessage("Members")
        });
        this.tabs.push(this.members);
        var authlist = CQ.Util.applyDefaults(config.authlist,{
            "anchor":"30%",
            "listeners":{
                "rowdblclick": {
                    "fn":this.selectionHandler,
                    "scope":this
                },
                "authremoved" : {
                    "fn":this.removeHandler,
                    "scope":this
                }
            }
        });
        this.list = new CQ.security.AuthorizableList(authlist);
        this.aclEditor = new CQ.security.PermissionEditor({
            "title": CQ.I18n.getMessage("Page Permissions"),
            "border":false});
        this.tabs.push(this.aclEditor);
        this.replicationEditor = new CQ.security.PrivilegeEditor({"hidden":true});
        this.tabs.push(this.replicationEditor);
        this.tabs.push(new CQ.security.Privileges({"hidden":true}));
        this.tabs.push(new CQ.security.AuthRelationPanel({
            "authType": CQ.security.UserAdminPanel.TYPE_USER,
            "disabled":true,
            "allowUserAdd":true,
            "field":"sudoers",
            "title":CQ.I18n.getMessage("Impersonators")})
        );
        this.tabs.push(new CQ.security.Preferences({}));

        this.tabPanel = new CQ.Ext.TabPanel({
            region:"center",
            border:false,
            enableTabScroll:true,
            defaults:{
                autoScroll:true
            },
            items:this.tabs,
            activeTab:0
        });

        // init component by calling super constructor
        CQ.security.UserAdmin.superclass.constructor.call(this, {
            "id":"cq-useradmin",
            "layout":"border",
            "renderTo":"CQ",
            "items": [{
                    "id":"cq-useradmin-wrapper",
                    "xtype":"panel",
                    "layout":"border",
                    "region":"center",
                    "border":false,
                    "items": [{
                            "id":"cq-header",
                            "xtype":"container",
                            "autoEl":"div",
                            "region":"north",
                            "items": [{
                                    "xtype":"panel",
                                    "border":false,
                                    "layout":"column",
                                    "cls": "cq-header-toolbar",
                                    "items": [
                                        new CQ.Switcher({}),
                                        new CQ.UserInfo({})
                                    ]
                                }
                            ]
                        },{
                        "xtype":"panel",
                        "region":"center",
                        "layout":"border",
                        "id":"editor",
                        "items":[
                            this.list,
                            {
                                "xtype":"panel",
                                "layout":"border",
                                "region":"center",
                                "margins":"5 5 5 0",
                                "items":[
                                    this.selectionBar,
                                    this.tabPanel
                                ]
                            }
                        ]
                    }
                    ]
                }
            ]
        });
    },

    loadRecord:function(rec) {
        var store = this.selectionStore;
        if (store) {
            store.removeAll();
            store.add(rec);
            store.baseParams.id=[rec.id]
        }
        for (var i=0;i<this.tabs.length;i++) {
            var tab = this.tabs[i];
            if (!tab.disabled && tab.loadRecord) {
                tab.loadRecord(rec);
            } else if (tab.onSelectionChanged) {
                tab.onSelectionChanged(store, rec);
            }
        }
    },

    unloadRecord:function(rec) {
        var store = this.selectionStore;
        for (var i=0;i<this.tabs.length;i++) {
            var tab = this.tabs[i];
            if (!tab.disabled && tab.unloadRecord) {
                tab.unloadRecord(rec);
            } else if (tab.onSelectionChanged) {
                tab.onSelectionChanged(store, rec);
            }
        }
    },

    switchVisibilty: function(type) {
        for (var i=0;i<this.tabs.length;i++) {
            var tab = this.tabs[i];
            if (!tab.authType || tab.authType==type) {
                tab.enable();
            } else {
                tab.disable();
            }
        }
    },

    selectionHandler: function(grid/*,rowIdx,eventObj*/) {
        var sel = grid.getSelectionModel();
        if (sel.hasSelection()) {
            var rec = sel.getSelected();
            this.tabPanel.activate(this.userProperties);
            this.switchVisibilty(rec.get("type"));
            this.loadRecord(rec);
            sel.clearSelections();
        }
    },

    removeHandler:function(authGrid, rec/*, index*/) {
        var store = this.selectionStore;
        if (store) {
            var sel = store.query("id", rec.id);
            if (sel.length > 0) {
                for (var j = 0; j < sel.length; j++) {
                    var selected = sel.itemAt(j);
                    this.unloadRecord(selected)
                    store.remove(selected);
                }
            }

            if (store.getCount()==0) {
                this.tabPanel.activate(this.userProperties);
                for (var i=1;i<this.tabs.length;i++) {
                    this.tabs[i].disable();
                }
            }
        }
    },

    dispatchAuthSaved:function(source, rec, field) {
        for(var i=0;i<this.tabs.length;i++) {
            var t = this.tabs[i];
            if (t!=source && t.onSelectionModified) {
                t.onSelectionModfied(rec, field);
            }
        }
        if (this.list) {
            this.list.updateRelation(rec, field);
        }
    }
    
});
CQ.UserAdmin = CQ.security.UserAdmin;
CQ.Ext.reg("useradmin", CQ.security.UserAdmin);
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

CQ.security.UserAdmin.createUser = function() {
    var createUserDialog = {
        "jcr:primaryType": "cq:Dialog",
        "title":CQ.I18n.getMessage("Create User"),
        "formUrl":"/libs/security/authorizables/POST",
        "params": {
            "_charset_":"utf-8"
        },
        "items": {
            "jcr:primaryType": "cq:Panel",
            "items": {
                "jcr:primaryType": "cq:WidgetCollection",
                "login": {
                    "fieldLabel":CQ.I18n.getMessage("Login ID"),
                    "emptyText":CQ.I18n.getMessage("Enter login for the user"),
                    "allowBlank":false,
                    "name":"rep:userId",
                    "msgTarget":"under"
                },
                "fname": {
                    "fieldLabel":CQ.I18n.getMessage("First Name"),
                    "name":"givenName",
                    "msgTarget":"under"
                },
                "name": {
                    "fieldLabel":CQ.I18n.getMessage("Last Name"),
                    "allowBlank":false,
                    "name":"familyName",
                    "msgTarget":"under"
                },
                "mail": {
                    "fieldLabel":CQ.I18n.getMessage("Mail"),
                    "vtype":"email",
                    "name":"email",
                    "msgTarget":"under"
                },
                "password": {
                    "inputType":"password",
                    "fieldLabel":CQ.I18n.getMessage("Password"),
                    "name":"rep:password",
                    "allowBlank":false,
                    "msgTarget":"under"
                },
                "password2": {
                    "inputType":"password",
                    "fieldLabel":CQ.I18n.getMessage("Confirm Password"),
                    "name":"rep:password",
                    "allowBlank":false,
                    "msgTarget":"under",
                    "validator":function(value) {
                        var pwd = this.ownerCt.items.get(4).getRawValue();
                        if (pwd == value) {
                            return true;
                        }
                        return CQ.I18n.getMessage("Provided passwords do not match.");
                    }
                }
            }
        },
        "okText":CQ.I18n.getMessage("Create")
    };
    var dialog = CQ.WCM.getDialog(createUserDialog);
    dialog.failure = function() {
        CQ.Ext.Msg.alert("Error", "Could not create user.")
    };
    dialog.success = function() {
        var st = CQ.Ext.StoreMgr.lookup("cq-useradmin-authstore");
        st.reload.defer(500, st);
    }
    dialog.show();
}

CQ.security.UserAdmin.createGroup = function() {
    var createGroupDialog = {
        "jcr:primaryType": "cq:Dialog",
        "title":CQ.I18n.getMessage("Create Group"),
        "formUrl":"/libs/security/authorizables/POST",
        "params": {
            "_charset_":"utf-8"
        },
        "items": {
            "jcr:primaryType": "cq:Panel",
            "items": {
                "jcr:primaryType": "cq:WidgetCollection",
                "login": {
                    "fieldLabel":CQ.I18n.getMessage("ID"),
                    "emptyText":CQ.I18n.getMessage("Enter ID for the group"),
                    "allowBlank":false,
                    "name":"groupName",
                    "msgTarget":"under"
                },
                "description": {
                    "fieldLabel":CQ.I18n.getMessage("Description"),
                    "name":"aboutMe",
                    "msgTarget":"under"
                }
            }
        },
        "okText":CQ.I18n.getMessage("Create")
    };
    var dialog = CQ.WCM.getDialog(createGroupDialog);
    dialog.failure = function() {
        CQ.Ext.Msg.alert("Error", "Could not create group.")
    };
    dialog.success = function() {
        var st = CQ.Ext.StoreMgr.lookup("cq-useradmin-authstore");
        st.reload.defer(200, st);
    };
    dialog.show();
}/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * The <code>CQ.MediaBrowseDialog</code> class represents a dialog for editing ACLs.
 *
 * @class
 * @extends CQ.Ext.Dialog
 */
CQ.security.AclDialog = CQ.Ext.extend(CQ.Dialog, {
	/**
	 * The ACL editor panel
	 * @private
	 * @type CQ.security.AclEditor
	 */
	aclEditor: null,
	
	/**
     * Save Button
     * @private
     * @type CQ.Ext.Button
     */
    saveButton: null,

   /**
     * Load the data via the grid-stores.
     * The data Object is used for testing
     * @param {String / Object} the content path to request the ACL for.
     *        Data-Object for testing
     */
    loadContent: function(path) {
        this.aclEditor.load(path);
    },

    /**
     * see cfg options
     * @param config {Object} Extension of dialog-config
     */
    constructor: function(config) {
    	config = CQ.Util.applyDefaults(config, {
    		title:CQ.I18n.getMessage("Edit Permissions"),
    		cancelText:CQ.I18n.getMessage("Close"),
    		editor: {
    			title:null,
    			border:false
    		}
	    });
    	config.buttons = [
    	    {
    	    	disabled:true,
    	        text:CQ.I18n.getMessage("Save"),
    	        qtip:CQ.I18n.getMessage("Save changes"),
    	        handler:this.save,
    	        scope:this
    	    },
    	    CQ.Dialog.CANCEL
        ];
    	this.aclEditor = new CQ.security.AclEditor(config.editor);
    	this.aclEditor.on("aclchanged", this.enableButtons, this);
    	this.aclEditor.on("aclsaved", this.saveCallback, this);
    	
    	config.items = this.aclEditor;
    	CQ.security.AclDialog.superclass.constructor.call(this, config);
    	
    	this.saveButton = this.buttons[0];
    },
    
    save: function() {
    	this.aclEditor.save.call(this.aclEditor);
    },
    
    saveCallback: function(editor, success) {
    	if (success) {
    		this.saveButton.disable();
    		this.aclEditor.reload();
    	}
    },

    /**
     * Listens on ACL store to en-/disable save button
     * @private
     */
    enableButtons: function() {
        if (this.saveButton.disabled) {
        	this.saveButton.enable();
        }
    }
});

CQ.Ext.reg("acldialog", CQ.security.AclDialog);
/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */

/**
 * Provide static util to access Permissions of a request.
 * Will register itself as PermissionProvider with the CQ.utils.User
 *
 */
CQ.security.utils = new Object();

CQ.security.utils.Permissions = new Object();

CQ.security.utils.Permissions.register = function() {
    var user = CQ.User.getCurrentUser();
    var store = new CQ.security.data.UserAclStore({
        id:CQ.User.PRIVILEGES_STORE_ID,
        recId:"path",
        dataUrl:user.getHome()+".permissions" + CQ.HTTP.EXTENSION_JSON
    });
    user.setPermissionStore(store);
};

CQ.security.utils.Permissions.register();
