/*!
 * Tine 2.0 - Addressbook 
 * Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * http://www.gnu.org/licenses/agpl.html AGPL Version 3
 */
/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2010-2017 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * TODO         add 'one of / in' operator?
 */
Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListMemberFilterModel
 * @extends     Tine.widgets.grid.FilterModel
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 */
Tine.Addressbook.ListMemberFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    /**
     * @property Tine.Tinebase.Application app
     */
    app: null,
    
    field: 'list',
    defaultOperator: 'equals',

    /**
     * @private
     */
    initComponent: function() {
        Tine.Addressbook.ListMemberFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        
        this.operators = ['equals'];
        this.label = this.app.i18n._('Member of List');
    },
    
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        var value = new Tine.Tinebase.widgets.form.RecordPickerComboBox({
            blurOnSelect: true,
            recordClass: this.field == 'list' ? Tine.Addressbook.Model.List : Tine.Addressbook.Model.Contact,
            filter: filter,
            width: 200,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el
        });
        value.on('select', this.onFiltertrigger, this);
        return value;
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['addressbook.listMember'] = Tine.Addressbook.ListMemberFilterModel;
/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * TODO         add 'one of / in' operator?
 */
Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListRoleMemberFilterModel
 * @extends     Tine.widgets.grid.FilterModel
 * 
 * @author      Philipp Schüle <p.schuele@metaways.de>
 */
Tine.Addressbook.ListRoleMemberFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    /**
     * @property Tine.Tinebase.Application app
     */
    app: null,
    
    field: 'list_role_id',
    defaultOperator: 'equals',
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Addressbook.ListRoleMemberFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        
        this.operators = ['equals'];
        this.label = this.app.i18n._('List Role');
    },
    
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        var value = new Tine.Tinebase.widgets.form.RecordPickerComboBox({
            blurOnSelect: true,
            recordClass: Tine.Addressbook.Model.ListRole,
            filter: filter,
            width: 200,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el
        });
        value.on('select', this.onFiltertrigger, this);
        return value;
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['addressbook.listRoleMember'] = Tine.Addressbook.ListRoleMemberFilterModel;
/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
/*global Ext, Tine*/
 
Ext.ns('Tine.Addressbook.Model');

// TODO: move this into model definition and replace uscases (?) with getter fn
Tine.Addressbook.Model.ContactArray = Tine.Tinebase.Model.genericFields.concat([
    {name: 'id', omitDuplicateResolving: true},
    {name: 'tid', omitDuplicateResolving: true},
    {name: 'private', omitDuplicateResolving: true},
    {name: 'cat_id', omitDuplicateResolving: true},
    {name: 'n_family', label: 'Last Name', group: 'Name' },//_('Last Name') _('Name')
    {name: 'n_given', label: 'First Name', group: 'Name' }, //_('First Name')
    {name: 'n_middle', label: 'Middle Name', group: 'Name' }, //_('Middle Name')
    {name: 'n_prefix', label: 'Title', group: 'Name' }, //_('Title')
    {name: 'n_suffix', label: 'Suffix', group: 'Name'}, //_('Suffix')
    {name: 'n_fn', label: 'Display Name', group: 'Name', omitDuplicateResolving: true }, //_('Display Name')
    {name: 'n_fileas', group: 'Name', omitDuplicateResolving: true },
    {name: 'bday', label: 'Birthday', type: 'date', dateFormat: Date.patterns.ISO8601Long }, //_('Birthday')
    {name: 'org_name', label: 'Company', group: 'Company' }, //_('Company')
    {name: 'org_unit', label: 'Unit', group: 'Company' }, //_('Unit')
    {name: 'salutation', label: 'Salutation', group: 'Name' }, //_('Salutation')
    {name: 'title', label: 'Job Title', group: 'Company' }, //_('Job Title')
    {name: 'role', label: 'Job Role', group: 'Company' }, //_('Job Role')
    {name: 'assistent', group: 'Company', omitDuplicateResolving: true},
    {name: 'room', label: 'Room', group: 'Company' }, //_('Room')
    {name: 'adr_one_street', label: 'Street (Company Address)', group: 'Company Address' }, //_('Street (Company Address)')  _('Company Address')
    {name: 'adr_one_street2', label: 'Street 2 (Company Address)', group: 'Company Address' }, //_('Street 2 (Company Address)')
    {name: 'adr_one_locality', label: 'City (Company Address)', group: 'Company Address' }, //_('City (Company Address)')
    {name: 'adr_one_region', label: 'Region (Company Address)', group: 'Company Address' }, //_('Region (Company Address)')
    {name: 'adr_one_postalcode', label: 'Postal Code (Company Address)', group: 'Company Address' }, //_('Postal Code (Company Address)')
    {name: 'adr_one_countryname', label: 'Country (Company Address)', group: 'Company Address' }, //_('Country (Company Address)')
    {name: 'adr_one_lon', label: 'Longitude (Company Address)', group: 'Company Address', omitDuplicateResolving: true }, //_('Longitude (Company Address)')
    {name: 'adr_one_lat', label: 'Latitude (Company Address)', group: 'Company Address', omitDuplicateResolving: true }, //_('Latitude (Company Address)')
    {name: 'label', omitDuplicateResolving: true},
    {name: 'adr_two_street', label: 'Street (Private Address)', group: 'Private Address' }, //_('Street (Private Address)')  _('Private Address')
    {name: 'adr_two_street2', label: 'Street 2 (Private Address)', group: 'Private Address' }, //_('Street 2 (Private Address)')
    {name: 'adr_two_locality', label: 'City (Private Address)', group: 'Private Address' }, //_('City (Private Address)')
    {name: 'adr_two_region', label: 'Region (Private Address)', group: 'Private Address' }, //_('Region (Private Address)')
    {name: 'adr_two_postalcode', label: 'Postal Code (Private Address)', group: 'Private Address' }, //_('Postal Code (Private Address)')
    {name: 'adr_two_countryname', label: 'Country (Private Address)', group: 'Private Address' }, //_('Country (Private Address)')
    {name: 'adr_two_lon', group: 'Private Address', omitDuplicateResolving: true},
    {name: 'adr_two_lat', group: 'Private Address', omitDuplicateResolving: true},
    {name: 'tel_work', label: 'Phone', group: 'Company Communication' }, //_('Phone') _('Company Communication')
    {name: 'tel_cell', label: 'Mobile', group: 'Company Communication' }, //_('Mobile')
    {name: 'tel_fax', label: 'Fax', group: 'Company Communication' }, //_('Fax')
    {name: 'tel_assistent', group: 'contact_infos', omitDuplicateResolving: true },
    {name: 'tel_car', group: 'contact_infos', omitDuplicateResolving: true },
    {name: 'tel_pager', group: 'contact_infos', omitDuplicateResolving: true },
    {name: 'tel_home', label: 'Phone (private)', group: 'Private Communication' }, //_('Phone (private)') _('Private Communication')
    {name: 'tel_fax_home', label: 'Fax (private)', group: 'Private Communication' }, //_('Fax (private)')
    {name: 'tel_cell_private', label: 'Mobile (private)', group: 'Private Communication' }, //_('Mobile (private)')
    {name: 'tel_other', group: 'contact_infos', omitDuplicateResolving: true },
    {name: 'tel_prefer', group: 'contact_infos', omitDuplicateResolving: true},
    {name: 'email', label: 'E-Mail', group: 'Company Communication' }, //_('E-Mail')
    {name: 'email_home', label: 'E-Mail (private)', group: 'Private Communication' }, //_('E-Mail (private)')
    {name: 'url', label: 'Web', group: 'Company Communication' }, //_('Web')
    {name: 'url_home', label: 'Web (private)', group: 'Private Communication' }, //_('Web (private)')
    {name: 'freebusy_uri', omitDuplicateResolving: true},
    {name: 'calendar_uri', omitDuplicateResolving: true},
    {name: 'note', label: 'Description' }, //_('Description')
    {name: 'tz', omitDuplicateResolving: true},
    {name: 'pubkey', omitDuplicateResolving: true},
    {name: 'jpegphoto', omitDuplicateResolving: true},
    {name: 'account_id', omitDuplicateResolving: true},
    {name: 'tags'},
    {name: 'notes', omitDuplicateResolving: true},
    {name: 'relations', omitDuplicateResolving: true},
    {name: 'customfields', omitDuplicateResolving: true},
    {name: 'attachments', omitDuplicateResolving: true},
    {name: 'paths', omitDuplicateResolving: true},
    {name: 'type', omitDuplicateResolving: true},
    {name: 'memberroles', omitDuplicateResolving: true}
]);

/**
 * @namespace   Tine.Addressbook.Model
 * @class       Tine.Addressbook.Model.Contact
 * @extends     Tine.Tinebase.data.Record
 * @constructor
 * Model of a contact<br>
 * 
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Addressbook.Model.Contact = Tine.Tinebase.data.Record.create(Tine.Addressbook.Model.ContactArray, {
    appName: 'Addressbook',
    modelName: 'Contact',
    idProperty: 'id',
    titleProperty: 'n_fn',
    // ngettext('Contact', 'Contacts', n); gettext('Contacts');
    recordName: 'Contact',
    recordsName: 'Contacts',
    containerProperty: 'container_id',
    // ngettext('Addressbook', 'Addressbooks', n); gettext('Addressbooks');
    containerName: 'Addressbook',
    containersName: 'Addressbooks',
    copyOmitFields: ['account_id', 'type'],
    
    /**
     * returns true if record has an email address
     * @return {Boolean}
     */
    hasEmail: function() {
        return this.get('email') || this.get('email_home');
    },
    
    /**
     * returns true preferred email if available
     * @return {String}
     */
    getPreferredEmail: function(preferred) {
        var preferred = preferred || 'email',
            other = preferred == 'email' ? 'email_home' : 'email';
            
        return (this.get(preferred) || this.get(other));
    },

    getTitle: function() {
        var result = this.get('n_fn');

        var tinebaseApp = new Tine.Tinebase.Application({
            appName: 'Tinebase'
        });
        if (tinebaseApp.featureEnabled('featureShowAccountEmail')) {
            var email = this.getPreferredEmail();
            if (email) {
                result += ' (' + email + ')';
            }
        }

        return result;
    }
});

/**
 * get filtermodel of contact model
 * 
 * @namespace Tine.Addressbook.Model
 * @static
 * @return {Array} filterModel definition
 */ 
Tine.Addressbook.Model.Contact.getFilterModel = function() {
    var app = Tine.Tinebase.appMgr.get('Addressbook');
    
    var typeStore = [['contact', app.i18n._('Contact')], ['user', app.i18n._('User Account')]];
    
    return [
        {label: i18n._('Quick Search'),                                                      field: 'query',              operators: ['contains']},
        {filtertype: 'tine.widget.container.filtermodel', app: app, recordClass: Tine.Addressbook.Model.Contact},
        {filtertype: 'addressbook.listMember', app: app},
        {filtertype: 'addressbook.listRoleMember', app: app},
        {label: app.i18n._('Title'),                                                    field: 'n_prefix' },
        {label: app.i18n._('First Name'),                                               field: 'n_given' },
        {label: app.i18n._('Last Name'),                                                field: 'n_family'},
        {label: app.i18n._('Middle Name'),                                              field: 'n_middle'},
        {label: app.i18n._('Company'),                                                  field: 'org_name'},
        {label: app.i18n._('Unit'),                                                     field: 'org_unit'},
        {label: app.i18n._('Phone'),                                                    field: 'telephone',          operators: ['contains']},
        {label: app.i18n._('Job Title'),                                                field: 'title'},
        {label: app.i18n._('Description'),                                              field: 'note'},
        {label: app.i18n._('E-Mail'),                                                   field: 'email_query',        operators: ['contains']},
        {filtertype: 'tinebase.tag', app: app},
//        {label: app.i18n._('Job Role'),                                                 field: 'role'},
//        {label: app.i18n._('Birthday'),    field: 'bday', valueType: 'date'},
        {label: app.i18n._('Street') + ' (' + app.i18n._('Company Address') + ')',      field: 'adr_one_street',     defaultOperator: 'equals'},
        {label: app.i18n._('Region') + ' (' + app.i18n._('Company Address') + ')',      field: 'adr_one_region',     defaultOperator: 'equals'},
        {label: app.i18n._('Postal Code') + ' (' + app.i18n._('Company Address') + ')', field: 'adr_one_postalcode', defaultOperator: 'equals'},
        {label: app.i18n._('City') + '  (' + app.i18n._('Company Address') + ')',       field: 'adr_one_locality'},
        {label: app.i18n._('Country') + '  (' + app.i18n._('Company Address') + ')',    field: 'adr_one_countryname', valueType: 'country'},
        {label: app.i18n._('Street') + ' (' + app.i18n._('Private Address') + ')',      field: 'adr_two_street',     defaultOperator: 'equals'},
        {label: app.i18n._('Region') + ' (' + app.i18n._('Private Address') + ')',      field: 'adr_two_region',     defaultOperator: 'equals'},
        {label: app.i18n._('Postal Code') + ' (' + app.i18n._('Private Address') + ')', field: 'adr_two_postalcode', defaultOperator: 'equals'},
        {label: app.i18n._('City') + ' (' + app.i18n._('Private Address') + ')',        field: 'adr_two_locality'},
        {label: app.i18n._('Country') + '  (' + app.i18n._('Private Address') + ')',    field: 'adr_two_countryname', valueType: 'country'},
        {label: app.i18n._('Type'), defaultValue: 'contact', valueType: 'combo',        field: 'type',               store: typeStore},
        {label: i18n._('Last Modified Time'),                                                field: 'last_modified_time', valueType: 'date'},
        {label: i18n._('Last Modified By'),                                                  field: 'last_modified_by',   valueType: 'user'},
        {label: i18n._('Creation Time'),                                                     field: 'creation_time',      valueType: 'date'},
        {label: i18n._('Created By'),                                                        field: 'created_by',         valueType: 'user'},
        {
            label: app.i18n._('Salutation'),
            field: 'salutation',
            filtertype: 'tine.widget.keyfield.filter',
            app: app,
            keyfieldName: 'contactSalutation',
            defaultOperator: 'in'
        }
    ];
};

/**
 * default timesheets backend
 */
Tine.Addressbook.contactBackend = new Tine.Tinebase.data.RecordProxy({
    appName: 'Addressbook',
    modelName: 'Contact',
    recordClass: Tine.Addressbook.Model.Contact
});

/**
 * list model
 */
Tine.Addressbook.Model.List = Tine.Tinebase.data.Record.create([
    {name: 'id'},
    {name: 'container_id'},
    {name: 'created_by'},
    {name: 'creation_time'},
    {name: 'seq'},
    {name: 'last_modified_by'},
    {name: 'last_modified_time'},
    {name: 'is_deleted'},
    {name: 'deleted_time'},
    {name: 'deleted_by'},
    {name: 'name'},
    {name: 'description'},
    {name: 'members'},
    {name: 'memberroles'},
    {name: 'email'},
    {name: 'type'},
    {name: 'list_type'},
    {name: 'group_id'},
    {name: 'emails'},
    {name: 'notes', omitDuplicateResolving: true},
    {name: 'relations', omitDuplicateResolving: true},
    {name: 'customfields', omitDuplicateResolving: true},
    {name: 'tags'}
], {
    appName: 'Addressbook',
    modelName: 'List',
    idProperty: 'id',
    titleProperty: 'name',
    // ngettext('List', 'Lists', n); gettext('Lists');
    recordName: 'List',
    recordsName: 'Lists',
    containerProperty: 'container_id',
    // ngettext('Addressbook', 'Addressbooks', n); gettext('Addressbooks');
    containerName: 'Addressbook',
    containersName: 'Addressbooks',
    copyOmitFields: ['group_id']
});

Tine.Addressbook.Model.List.getFilterModel = function() {
    
    var app = Tine.Tinebase.appMgr.get('Addressbook');
    
    var typeStore = [['list', app.i18n._('List')], ['user', app.i18n._('User Account')]];
    
    return [
        {label: i18n._('Quick Search'),                                                      field: 'query',              operators: ['contains']},
        {filtertype: 'tine.widget.container.filtermodel', app: app, recordClass: Tine.Addressbook.Model.Contact},
        {filtertype: 'addressbook.listMember', app: app, field: 'contact'},
        {label: app.i18n._('Name'),                                               field: 'name' },
        {label: app.i18n._('Description'),                                                field: 'description'},
        {label: i18n._('Last Modified Time'),                                                field: 'last_modified_time', valueType: 'date'},
        {label: i18n._('Last Modified By'),                                                  field: 'last_modified_by',   valueType: 'user'},
        {label: i18n._('Creation Time'),                                                     field: 'creation_time',      valueType: 'date'},
        {label: i18n._('Created By'),                                                        field: 'created_by',         valueType: 'user'},
        {filtertype: 'tinebase.tag', app: app}
    ];
};

/**
 * default list backend
 */
Tine.Addressbook.listBackend = new Tine.Tinebase.data.RecordProxy({
    appName: 'Addressbook',
    modelName: 'List',
    recordClass: Tine.Addressbook.Model.List
});

/**
 * email address model
 */
Tine.Addressbook.Model.EmailAddress = Tine.Tinebase.data.Record.create([
   {name: 'n_fileas'},
   {name: 'emails'},
   {name: 'email'},
   {name: 'email_home'}
], {
    appName: 'Addressbook',
    modelName: 'EmailAddress',
    titleProperty: 'name',
    // ngettext('Email Address', 'Email Addresses', n); gettext('Email Addresses');
    recordName: 'Email Address',
    recordsName: 'Email Addresses',
    containerProperty: 'container_id',
    // ngettext('Addressbook', 'Addressbooks', n); gettext('Addressbooks');
    containerName: 'Addressbook',
    containersName: 'Addressbooks',
    copyOmitFields: ['group_id'],

    getPreferredEmail: function(preferred) {
        var emails = this.get("emails");
        if (! this.get("email") && ! this.get("email_home")) {
            return this.get("emails");
        } else {
            var preferred = preferred || 'email',
            other = preferred == 'email' ? 'email_home' : 'email';
            return (this.get(preferred) || this.get(other));
        }
    }
});


/**
 * get filtermodel of emailaddress model
 * 
 * @namespace Tine.Addressbook.Model
 * @static
 * @return {Array} filterModel definition
 */ 
Tine.Addressbook.Model.EmailAddress.getFilterModel = function() {
    return [
        {label: i18n._('Quick search'),       field: 'query',              operators: ['contains']}
    ];
};

/**
 * ListRole model
 */
Tine.Addressbook.Model.ListRole = Tine.Tinebase.data.Record.create([
    {name: 'id'},
    {name: 'name'},
    {name: 'members'},
    {name: 'description'}
], {
    appName: 'Addressbook',
    modelName: 'ListRole',
    titleProperty: 'name',
    // ngettext('List Role', List Roles', n); gettext('List Roles');
    recordName: 'List Role',
    recordsName: 'List Roles'
});

/**
 * get filtermodel of ListRole model
 *
 * @namespace Tine.Addressbook.Model
 * @static
 * @return {Array} filterModel definition
 */
Tine.Addressbook.Model.ListRole.getFilterModel = function() {
    return [
        {label: i18n._('Quick search'),       field: 'query',              operators: ['contains']}
    ];
};
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.GenericContactGridPanelHook
 * 
 * Hook prototype for other applications to hook into the addressbook contact grid context menu<br>
 * 
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 */

Tine.Addressbook.GenericContactGridPanelHook = function(config) {
    Tine.log.info('initialising addressbook hook');
    Ext.apply(this, config);
    
    this.recordClass = Tine[this.app.name].Model[this.modelName];
    var text = this.recordClass.getMeta('recordName');
    
    this.addRecordAction = new Ext.Action({
        actionType: 'add',
        requiredGrant: 'readGrant',
        text: text,
        iconCls: this.app.getIconCls(),
        scope: this,
        handler: this.onUpdateRecord,
        allowMultiple: true,
        listeners: {
            scope: this,
            render: this.onRender
        }
    });
    
    this.newRecordAction = new Ext.Action({
        actionType: 'new',
        requiredGrant: 'readGrant',
        text: text,
        iconCls: this.app.getIconCls(),
        scope: this,
        handler: this.onAddRecord,
        allowMultiple: true,
        listeners: {
            scope: this,
            render: this.onRender
        }
    });
    
    Ext.ux.ItemRegistry.registerItem('Addressbook-Contact-GridPanel-ContextMenu-Add', this.addRecordAction, 100);
    Ext.ux.ItemRegistry.registerItem('Addressbook-Contact-GridPanel-ContextMenu-New', this.newRecordAction, 100);
};

Ext.apply(Tine.Addressbook.GenericContactGridPanelHook.prototype, {
    
    /**
     * The hooking application
     * 
     * @type Tine.Tinebase.Application
     */
    app: null,
    
    /**
     * The hooking model name of the
     * 
     * @type 
     */
    modelName: null,
    
    /**
     * The Addressbook Contact Grid to hook into
     * 
     * @type {Tine.Addressbook.ContactGridPanel}
     */
    contactGridPanel: null,
        
    /**
     * get addressbook contact grid panel
     */
    getContactGridPanel: function() {
        if (! this.contactGridPanel) {
            this.contactGridPanel = Tine.Tinebase.appMgr.get('Addressbook').getMainScreen().getCenterPanel();
        }
        return this.contactGridPanel;
    },

    /**
     * add selected contacts to a new record
     * 
     * @param {Button} btn 
     */
    onAddRecord: function(btn) {
        var ms = this.app.getMainScreen(),
            cp = ms.getCenterPanel(),
            filter = this.getFilter(ms);
        
        if (!filter) {
            var addRelations = this.getSelectionsAsArray();
        } else {
            var addRelations = true;
        }

        cp.onEditInNewWindow.call(cp, 'add', null, [{
            ptype: 'addrelations_edit_dialog', selectionFilter: filter, addRelations: addRelations, callingApp: 'Addressbook', callingModel: 'Contact'
        }]);
    },
    
    /**
     * adds selected contacts to an existing record
     */
    onUpdateRecord: function() {
        var ms = this.app.getMainScreen(),
            cp = ms.getCenterPanel(),
            filter = this.getFilter(ms),
            sm = this.getContactGridPanel().selectionModel;
            
        if (! filter) {
            var addRelations = this.getSelectionsAsArray(),
                count = this.getSelectionsAsArray().length;
        } else {
            var addRelations = true,
                count = sm.store.totalLength;
        }
        var cn = 'AddTo' + this.modelName + 'Panel';
        
        Tine[this.app.name][cn].openWindow({
            count: count, selectionFilter: filter, addRelations: addRelations, callingApp: 'Addressbook', callingModel: 'Contact'
        });
    },
    
    /**
     * returns the current filter if is filter selection
     * @param {Tine.widgets.MainScreen}
     * @return {Object}
     */
    getFilter: function(ms) {
        var sm = this.getContactGridPanel().selectionModel,
            filter = null;
        
        if (sm.isFilterSelect) {
            var filter = sm.getSelectionFilter();
        }
        return filter;
    },
    
    /**
     * returns selected contacts
     * @returns {Array}
     */
    getSelectionsAsArray: function() {
        var contacts = this.getContactGridPanel().grid.getSelectionModel().getSelections(),
            cont = [];
        
        Ext.each(contacts, function(contact) {
           if (contact.data) cont.push(contact.data);
        });
        
        return cont;
    },
    
    /**
     * add to action updater the first time we render
     */
    onRender: function() {
        var actionUpdater = this.getContactGridPanel().actionUpdater,
            registeredActions = actionUpdater.actions;

        if (registeredActions.indexOf(this.addRecordAction) < 0) {
            actionUpdater.addActions([this.addRecordAction]);
        }

        if (registeredActions.indexOf(this.newRecordAction) < 0) {
            actionUpdater.addActions([this.newRecordAction]);
        }
    }
});/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.Application
 * @extends     Tine.Tinebase.Application
 * Addressbook Application Object <br>
 * 
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Addressbook.Application = Ext.extend(Tine.Tinebase.Application, {
    
    /**
     * auto hook text i18n._('New Contact')
     */
    addButtonText: 'New Contact',
    
    /**
     * Get translated application title of the calendar application
     * 
     * @return {String}
     */
    getTitle: function() {
        return this.i18n.ngettext('Addressbook', 'Addressbooks', 1);
    },

    /** 
     * Overide get main screen to allow for feature gating
     *
     **/
    getMainScreen: function() {
        var mainscreen = Tine.Addressbook.Application.superclass.getMainScreen.call(this);

        if (!Tine.Tinebase.appMgr.get('Addressbook').featureEnabled('featureListView')) {
            mainscreen.useModuleTreePanel = false;
        };

        return mainscreen;
    },

    registerCoreData: function() {
        Tine.CoreData.Manager.registerGrid('adb_lists', Tine.Addressbook.ListGridPanel, {
            app: this,
            initialLoadAfterRender: false
        });

        Tine.CoreData.Manager.registerGrid(
            'adb_list_roles',
            Tine.widgets.grid.GridPanel,
            {
                recordClass: Tine.Addressbook.Model.ListRole,
                app: this,
                initialLoadAfterRender: false,
                // TODO move this to a generic place
                gridConfig: {
                    autoExpandColumn: 'name',
                    columns: [{
                        id: 'id',
                        header: this.i18n._("ID"),
                        width: 150,
                        sortable: true,
                        dataIndex: 'id',
                        hidden: true
                    }, {
                        id: 'name',
                        header: this.i18n._("Name"),
                        width: 300,
                        sortable: true,
                        dataIndex: 'name'
                    }, {
                        id: 'description',
                        header: this.i18n._("Description"),
                        width: 300,
                        sortable: true,
                        dataIndex: 'description',
                        hidden: true
                    }]
                }
            }
        );
    }
});

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.MainScreen
 * @extends     Tine.widgets.MainScreen
 * MainScreen of the Addressbook Application <br>
 * 
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Addressbook.MainScreen = Ext.extend(Tine.widgets.MainScreen, {
    activeContentType: 'Contact',
    contentTypes: [
        {model: 'Contact',  requiredRight: null, singularContainerMode: false},
        {model: 'List',  requiredRight: null, singularContainerMode: false}
    ]
});

Tine.Addressbook.ContactTreePanel = function(config) {
    Ext.apply(this, config);
    
    this.id = 'Addressbook_Contact_Tree';
    this.filterMode = 'filterToolbar';
    this.recordClass = Tine.Addressbook.Model.Contact;
    Tine.Addressbook.ContactTreePanel.superclass.constructor.call(this);
};
Ext.extend(Tine.Addressbook.ContactTreePanel , Tine.widgets.container.TreePanel);

Tine.Addressbook.ListTreePanel = function(config) {
    Ext.apply(this, config);
    
    this.id = 'Addressbook_List_Tree';
    this.filterMode = 'filterToolbar';
    this.recordClass = Tine.Addressbook.Model.List;
    Tine.Addressbook.ListTreePanel.superclass.constructor.call(this);
};
Ext.extend(Tine.Addressbook.ListTreePanel , Tine.widgets.container.TreePanel);


Tine.Addressbook.handleRequestException = Tine.Tinebase.ExceptionHandler.handleRequestException;

Tine.Addressbook.ContactFilterPanel = function(config) {
    Ext.apply(this, config);
    Tine.Addressbook.ContactFilterPanel.superclass.constructor.call(this);
};

Ext.extend(Tine.Addressbook.ContactFilterPanel, Tine.widgets.persistentfilter.PickerPanel, {
    filter: [{field: 'model', operator: 'equals', value: 'Addressbook_Model_ContactFilter'}]
});

Tine.Addressbook.ListFilterPanel = function(config) {
    Ext.apply(this, config);
    Tine.Addressbook.ListFilterPanel.superclass.constructor.call(this);
};

Ext.extend(Tine.Addressbook.ListFilterPanel, Tine.widgets.persistentfilter.PickerPanel, {
    filter: [{field: 'model', operator: 'equals', value: 'Addressbook_Model_ListFilter'}]
});
/**
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 * TODO         add preference for sending mails with felamimail or mailto?
 */
 
Ext.ns('Tine.Addressbook');

/**
 * the details panel (shows contact details)
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ContactGridDetailsPanel
 * @extends     Tine.widgets.grid.DetailsPanel
 */
Tine.Addressbook.ContactGridDetailsPanel = Ext.extend(Tine.widgets.grid.DetailsPanel, {
    
    il8n: null,
    felamimail: false,
    
    /**
     * init
     */
    initComponent: function() {

        // init templates
        this.initTemplate();
        this.initDefaultTemplate();
        
        Tine.Addressbook.ContactGridDetailsPanel.superclass.initComponent.call(this);
    },

    /**
     * add on click event after render
     */
    afterRender: function() {
        Tine.Addressbook.ContactGridDetailsPanel.superclass.afterRender.apply(this, arguments);
        
        if (this.felamimail === true) {
            this.body.on('click', this.onClick, this);
        }
    },
    
    /**
     * init default template
     */
    initDefaultTemplate: function() {
        
        this.defaultTpl = new Ext.XTemplate(
            '<div class="preview-panel-timesheet-nobreak">',    
                '<!-- Preview contacts -->',
                '<div class="preview-panel preview-panel-timesheet-left">',
                    '<div class="bordercorner_1"></div>',
                    '<div class="bordercorner_2"></div>',
                    '<div class="bordercorner_3"></div>',
                    '<div class="bordercorner_4"></div>',
                    '<div class="preview-panel-declaration">' + this.il8n._('Contacts') + '</div>',
                    '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                        '<span class="preview-panel-bold">',
                            this.il8n._('Select contact') + '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                        '</span>',
                    '</div>',
                    '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                        '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                        '</span>',
                    '</div>',
                '</div>',
                '<!-- Preview xxx -->',
                '<div class="preview-panel-timesheet-right">',
                    '<div class="bordercorner_gray_1"></div>',
                    '<div class="bordercorner_gray_2"></div>',
                    '<div class="bordercorner_gray_3"></div>',
                    '<div class="bordercorner_gray_4"></div>',
                    '<div class="preview-panel-declaration"></div>',
                    '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                        '<span class="preview-panel-bold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                        '</span>',
                    '</div>',
                    '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                        '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                        '</span>',
                    '</div>',
                '</div>',
            '</div>'        
        );
    },
    
    /**
     * init single contact template (this.tpl)
     */
    initTemplate: function() {
        this.tpl = new Ext.XTemplate(
            '<tpl for=".">',
                '<div class="preview-panel-adressbook-nobreak">',
                '<div class="preview-panel-left">',                
                    '<!-- Preview image -->',
                    '<div class="preview-panel preview-panel-left preview-panel-image">',
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<img src="{[this.getImageUrl(values.jpegphoto, 90, 113, values)]}"/>',
                    '</div>',
                
                    '<!-- Preview office -->',
                    '<div class="preview-panel preview-panel-office preview-panel-left">',                
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<div class="preview-panel-declaration">' + this.il8n._('Company') + '</div>',
                        '<div class="preview-panel-address preview-panel-left">',
                            '<span class="preview-panel-bold">{[this.encode(values.org_name, "mediumtext")]}{[this.encode(values.org_unit, "prefix", " / ")]}</span><br/>',
                            '{[this.encode(values.adr_one_street)]}<br/>',
                            '{[this.encode(values.adr_one_postalcode, " ")]}{[this.encode(values.adr_one_locality)]}<br/>',
                            '{[this.encode(values.adr_one_region, " / ")]}{[this.encode(values.adr_one_countryname, "country")]}<br/>',
                        '</div>',
                        '<div class="preview-panel-contact preview-panel-right">',
                            '<span class="preview-panel-symbolcompare">' + this.il8n._('Phone') + '</span>{[this.encode(values.tel_work)]}<br/>',
                            '<span class="preview-panel-symbolcompare">' + this.il8n._('Mobile') + '</span>{[this.encode(values.tel_cell)]}<br/>',
                            '<span class="preview-panel-symbolcompare">' + this.il8n._('Fax') + '</span>{[this.encode(values.tel_fax)]}<br/>',
                            '<span class="preview-panel-symbolcompare">' + this.il8n._('E-Mail') 
                                + '</span>{[this.getMailLink(values.email, ' + this.felamimail + ')]}<br/>',
                            '<span class="preview-panel-symbolcompare">' + this.il8n._('Web') + '</span><a href="{[this.encode(values.url, "href")]}" target="_blank">{[this.encode(values.url, "shorttext")]}</a><br/>',
                        '</div>',
                    '</div>',
                '</div>',

                '<!-- Preview privat -->',
                '<div class="preview-panel preview-panel-privat preview-panel-left">',                
                    '<div class="bordercorner_1"></div>',
                    '<div class="bordercorner_2"></div>',
                    '<div class="bordercorner_3"></div>',
                    '<div class="bordercorner_4"></div>',
                    '<div class="preview-panel-declaration">' + this.il8n._('Private') + '</div>',
                    '<div class="preview-panel-address preview-panel-left">',
                        '<span class="preview-panel-bold">{[this.encode(values.n_fn)]}</span><br/>',
                        '{[this.encode(values.adr_two_street)]}<br/>',
                        '{[this.encode(values.adr_two_postalcode, " ")]}{[this.encode(values.adr_two_locality)]}<br/>',
                        '{[this.encode(values.adr_two_region, " / ")]}{[this.encode(values.adr_two_countryname, "country")]}<br/>',
                    '</div>',
                    '<div class="preview-panel-contact preview-panel-right">',
                        '<span class="preview-panel-symbolcompare">' + this.il8n._('Phone') + '</span>{[this.encode(values.tel_home)]}<br/>',
                        '<span class="preview-panel-symbolcompare">' + this.il8n._('Mobile') + '</span>{[this.encode(values.tel_cell_private)]}<br/>',
                        '<span class="preview-panel-symbolcompare">' + this.il8n._('Fax') + '</span>{[this.encode(values.tel_fax_home)]}<br/>',
                        '<span class="preview-panel-symbolcompare">' + this.il8n._('E-Mail') 
                            + '</span>{[this.getMailLink(values.email_home, ' + this.felamimail + ')]}<br/>',
                        '<span class="preview-panel-symbolcompare">' + this.il8n._('Web') + '</span><a href="{[this.encode(values.url, "href")]}" target="_blank">{[this.encode(values.url_home, "shorttext")]}</a><br/>',
                    '</div>',                
                '</div>',
                
                '<!-- Preview info -->',
                '<div class="preview-panel-description preview-panel-left" ext:qtip="{[this.encode(values.note)]}">',
                    '<div class="bordercorner_gray_1"></div>',
                    '<div class="bordercorner_gray_2"></div>',
                    '<div class="bordercorner_gray_3"></div>',
                    '<div class="bordercorner_gray_4"></div>',
                    '<div class="preview-panel-declaration">' + this.il8n._('Info') + '</div>',
                    '{[this.encode(values.note, "longtext")]}',
                '</div>',
                '</div>',
                //  '{[this.getTags(values.tags)]}',
            '</tpl>',
            {
                /**
                 * encode
                 */
                encode: function(value, type, prefix) {
                    //var metrics = Ext.util.TextMetrics.createInstance('previewPanel');
                    if (value) {
                        if (type) {
                            switch (type) {
                                case 'country':
                                    value = Locale.getTranslationData('CountryList', value);
                                    break;
                                case 'longtext':
                                    value = Ext.util.Format.ellipsis(value, 135);
                                    break;
                                case 'mediumtext':
                                    value = Ext.util.Format.ellipsis(value, 30);
                                    break;
                                case 'shorttext':
                                    //console.log(metrics.getWidth(value));
                                    value = Ext.util.Format.ellipsis(value, 18);
                                    break;
                                case 'prefix':
                                    if (prefix) {
                                        value = prefix + value;
                                    }
                                    break;
                                case 'href':
                                    if (! String(value).match(/^(https?|ftps?)/)) {
                                        var adb = Tine.Tinebase.appMgr.get('Addressbook');
                                        return "javascript:Ext.Msg.alert('" + adb.i18n._('Insecure link') + "', '" + adb.i18n._('Please review this link in edit dialog.') + "');";
                                    }
                                    break;
                                default:
                                    value += type;
                            }
                        }
                        value = Ext.util.Format.htmlEncode(value);
                        return Ext.util.Format.nl2br(value);
                    } else {
                        return '';
                    }
                },
                
                /**
                 * get tags
                 * 
                 * TODO make it work
                 */
                getTags: function(value) {
                    var result = '';
                    for (var i=0; i<value.length; i++) {
                        result += value[i].name + ' ';
                    }
                    return result;
                },
                
                /**
                 * get image url
                 */
                getImageUrl: function(url, width, height, contact) {
                    var mtime = contact.last_modified_time || contact.creation_time;
                    if (url.match(/&/)) {
                        url = Ext.ux.util.ImageURL.prototype.parseURL(url);
                        url.width = width;
                        url.height = height;
                        url.ratiomode = 0;
                        url.mtime = Ext.isDate(mtime) ? mtime.getTime() : new Date().getTime();
                    }
                    return url;
                },

                /**
                 * get email link
                 */
                getMailLink: function(email, felamimail) {
                    if (! email) {
                        return '';
                    }
                    
                    email = this.encode(email);
                    var link = (felamimail === true) ? '#' : 'mailto:' + email;
                    var id = Ext.id() + ':' + email;
                    
                    return '<a href="' + link + '" class="tinebase-email-link" id="' + id + '">'
                        + Ext.util.Format.ellipsis(email, 18) + '</a>';
                }
            }
        );
    },
    
    /**
     * on click for compose mail
     * 
     * @param {} e
     * 
     * TODO check if account is configured?
     * TODO generalize that
     */
    onClick: function(e) {
        var target = e.getTarget('a[class=tinebase-email-link]');
        if (target) {
            var email = target.id.split(':')[1];
            var defaults = Tine.Felamimail.Model.Message.getDefaultData();
            defaults.to = [email];
            defaults.body = Tine.Felamimail.getSignature();
            
            var record = new Tine.Felamimail.Model.Message(defaults, 0);
            var popupWindow = Tine.Felamimail.MessageEditDialog.openWindow({
                record: record
            });
        }
    }
});
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Addressbook');

/**
 * Contact grid panel
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ContactGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Contact Grid Panel</p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.ContactGridPanel
 */
Tine.Addressbook.ContactGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
    /**
     * record class
     * @cfg {Tine.Addressbook.Model.Contact} recordClass
     */
    recordClass: Tine.Addressbook.Model.Contact,
    
    /**
     * grid specific
     * @private
     */ 
    defaultSortInfo: {field: 'n_fileas', direction: 'ASC'},
    gridConfig: {
        autoExpandColumn: 'n_fileas',
        enableDragDrop: true,
        ddGroup: 'containerDDGroup'
    },
    copyEditAction: true,
    felamimail: false,
    multipleEdit: true,
    duplicateResolvable: true,
    
    /**
     * @cfg {Bool} hasDetailsPanel 
     */
    hasDetailsPanel: true,
    
    /**
     * inits this cmp
     * @private
     */
    initComponent: function() {
        this.recordProxy = Tine.Addressbook.contactBackend;
        
        // check if felamimail is installed and user has run right and wants to use felamimail in adb
        if (Tine.Felamimail && Tine.Tinebase.common.hasRight('run', 'Felamimail') && Tine.Felamimail.registry.get('preferences').get('useInAdb')) {
            this.felamimail = (Tine.Felamimail.registry.get('preferences').get('useInAdb') == 1);
        }
        this.gridConfig.cm = this.getColumnModel();

        if (this.hasDetailsPanel) {
            this.detailsPanel = this.getDetailsPanel();
        }

        Tine.Addressbook.ContactGridPanel.superclass.initComponent.call(this);
    },
    
    /**
     * returns column model
     * 
     * @return Ext.grid.ColumnModel
     * @private
     */
    getColumnModel: function() {
        return new Ext.grid.ColumnModel({
            defaults: {
                sortable: true,
                hidden: true,
                resizable: true
            },
            columns: this.getColumns()
        });
    },
    
    /**
     * returns array with columns
     * 
     * @return {Array}
     */
    getColumns: function() {
        return Tine.Addressbook.ContactGridPanel.getBaseColumns(this.app.i18n)
            .concat(this.getModlogColumns().concat(this.getCustomfieldColumns()));
    },
    
    /**
     * @private
     */
    initActions: function() {
        this.actions_exportContact = new Ext.Action({
            requiredGrant: 'exportGrant',
            text: String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 50), this.i18nRecordsName),
            singularText: String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 1), this.i18nRecordName),
            pluralText:  String.format(this.app.i18n.ngettext('Export {0}', 'Export {0}', 1), this.i18nRecordsName),
            translationObject: this.app.i18n,
            iconCls: 'action_export',
            scope: this,
            disabled: true,
            allowMultiple: true,
            menu: {
                items: [
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as PDF'),
                        iconCls: 'action_exportAsPdf',
                        format: 'pdf',
                        exportFunction: 'Addressbook.exportContacts',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as CSV'),
                        iconCls: 'tinebase-action-export-csv',
                        format: 'csv',
                        exportFunction: 'Addressbook.exportContacts',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ODS'),
                        format: 'ods',
                        iconCls: 'tinebase-action-export-ods',
                        exportFunction: 'Addressbook.exportContacts',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as XLS'),
                        format: 'xls',
                        iconCls: 'tinebase-action-export-xls',
                        exportFunction: 'Addressbook.exportContacts',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as DOC'),
                        format: 'doc',
                        iconCls: 'tinebase-action-export-doc',
                        exportFunction: 'Addressbook.exportContacts',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ...'),
                        iconCls: 'tinebase-action-export-xls',
                        exportFunction: 'Addressbook.exportContacts',
                        showExportDialog: true,
                        gridPanel: this
                    })
                ]
            }
        });

        this.actions_import = new Ext.Action({
            //requiredGrant: 'addGrant',
            text: this.app.i18n._('Import contacts'),
            disabled: false,
            handler: this.onImport,
            iconCls: 'action_import',
            scope: this,
            allowMultiple: true
        });
        
        // register actions in updater
        this.actionUpdater.addActions([
            this.actions_exportContact,
            this.actions_import
        ]);
        
        Tine.Addressbook.ContactGridPanel.superclass.initActions.call(this);
    },

    /**
     * get default / selected addressbook container
     *
     * @returns {Object|Tine.Tinebase.Model.Container}
     */
    getDefaultContainer: function() {
        return this.app.getMainScreen().getWestPanel().getContainerTreePanel().getDefaultContainer('defaultAddressbook');
    },
    
    /**
     * add custom items to action toolbar
     * 
     * @return {Object}
     */
    getActionToolbarItems: function() {
        return [
            {
                xtype: 'buttongroup',
                columns: 1,
                frame: false,
                items: [
                    this.actions_exportContact,
                    this.actions_import
                ]
            }
        ];
    },
    
    /**
     * add custom items to context menu
     * 
     * @return {Array}
     */
    getContextMenuItems: function() {
        var items = [
            '-',
            this.actions_exportContact,
            '-'
        ];
        
        return items;
    },

    /**
     * returns details panel
     * 
     * @private
     * @return {Tine.Addressbook.ContactGridDetailsPanel}
     */
    getDetailsPanel: function() {
        return new Tine.Addressbook.ContactGridDetailsPanel({
            gridpanel: this,
            il8n: this.app.i18n,
            felamimail: this.felamimail
        });
    }
});

// Static Methods

/**
 * tid renderer
 * 
 * @private
 * @return {String} HTML
 */
Tine.Addressbook.ContactGridPanel.contactTypeRenderer = function(data, cell, record) {
    var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n,
        hasAccount = ((record.get && record.get('account_id')) || record.account_id),
        cssClass = hasAccount ? 'renderer_typeAccountIcon' : 'renderer_typeContactIcon',
        qtipText = Tine.Tinebase.common.doubleEncode(hasAccount ? i18n._('Contact of a user account') : i18n._('Contact'));
    
    return '<div ext:qtip="' + qtipText + '" style="background-position:0px;" class="' + cssClass + '">&#160</div>';
};

Tine.Addressbook.ContactGridPanel.displayNameRenderer = function(data) {
    var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n;
    return data ? Ext.util.Format.htmlEncode(data) : ('<div class="renderer_displayNameRenderer_noName">' + i18n._('No name') + '</div>');
};

Tine.Addressbook.ContactGridPanel.countryRenderer = function(data) {
    data = Locale.getTranslationData('CountryList', data);
    return Ext.util.Format.htmlEncode(data);
};

/**
 * Statically constructs the columns used to represent a contact. Reused by ListMemberGridPanel + ListMemberRoleGridPanel
 */
Tine.Addressbook.ContactGridPanel.getBaseColumns = function(i18n) {
    return [
        { id: 'type', header: i18n._('Type'), dataIndex: 'type', width: 30, renderer: Tine.Addressbook.ContactGridPanel.contactTypeRenderer.createDelegate(this), hidden: false },
        { id: 'tags', header: i18n._('Tags'), dataIndex: 'tags', width: 50, renderer: Tine.Tinebase.common.tagsRenderer, sortable: false, hidden: false  },
        { id: 'salutation', header: i18n._('Salutation'), dataIndex: 'salutation', renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Addressbook', 'contactSalutation') },
        {
            id: 'container_id',
            dataIndex: 'container_id',
            header: Tine.Addressbook.Model.Contact.getContainerName(),
            width: 150,
            renderer: Tine.Tinebase.common.containerRenderer
        },
        { id: 'n_prefix', header: i18n._('Title'), dataIndex: 'n_prefix', width: 80 },
        { id: 'n_middle', header: i18n._('Middle Name'), dataIndex: 'n_middle', width: 80 },
        { id: 'n_family', header: i18n._('Last Name'), dataIndex: 'n_family' },
        { id: 'n_given', header: i18n._('First Name'), dataIndex: 'n_given', width: 80 },
        { id: 'n_fn', header: i18n._('Full Name'), dataIndex: 'n_fn', renderer: Tine.Addressbook.ContactGridPanel.displayNameRenderer },
        { id: 'n_fileas', header: i18n._('Display Name'), dataIndex: 'n_fileas', hidden: false, renderer: Tine.Addressbook.ContactGridPanel.displayNameRenderer},
        { id: 'org_name', header: i18n._('Company'), dataIndex: 'org_name', width: 120, hidden: false },
        { id: 'org_unit', header: i18n._('Unit'), dataIndex: 'org_unit'  },
        { id: 'title', header: i18n._('Job Title'), dataIndex: 'title' },
//            { id: 'role', header: i18n._('Job Role'), dataIndex: 'role' },
//            { id: 'room', header: i18n._('Room'), dataIndex: 'room' },
        { id: 'adr_one_street', header: i18n._('Street'), dataIndex: 'adr_one_street' },
        { id: 'adr_one_locality', header: i18n._('City'), dataIndex: 'adr_one_locality', width: 150, hidden: false },
        { id: 'adr_one_region', header: i18n._('Region'), dataIndex: 'adr_one_region' },
        { id: 'adr_one_postalcode', header: i18n._('Postalcode'), dataIndex: 'adr_one_postalcode' },
        { id: 'adr_one_countryname', header: i18n._('Country'), dataIndex: 'adr_one_countryname', renderer: Tine.Addressbook.ContactGridPanel.countryRenderer },
        { id: 'adr_two_street', header: i18n._('Street (private)'), dataIndex: 'adr_two_street' },
        { id: 'adr_two_locality', header: i18n._('City (private)'), dataIndex: 'adr_two_locality' },
        { id: 'adr_two_region', header: i18n._('Region (private)'), dataIndex: 'adr_two_region' },
        { id: 'adr_two_postalcode', header: i18n._('Postalcode (private)'), dataIndex: 'adr_two_postalcode' },
        { id: 'adr_two_countryname', header: i18n._('Country (private)'), dataIndex: 'adr_two_countryname', renderer: Tine.Addressbook.ContactGridPanel.countryRenderer },
        { id: 'email', header: i18n._('Email'), dataIndex: 'email', width: 150, hidden: false },
        { id: 'tel_work', header: i18n._('Phone'), dataIndex: 'tel_work', hidden: false },
        { id: 'tel_cell', header: i18n._('Mobile'), dataIndex: 'tel_cell', hidden: false },
        { id: 'tel_fax', header: i18n._('Fax'), dataIndex: 'tel_fax' },
        { id: 'tel_car', header: i18n._('Car phone'), dataIndex: 'tel_car' },
        { id: 'tel_pager', header: i18n._('Pager'), dataIndex: 'tel_pager' },
        { id: 'tel_home', header: i18n._('Phone (private)'), dataIndex: 'tel_home' },
        { id: 'tel_fax_home', header: i18n._('Fax (private)'), dataIndex: 'tel_fax_home' },
        { id: 'tel_cell_private', header: i18n._('Mobile (private)'), dataIndex: 'tel_cell_private' },
        { id: 'email_home', header: i18n._('Email (private)'), dataIndex: 'email_home' },
        { id: 'url', header: i18n._('Web'), dataIndex: 'url' },
        { id: 'url_home', header: i18n._('URL (private)'), dataIndex: 'url_home' },
        { id: 'note', header: i18n._('Note'), dataIndex: 'note' },
        { id: 'tz', header: i18n._('Timezone'), dataIndex: 'tz' },
        { id: 'geo', header: i18n._('Geo'), dataIndex: 'geo' },
        { id: 'bday', header: i18n._('Birthday'), dataIndex: 'bday', renderer: Tine.Tinebase.common.dateRenderer },
        { id: 'memberroles', header: i18n._('List Roles'), dataIndex: 'memberroles', renderer: Tine.Addressbook.ListMemberRoleRenderer }
    ];
};

/**
 *list member role render
 *
 * @param value
 * @returns {string}
 * @constructor
 */
Tine.Addressbook.ListMemberRoleRenderer = function(value) {
    if (Ext.isArray(value)) {
        var result = [];
        Ext.each(value, function(memberrole) {
            if (memberrole.list_role_id.name) {
                result.push(memberrole.list_role_id.name);
            }
        });
        return result.toString();
    }

    return '';
};
/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2010 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Calendar');

/**
 * @namespace   Tine.Calendar
 * @class       Tine.Addressbook.ContactFilterModel
 * @extends     Tine.widgets.grid.FilterModel
 * 
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Addressbook.ContactFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    /**
     * @property Tine.Tinebase.Application app
     */
    app: null,
    
    field: 'contact_id',
    defaultOperator: 'equals',
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Addressbook.ContactFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        
        this.operators = ['equals'/*, 'notin'*/];
        this.label = this.label || this.app.i18n._('Contact');
    },
    
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        var value = new Tine.Addressbook.SearchCombo({
            app: this.app,
            filter: filter,
            width: 200,
            listWidth: 400,
            listAlign : 'tr-br?',
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el,
            getValue: function() {
                return this.selectedRecord.id;
            },
            onSelect: function(record) {
                this.setValue(record);
                this.collapse();
        
                this.fireEvent('select', this, record);
                if (this.blurOnSelect) {
                    this.fireEvent('blur', this);
                }
            },
            setValue: function(value) {
                this.selectedRecord = value;
                var displayValue = Tine.Calendar.AttendeeGridPanel.prototype.renderAttenderUserName.call(this, value);
                Tine.Addressbook.SearchCombo.superclass.setValue.call(this, displayValue);
            }
        });
        value.on('select', this.onFiltertrigger, this);
        return value;
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['addressbook.contact'] = Tine.Addressbook.ContactFilterModel;

/*
 * Tine 2.0
 *
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

/*global Ext, Tine*/

Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ContactEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * Addressbook Edit Dialog <br>
 *
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 */
Tine.Addressbook.ContactEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {

    /**
     * parse address button
     * @type Ext.Button
     */
    parseAddressButton: null,

    windowNamePrefix: 'ContactEditWindow_',
    appName: 'Addressbook',
    recordClass: Tine.Addressbook.Model.Contact,
    showContainerSelector: true,
    multipleEdit: true,
    displayNotes: true,

    getFormItems: function () {
        if (Tine.Tinebase.configManager.get('mapPanel') && Tine.widgets.MapPanel) {
            this.mapPanel = new Tine.Addressbook.MapPanel({
                layout: 'fit',
                title: this.app.i18n._('Map'),
                disabled: (Ext.isEmpty(this.record.get('adr_one_lon')) || Ext.isEmpty(this.record.get('adr_one_lat'))) && (Ext.isEmpty(this.record.get('adr_two_lon')) || Ext.isEmpty(this.record.get('adr_two_lat')))
            });

            Tine.widgets.dialog.MultipleEditDialogPlugin.prototype.registerSkipItem(this.mapPanel);

        } else {
            this.mapPanel = new Ext.Panel({
                layout: 'fit',
                title: this.app.i18n._('Map'),
                disabled: true,
                html: ''
            });
        }

        return {
            xtype: 'tabpanel',
            border: false,
            plain: true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            defaults: {
                hideMode: 'offsets'
            },
            items: [{
                title: this.app.i18n.n_('Contact', 'Contacts', 1),
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    layout: 'border',
                    items: [{
                        xtype: 'fieldset',
                        region: 'north',
                        autoHeight: true,
                        title: this.app.i18n._('Personal Information'),
                        items: [{
                            xtype: 'panel',
                            layout: 'hbox',
                            align: 'stretch',
                            items: [{
                                flex: 1,
                                xtype: 'columnform',
                                autoHeight: true,
                                style:'padding-right: 5px;',
                                items: [[
                                    new Tine.Tinebase.widgets.keyfield.ComboBox({
                                        fieldLabel: this.app.i18n._('Salutation'),
                                        name: 'salutation',
                                        app: 'Addressbook',
                                        keyFieldName: 'contactSalutation',
                                        value: '',
                                        columnWidth: 0.35,
                                        listeners: {
                                            scope: this,
                                            'select': function (combo, record, index) {
                                                var jpegphoto = this.getForm().findField('jpegphoto');
                                                // set new empty photo depending on chosen salutation only if user doesn't have own image
                                                jpegphoto.setDefaultImage(record.json.image);
                                            }
                                        }
                                }), {
                                    columnWidth: 0.65,
                                    fieldLabel: this.app.i18n._('Title'),
                                    name: 'n_prefix',
                                    maxLength: 64
                                }], [{
                                    columnWidth: 0.35,
                                    fieldLabel: this.app.i18n._('First Name'),
                                    name: 'n_given',
                                    maxLength: 64
                                }, {
                                    columnWidth: 0.30,
                                    fieldLabel: this.app.i18n._('Middle Name'),
                                    name: 'n_middle',
                                    maxLength: 64
                                }, {
                                    columnWidth: 0.35,
                                    fieldLabel: this.app.i18n._('Last Name'),
                                    name: 'n_family',
                                    maxLength: 255
                                }], [{
                                    columnWidth: 0.65,
                                    xtype: 'mirrortextfield',
                                    fieldLabel: this.app.i18n._('Company'),
                                    name: 'org_name',
                                    maxLength: 255
                                }, {
                                    columnWidth: 0.35,
                                    fieldLabel: this.app.i18n._('Unit'),
                                    name: 'org_unit',
                                    maxLength: 64
                                }]/* move to seperate tab, [{
                                    columnWidth: .4,
                                    fieldLabel: this.app.i18n._('Suffix'),
                                    name:'n_suffix'
                                }, {
                                    columnWidth: .4,
                                    fieldLabel: this.app.i18n._('Job Role'),
                                    name:'role'
                                }, {
                                    columnWidth: .2,
                                    fieldLabel: this.app.i18n._('Room'),
                                    name:'room'
                                }]*/
                            ]},
                            new Ext.ux.form.ImageField({
                                name: 'jpegphoto',
                                width: 90,
                                height: 120
                            })
                        ]
                        }, {
                            xtype: 'columnform',
                            items: [[
                                {
                                    columnWidth: 0.64,
                                    xtype: 'combo',
                                    fieldLabel: this.app.i18n._('Display Name'),
                                    name: 'n_fn',
                                    disabled: true
                                }, {
                                     columnWidth: 0.36,
                                     fieldLabel: this.app.i18n._('Job Title'),
                                     name: 'title',
                                     maxLength: 64
                                }, {
                                     width: 110,
                                     xtype: 'extuxclearabledatefield',
                                     fieldLabel: this.app.i18n._('Birthday'),
                                     name: 'bday'
                                }
                            ]]
                        }]
                    }, {
                        xtype: 'fieldset',
                        region: 'center',
                        title: this.app.i18n._('Contact Information'),
                        autoScroll: true,
                        items: [{
                            xtype: 'columnform',
                            items: [[{
                                fieldLabel: this.app.i18n._('Phone'),
                                labelIcon: 'images/oxygen/16x16/apps/kcall.png',
                                name: 'tel_work',
                                maxLength: 40
                            }, {
                                fieldLabel: this.app.i18n._('Mobile'),
                                labelIcon: 'images/oxygen/16x16/devices/phone.png',
                                name: 'tel_cell',
                                maxLength: 40
                            }, {
                                fieldLabel: this.app.i18n._('Fax'),
                                labelIcon: 'images/oxygen/16x16/devices/printer.png',
                                name: 'tel_fax',
                                maxLength: 40
                            }], [{
                                fieldLabel: this.app.i18n._('Phone (private)'),
                                labelIcon: 'images/oxygen/16x16/apps/kcall.png',
                                name: 'tel_home',
                                maxLength: 40
                            }, {
                                fieldLabel: this.app.i18n._('Mobile (private)'),
                                labelIcon: 'images/oxygen/16x16/devices/phone.png',
                                name: 'tel_cell_private',
                                maxLength: 40
                            }, {
                                fieldLabel: this.app.i18n._('Fax (private)'),
                                labelIcon: 'images/oxygen/16x16/devices/printer.png',
                                name: 'tel_fax_home',
                                maxLength: 40
                            }], [{
                                fieldLabel: this.app.i18n._('E-Mail'),
                                labelIcon: 'images/oxygen/16x16/actions/kontact-mail.png',
                                name: 'email',
                                vtype: 'email',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('E-Mail (private)'),
                                labelIcon: 'images/oxygen/16x16/actions/kontact-mail.png',
                                name: 'email_home',
                                vtype: 'email',
                                maxLength: 64
                            }, {
                                xtype: 'mirrortextfield',
                                fieldLabel: this.app.i18n._('Web'),
                                labelIcon: 'images/oxygen/16x16/actions/network.png',
                                name: 'url',
                                maxLength: 128,
                                listeners: {
                                    scope: this,
                                    focus: function (field) {
                                        if (! field.getValue()) {
                                            field.setValue('http://www.');
                                            field.selectText.defer(100, field, [7, 11]);
                                        }
                                    },
                                    blur: function (field) {
                                        if (field.getValue() === 'http://www.') {
                                            field.setValue(null);
                                            field.validate();
                                        }
                                        if (field.getValue().indexOf('http://http://') == 0 || field.getValue().indexOf('http://https://') == 0) {
                                            field.setValue(field.getValue().substr(7));
                                            field.validate();
                                        }
                                        if (field.getValue().indexOf('http://www.http://') == 0 || field.getValue().indexOf('http://www.https://') == 0) {
                                            field.setValue(field.getValue().substr(11));
                                            field.validate();
                                        }
                                    }
                                }
                            }]]
                        }]
                    }, {
                        xtype: 'tabpanel',
                        region: 'south',
                        border: false,
                        deferredRender: false,
                        height: 124,
                        split: true,
                        activeTab: 0,
                        defaults: {
                            frame: true
                        },
                        items: [{
                            title: this.app.i18n._('Company Address'),
                            xtype: 'columnform',
                            items: [[{
                                fieldLabel: this.app.i18n._('Street'),
                                name: 'adr_one_street',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('Street 2'),
                                name: 'adr_one_street2',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('Region'),
                                name: 'adr_one_region',
                                maxLength: 64
                            }], [{
                                fieldLabel: this.app.i18n._('Postal Code'),
                                name: 'adr_one_postalcode',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('City'),
                                name: 'adr_one_locality',
                                maxLength: 64
                            }, {
                                xtype: 'widget-countrycombo',
                                fieldLabel: this.app.i18n._('Country'),
                                name: 'adr_one_countryname',
                                maxLength: 64
                            }]]
                        }, {
                            title: this.app.i18n._('Private Address'),
                            xtype: 'columnform',
                            items: [[{
                                fieldLabel: this.app.i18n._('Street'),
                                name: 'adr_two_street',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('Street 2'),
                                name: 'adr_two_street2',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('Region'),
                                name: 'adr_two_region',
                                maxLength: 64
                            }], [{
                                fieldLabel: this.app.i18n._('Postal Code'),
                                name: 'adr_two_postalcode',
                                maxLength: 64
                            }, {
                                fieldLabel: this.app.i18n._('City'),
                                name: 'adr_two_locality',
                                maxLength: 64
                            }, {
                                xtype: 'widget-countrycombo',
                                fieldLabel: this.app.i18n._('Country'),
                                name: 'adr_two_countryname',
                                maxLength: 64
                            }]]
                        }]
                    }]
                }, {
                    // activities and tags
                    // TODO make order of accordion items stateful
                    region: 'east',
                    layout: 'accordion',
                    animate: true,
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                        new Ext.Panel({
                            // @todo generalise!
                            title: this.app.i18n._('Description'),
                            iconCls: 'descriptionIcon',
                            layout: 'form',
                            labelAlign: 'top',
                            border: false,
                            items: [{
                                style: 'margin-top: -4px; border 0px;',
                                labelSeparator: '',
                                xtype: 'textarea',
                                name: 'note',
                                hideLabel: true,
                                grow: false,
                                preventScrollbars: false,
                                anchor: '100% 100%',
                                emptyText: this.app.i18n._('Enter description'),
                                requiredGrant: 'editGrant'
                            }]
                        }),
                        new Tine.widgets.tags.TagPanel({
                            app: 'Addressbook',
                            border: false,
                            bodyStyle: 'border:1px solid #B5B8C8;'
                        })
                    ]
                }]
            }, this.mapPanel,
            new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (this.record && ! this.copyRecord) ? this.record.id : '',
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })
            ]
        };
    },

    /**
     * init component
     */
    initComponent: function () {
        var relatedRecords = {};

        this.initToolbar();

        this.supr().initComponent.apply(this, arguments);
    },

    /**
     * initToolbar
     */
    initToolbar: function() {
        var exportContactButton = new Ext.Action({
            id: 'exportButton',
            text: Tine.Tinebase.appMgr.get('Addressbook').i18n._('Export as pdf'),
            handler: this.onExportContact,
            iconCls: 'action_exportAsPdf',
            disabled: false,
            scope: this
        });
        this.parseAddressButton = new Ext.Action({
            text: Tine.Tinebase.appMgr.get('Addressbook').i18n._('Parse address'),
            handler: this.onParseAddress,
            iconCls: 'action_parseAddress',
            disabled: false,
            scope: this,
            enableToggle: true
        });

        this.tbarItems = [exportContactButton, this.parseAddressButton];
    },

    /**
     * export pdf handler
     */
    onExportContact: function () {
        var downloader = new Ext.ux.file.Download({
            params: {
                method: 'Addressbook.exportContacts',
                filter: Ext.encode([{field: 'id', operator: 'in', value: this.record.id}]),
                options: Ext.util.JSON.encode({
                    format: 'pdf'
                })
            }
        });
        downloader.start();
    },

    /**
     * parse address handler
     *
     * opens message box where user can paste address
     *
     * @param {Ext.Button} button
     */
    onParseAddress: function (button) {
        if (button.pressed) {
            Ext.Msg.prompt(this.app.i18n._('Paste address'), this.app.i18n._('Please paste an address or a URI to a vcard that should be parsed:'), function(btn, text) {
                if (btn == 'ok'){
                    this.parseAddress(text);
                } else if (btn == 'cancel') {
                    button.toggle();
                }
            }, this, 100);
        } else {
            button.setText(this.app.i18n._('Parse address'));
            this.tokenModePlugin.endTokenMode();
        }
    },

    /**
     * send address to server + fills record/form with parsed data + adds unrecognizedTokens to description box
     *
     * @param {String} address
     */
    parseAddress: function(address) {
        Tine.log.debug('parsing address ... ');

        Tine.Addressbook.parseAddressData(address, function(result, response) {
            Tine.log.debug('parsed address:');
            Tine.log.debug(result);

            if (result.hasOwnProperty('exceptions')) {
                Ext.Msg.alert(this.app.i18n._('Failed to parse address!'), this.app.i18n._('The address could not be read.'));
            } else {
                // only set the fields that could be detected
                Ext.iterate(result.contact, function(key, value) {
                    if (value && ! this.record.get(key)) {
                        this.record.set(key, value);
                    }
                }, this);

                var oldNote = (this.record.get('note')) ? this.record.get('note') : '';

                if (result.hasOwnProperty('unrecognizedTokens')) {
                    this.record.set('note', result.unrecognizedTokens.join(' ') + oldNote);
                }

                this.onRecordLoad();

                this.parseAddressButton.setText(this.app.i18n._('End token mode'));
                this.tokenModePlugin.startTokenMode();
            }
        }, this);
    },

    /**
     * onRecordLoad
     */
    onRecordLoad: function () {
        // NOTE: it comes again and again till
        if (this.rendered) {
            var container = this.record.get('container_id');

            // handle default container
            // TODO should be generalized
            if (! this.record.id && ! Ext.isObject(container)) {
                container = this.app.getMainScreen().getWestPanel().getContainerTreePanel().getSelectedContainer('addGrant', Tine.Addressbook.registry.get('defaultAddressbook'));
                this.record.set('container_id', container);
            }

            if (this.mapPanel instanceof Tine.Addressbook.MapPanel) {
                this.mapPanel.onRecordLoad(this.record);
            }
        }
        this.supr().onRecordLoad.apply(this, arguments);
    }
});

/**
 * Opens a new contact edit dialog window
 *
 * @return {Ext.ux.Window}
 */
Tine.Addressbook.ContactEditDialog.openWindow = function (config) {
    var id = (config.record && config.record.id) ? config.record.id : 0;
    var window = Tine.WindowFactory.getWindow({
        width: 800,
        height: 610,
        name: Tine.Addressbook.ContactEditDialog.prototype.windowNamePrefix + id,
        contentPanelConstructor: 'Tine.Addressbook.ContactEditDialog',
        contentPanelConstructorConfig: config
    });
    return window;
};
/*
 * Tine 2.0
 *
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */

/*global Ext, Tine*/

Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListRoleEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * Addressbook Edit Dialog <br>
 *
 * @author      Philipp Schüle <p.schuele@metaways.de>
 */
Tine.Addressbook.ListRoleEditDialog = Ext.extend(Tine.widgets.dialog.SimpleRecordEditDialog, {
    windowNamePrefix: 'ListRoleEditWindow_',
    appName: 'Addressbook',
    recordClass: Tine.Addressbook.Model.ListRole,
});

/**
 * Opens a new contact edit dialog window
 *
 * @return {Ext.ux.Window}
 */
Tine.Addressbook.ListRoleEditDialog.openWindow = function (config) {
    var id = (config.record && config.record.id) ? config.record.id : 0;
    var window = Tine.WindowFactory.getWindow({
        width: 400,
        height: 300,
        name: Tine.Addressbook.ListRoleEditDialog.prototype.windowNamePrefix + id,
        contentPanelConstructor: 'Tine.Addressbook.ListRoleEditDialog',
        contentPanelConstructorConfig: config
    });
    return window;
};
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Frederic Heihoff <heihoff@sh-systems.eu>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Addressbook');

/**
 * List grid panel
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>List Grid Panel</p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Frederic Heihoff <heihoff@sh-systems.eu>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.ListGridPanel
 */
Tine.Addressbook.ListGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
    /**
     * record class
     * @cfg {Tine.Addressbook.Model.List} recordClass
     */
    recordClass: Tine.Addressbook.Model.List,
    
    /**
     * grid specific
     * @private
     */ 
    defaultSortInfo: {field: 'name', direction: 'ASC'},
    copyEditAction: true,
    felamimail: false,
    multipleEdit: false,
    duplicateResolvable: false,
    
    /**
     * @cfg {Bool} hasDetailsPanel 
     */
    hasDetailsPanel: false,
    
    /**
     * inits this cmp
     * @private
     */
    initComponent: function() {
        this.recordProxy = Tine.Addressbook.listBackend;
        
        // check if felamimail is installed and user has run right and wants to use felamimail in adb
        if (Tine.Felamimail && Tine.Tinebase.common.hasRight('run', 'Felamimail') && Tine.Felamimail.registry.get('preferences').get('useInAdb')) {
            this.felamimail = (Tine.Felamimail.registry.get('preferences').get('useInAdb') == 1);
        }
        this.gridConfig.cm = this.getColumnModel();
        this.filterToolbar = this.filterToolbar || this.getFilterToolbar();

        if (this.hasDetailsPanel) {
            this.detailsPanel = this.getDetailsPanel();
        }

        this.plugins = this.plugins || [];
        this.plugins.push(this.filterToolbar);
        
        Tine.Addressbook.ListGridPanel.superclass.initComponent.call(this);
    },
    
    /**
     * returns column model
     * 
     * @return Ext.grid.ColumnModel
     * @private
     */
    getColumnModel: function() {
        return new Ext.grid.ColumnModel({
            defaults: {
                sortable: true,
                hidden: true,
                resizable: true
            },
            columns: this.getColumns()
        });
    },
    
    /**
     * returns array with columns
     * 
     * @return {Array}
     */
    getColumns: function() {
        return [
            { id: 'type', header: this.app.i18n._('Type'), dataIndex: 'type', width: 30, renderer: Tine.Addressbook.ListGridPanel.listTypeRenderer, hidden: false },
            { id: 'tags', header: this.app.i18n._('Tags'), dataIndex: 'tags', width: 50, renderer: Tine.Tinebase.common.tagsRenderer, sortable: false, hidden: false },
            { id: 'name', header: this.app.i18n._('Name'), dataIndex: 'name', width: 30, hidden: false },
            { id: 'list_type', header: this.app.i18n._('List type'), dataIndex: 'list_type', width: 30, renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Addressbook', 'listType'), hidden: false },
            { id: 'emails', header: this.app.i18n._('Emails'), dataIndex: 'emails', hidden: false, renderer: function(value) {
                if (! value) {
                    return '';
                }
                // TODO should be fixed on server side
                // remove leading comma
                return value.replace(/^,/, '');
            }},
        ].concat(this.getModlogColumns().concat(this.getCustomfieldColumns()));
    },
    
    /**
     * @private
     */
    initActions: function() {        
        Tine.Addressbook.ListGridPanel.superclass.initActions.call(this);
    },

    /**
     * returns details panel
     * 
     * @private
     * @return {Tine.Addressbook.ListGridDetailsPanel}
     */
    getDetailsPanel: function() {
        return new Tine.Addressbook.ListGridDetailsPanel({
            gridpanel: this,
            il8n: this.app.i18n
        });
    }
});

/**
 * list type renderer
 *
 * @private
 * @return {String} HTML
 */
Tine.Addressbook.ListGridPanel.listTypeRenderer = function(data, cell, record) {
    var i18n = Tine.Tinebase.appMgr.get('Addressbook').i18n,
        type = ((record.get && record.get('type')) || record.type),
        cssClass = type == 'group' ? 'renderer_typeGroupIcon' : 'renderer_typeListIcon',
        qtipText = Tine.Tinebase.common.doubleEncode(type == 'group' ? i18n._('System Group') : i18n._('Group'));

    return '<div ext:qtip="' + qtipText + '" style="background-position:0px;" class="' + cssClass + '">&#160</div>';
};
/**
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Frederic Heihoff <heihoff@sh-systems.eu>
 * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Addressbook');

/**
 * the details panel (shows List details)
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListGridDetailsPanel
 * @extends     Tine.widgets.grid.DetailsPanel
 */
Tine.Addressbook.ListGridDetailsPanel = Ext.extend(Tine.widgets.grid.DetailsPanel, {
    
    il8n: null,
    felamimail: false,
    
    /**
     * init
     */
    initComponent: function() {

        // init templates
        this.initTemplate();
        this.initDefaultTemplate();
        
        Tine.Addressbook.ListGridDetailsPanel.superclass.initComponent.call(this);
    },

    /**
     * add on click event after render
     */
    afterRender: function() {
        Tine.Addressbook.ListGridDetailsPanel.superclass.afterRender.apply(this, arguments);
    },
    
    /**
     * init default template
     */
    initDefaultTemplate: function() {
        
        this.defaultTpl = new Ext.XTemplate(
            '<div class="preview-panel-list-nobreak">',    
                '<!-- Preview contacts -->',
                '<div class="preview-panel preview-panel-list-left">',
                    '<div class="bordercorner_1"></div>',
                    '<div class="bordercorner_2"></div>',
                    '<div class="bordercorner_3"></div>',
                    '<div class="bordercorner_4"></div>',
                    '<div class="preview-panel-declaration">' + this.il8n._('Lists') + '</div>',
                    '<div class="preview-panel-list-leftside preview-panel-left">',
                        '<span class="preview-panel-bold">',
                            this.il8n._('Select list') + '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                        '</span>',
                    '</div>',
                '</div>',
            '</div>'        
        );
    },
    
    /**
     * init single List template (this.tpl)
     */
    initTemplate: function() {
        this.tpl = new Ext.XTemplate(
            '<div class="preview-panel-list-nobreak">',    
                '<!-- Preview contacts -->',
                '<div class="preview-panel preview-panel-list-left">',
                    '<div class="bordercorner_1"></div>',
                    '<div class="bordercorner_2"></div>',
                    '<div class="bordercorner_3"></div>',
                    '<div class="bordercorner_4"></div>',
                    '<div class="preview-panel-declaration">' + this.il8n._('Lists') + '</div>',
                    '<div class="preview-panel-list-leftside preview-panel-left" style="width: 100%">',
                        '<span class="preview-panel-bold">',
                            this.il8n._('List') + '<br/>',
                        '</span>',
                        '<div style="word-wrap:break-word;">{[values.emails]}</div>',
                    '</div>',
                '</div>',
            '</div>'
        );
    }
});
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
Ext.ns('Tine.Addressbook');

/**
 * config grid panel
 *
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListMemberRoleLayerCombo
 * @extends     Tine.widgets.grid.LayerCombo
 */
Tine.Addressbook.ListMemberRoleLayerCombo = Ext.extend(Tine.widgets.grid.PickerGridLayerCombo, {

    /**
     * @cfg contact record
     */
    record: null,

    /**
     * @cfg grid record class
     */
    gridRecordClass: Tine.Addressbook.Model.ListRole,

    /**
     * sets values to innerForm (grid)
     */
    setFormValue: function (value) {
        var listRoles = [];
        if (Ext.isArray(value)) {
            Ext.each(value, function (role) {
                listRoles.push(role.list_role_id);
            });
        }

        this.setStoreFromArray(listRoles);
    },

    /**
     * retrieves values from grid
     *
     * @returns {*|Array}
     */
    getFormValue: function () {
        var listRoles = this.getFromStoreAsArray(),
            result = [];

        Ext.each(listRoles, function(role) {
            result.push({
                list_role_id: role,
                contact_id: this.record,
            })
        }, this);

        return result;
    },
});
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListMemberRoleGridPanel
 * @extends     Ext.grid.EditorGridPanel
 * @author      Philipp Schüle <p.schuele@metaways.de>
 */
Tine.Addressbook.ListMemberRoleGridPanel = Ext.extend(Tine.widgets.grid.PickerGridPanel, {

    recordClass: Tine.Addressbook.Model.Contact,
    clicksToEdit: 1,
    enableHdMenu: false,
    autoExpandColumn: 'n_fileas',
    memberroles: null,
    memberRolesPanel: null,

    // the list record
    record: null,

    /**
     * init component
     */
    initComponent: function() {
        this.app = this.app ? this.app : Tine.Tinebase.appMgr.get('Addressbook');

        this.title = this.hasOwnProperty('title') ? this.title : this.app.i18n._('Members');
        this.plugins = this.plugins || [];
        this.plugins.push(new Ext.ux.grid.GridViewMenuPlugin({}));

        this.sm = new Ext.grid.RowSelectionModel({singleSelect:true});

        this.initColumns();
        this.store = new Ext.data.Store({
            autoSave: false,
            fields:  Tine.Addressbook.Model.Contact,
            proxy: Tine.Addressbook.contactBackend,
            reader: Tine.Addressbook.contactBackend.getReader(),
            listeners: {
                load: this.onLoad,
                scope: this
            }
        });

        this.addListener("beforeedit", this.onBeforeEdit, this);
        this.addListener("afteredit", this.onAfterEdit, this);

        Tine.Addressbook.ListMemberRoleGridPanel.superclass.initComponent.call(this);
    },

    /**
     * before cell edit
     *
     * @param o
     */
    onBeforeEdit: function(o) {
        var ed = this.colModel.getCellEditor(o.column, o.row);
        ed.record = o.record
    },

    /**
     * before cell edit
     *
     * @param o
     */
    onAfterEdit: function(o) {
        o.record.commit();

        // update this.memberroles + memberRolesPanel
        this.memberRolesPanel.setListRolesOfContact(o.record);
    },

    /**
     * initialises grid with an array of member uids
     */
    setMembers: function() {
        var members = this.record.get("members"),
            memberroles = this.record.get("memberroles")

        if (members) {
            var options = {params: {filter: [ { "field":"id","operator":"in", "value": members } ]}};
            this.store.load(options);
            this.store.sort("n_fileas");
        }

        this.memberroles = memberroles;
    },

    onLoad: function(store) {
        if (this.memberroles) {
            this.store.each(function(contact) {
                var contactRoles = [];
                Tine.log.debug(contact);
                // TODO improve detection of matching contact (filter?)
                Ext.each(this.memberroles, function(memberrole) {
                    if (memberrole.contact_id.id == contact.get('id')
                        && memberrole.list_id == this.record.get('id')
                        && memberrole.list_role_id.id
                    )
                    {
                        contactRoles.push(memberrole);
                    }
                }, this);
                if (contactRoles.length > 0) {
                    contact.set('memberroles', contactRoles);
                    contact.commit();
                }
            }, this);

            this.memberRolesPanel.setListRoles(this.memberroles);
        }
    },

    /**
     * returns current array of member uids
     *
     * @return []
     */
    getMembers: function() {
        var result = [],
            roles = null;
        this.memberroles = [];

        this.store.each(function(contact) {
            if (contact.get('id')) {
                result.push(contact.get('id'));
                if (contact.get('memberroles').length > 0) {
                    roles = contact.get('memberroles');
                    Ext.each(roles, function (role) {
                        role.contact_id = contact.get('id');
                        role.list_id = this.record.get('id');
                        this.memberroles.push(role);
                    }, this);
                }
            }
        }, this);

        return result;
    },

    /**
     * returns current array of member roles
     *
     * @return []
     */
    getMemberRoles: function() {
        return this.memberroles;
    },

    /**
     * init columns
     */
    initColumns: function() {
        this.columns = this.getColumns();
        this.editors = [];
        var visibleColumns = ["n_fileas", "email", "memberroles"];
        Ext.each(this.columns, function(value, idx) {
            if (visibleColumns.indexOf(this.columns[idx].id) === -1) {
                this.columns[idx].hidden = true;
            } else {
                this.columns[idx].width = 150;
            }
            if (this.columns[idx].id === "memberroles") {
                this.editors[idx] = new Tine.Addressbook.ListMemberRoleLayerCombo({});
                this.columns[idx].editor = this.editors[idx];
            }
        }, this);
    },

    /**
     * returns array with columns
     * 
     * @return {Array}
     */
    getColumns: function() {
        return Tine.Addressbook.ContactGridPanel.getBaseColumns(this.app.i18n);
    },

    // NOTE: Ext doc seems to be wrong on arguments here
    onContextMenu: function(e, target) {
        e.preventDefault();
        var row = this.getView().findRowIndex(target);
        var contact = this.store.getAt(row);
        if (contact) {
            // don't delete 'add' row
            if (! contact.get('id')) {
                return;
            }

            // select row
            this.getSelectionModel().selectRow(row);

            Tine.log.debug('onContextMenu - contact:');
            Tine.log.debug(contact);

            var items = [{
                text: this.app.i18n._('Remove Member'),
                iconCls: 'action_delete',
                scope: this,
                //disabled: ! this.record.get('editGrant'),
                handler: function() {
                    this.store.removeAt(row);
                }
            }, '-'];

            this.ctxMenu = new Ext.menu.Menu({
                items: items,
                listeners: {
                    scope: this,
                    hide: function() {
                        this.getSelectionModel().clearSelections();
                    }
                }
            });
            this.ctxMenu.showAt(e.getXY());
        }
    }
});
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListRoleGridPanel
 * @extends     Ext.grid.EditorGridPanel
 * @author      Philipp Schüle <p.schuele@metaways.de>
 *
 * NOTE: this is currently only used in ListEditDialog. If we need a separate ListRole grid panel with toolbars, filters,
 * ... this needs to be splitted/renamed.
 */
Tine.Addressbook.ListRoleGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {

    recordClass: Tine.Addressbook.Model.ListRole,
    usePagingToolbar: false,

    autoExpandColumn: 'name',
    memberroles: null,

    // the list record
    record: null,

    // deactivate some fns
    initActions: Ext.emptyFn,
    initFilterPanel: Ext.emptyFn,

    /**
     * init component
     */
    initComponent: function() {
        this.app = this.app ? this.app : Tine.Tinebase.appMgr.get('Addressbook');

        this.gridConfig.cm = new Ext.grid.ColumnModel({
            defaults: {
                resizable: true
            },
            columns: this.getColumns()
        });

        this.title = this.app.i18n._('List Roles')

        Tine.Addressbook.ListRoleGridPanel.superclass.initComponent.call(this);
    },

    getColumns: function() {
        return [{
            id: 'id',
            header: this.app.i18n._("ID"),
            width: 50,
            sortable: true,
            dataIndex: 'id',
            hidden: true
        }, {
            id: 'name',
            header: this.app.i18n._("Name"),
            width: 100,
            sortable: true,
            dataIndex: 'name'
        }, {
            id: 'members',
            header: this.app.i18n._("Members"),
            width: 150,
            sortable: true,
            dataIndex: 'members',
            renderer: this.memberRenderer
        }];
    },

    /**
     * fill store with list roles and the members
     *
     * @param [] memberroles
     */
    setListRoles: function(memberroles) {
        Ext.each(memberroles, function(memberrole) {
            this.updateListRole(memberrole);
        }, this);
    },

    updateListRole: function(memberrole) {
        // check if already in store
        var listRoleId = memberrole.list_role_id.id;

        if (listRoleId) {
            var listRole = this.getStore().getById(listRoleId),
                insertNew = false,
                members = [],
                newMember = true;

            if (! listRole) {
                listRole = new this.recordClass(memberrole.list_role_id);
                insertNew = true;
                members = [memberrole.contact_id];
            } else {
                members = listRole.get('members');

                Ext.each(members, function(member) {
                    if (member.id == memberrole.contact_id.id) {
                        // already id
                        newMember = false;
                    }
                });

                if (! newMember) {
                    return;
                }

                members.push(memberrole.contact_id)
            }

            listRole.set('members', members);
            listRole.commit();

            if (insertNew) {
                this.getStore().insert(0, listRole);
            }
        }
    },

    /**
     * TODO implement removal of a memberrole OR reset role members after each update
     *
     * @param contact
     */
    setListRolesOfContact: function(contact) {
        Ext.each(contact.get('memberroles'), function(memberrole) {
            memberrole.contact_id = Ext.copyTo({}, contact.data, 'id,n_fn');
            this.updateListRole(memberrole);
        }, this);
    },

    memberRenderer: function(value) {
        if (Ext.isArray(value)) {
            var result = [];
            Ext.each(value, function(contact) {
                if (contact) {
                    result.push(contact.n_fn);
                }
            });
            return result.toString();
        }

        return '';
    }
});
/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Frederic Heihoff <heihoff@sh-systems.eu>
 * @copyright   Copyright (c) 2009-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

/*global Ext, Tine*/

Ext.ns('Tine.Addressbook');

/**
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.ListEditDialog
 * @extends     Tine.widgets.dialog.EditDialog
 * Addressbook Edit Dialog <br>
 * 
 * @author      Frederic Heihoff <heihoff@sh-systems.eu>
 */
Tine.Addressbook.ListEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {

    windowNamePrefix: 'ListEditWindow_',
    appName: 'Addressbook',
    recordClass: Tine.Addressbook.Model.List,
    showContainerSelector: true,
    multipleEdit: true,
    displayNotes: true,

    /**
     * init component
     */
    initComponent: function () {
        this.memberGridPanel = new Tine.Addressbook.ListMemberRoleGridPanel({
            region: "center",
            frame: true,
            margins: '6 0 0 0'
        });
        this.memberRolesPanel = new Tine.Addressbook.ListRoleGridPanel({
            region: "south",
            frame: true,
            margins: '6 0 0 0',
            height: 150
        });
        this.supr().initComponent.apply(this, arguments);
    },

    getFormItems: function () {
        return {
            xtype: 'tabpanel',
            border: false,
            plain: true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            items: [{
                title: this.app.i18n.n_('List', 'Lists', 1),
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    layout: 'border',
                    items: [{
                        xtype: 'fieldset',
                        region: 'north',
                        autoHeight: true,
                        title: this.app.i18n._('List Information'),
                        items: [{
                            xtype: 'panel',
                            layout: 'hbox',
                            align: 'stretch',
                            items: [{
                                flex: 1,
                                xtype: 'columnform',
                                autoHeight: true,
                                style:'padding-right: 5px;',
                                items: [[{
                                    columnWidth: 1,
                                    fieldLabel: this.app.i18n._('Name'),
                                    name: 'name',
                                    maxLength: 64
                                }],
                                [new Tine.Tinebase.widgets.keyfield.ComboBox({
                                        columnWidth: 1,
                                        fieldLabel: this.app.i18n._('List type'),
                                        name: 'list_type',
                                        app: 'Addressbook',
                                        keyFieldName: 'listType',
                                        value: ''
                                    })
                                ]]
                            }]
                        }]
                    },
                        // TODO allow user to switch between those two grid panels (card layout?)
                        this.memberGridPanel,
                        this.memberRolesPanel
                    ]
                }, {
                    // activities and tags
                    region: 'east',
                    layout: 'accordion',
                    animate: true,
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                        new Ext.Panel({
                            // @todo generalise!
                            title: this.app.i18n._('Description'),
                            iconCls: 'descriptionIcon',
                            layout: 'form',
                            labelAlign: 'top',
                            border: false,
                            items: [{
                                style: 'margin-top: -4px; border 0px;',
                                labelSeparator: '',
                                xtype: 'textarea',
                                name: 'description',
                                hideLabel: true,
                                grow: false,
                                preventScrollbars: false,
                                anchor: '100% 100%',
                                emptyText: this.app.i18n._('Enter description'),
                                requiredGrant: 'editGrant'
                            }]
                        }),
                        new Tine.widgets.tags.TagPanel({
                            app: 'Addressbook',
                            border: false,
                            bodyStyle: 'border:1px solid #B5B8C8;'
                        })
                    ]
                }]
            },
            new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (this.record && ! this.copyRecord) ? this.record.id : '',
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })
            ]
        };
    },
    
    /**
     * checks if form data is valid
     * 
     * @return {Boolean}
     */
    isValid: function () {
        var form = this.getForm();
        var isValid = true;
        
        // you need to fill in one of: n_given n_family org_name
        // @todo required fields should depend on salutation ('company' -> org_name, etc.)
        //       and not required fields should be disabled (n_given, n_family, etc.)
        if (form.findField('name').getValue() === '') {
            var invalidString = String.format(this.app.i18n._('{0} must be given'), this.app.i18n._('Name'));
            
            form.findField('name').markInvalid(invalidString);
            
            isValid = false;
        }
        
        return isValid && Tine.Addressbook.ListEditDialog.superclass.isValid.apply(this, arguments);
    },
    
    /**
     * onRecordLoad
     */
    onRecordLoad: function () {
        // NOTE: it comes again and again till
        if (this.rendered) {
            this.memberGridPanel.record = this.record;
            if (this.record.id) {
                this.memberGridPanel.setMembers();
                this.memberGridPanel.memberRolesPanel = this.memberRolesPanel;
            }
        }
        this.supr().onRecordLoad.apply(this, arguments);
    },

    /**
     * onRecordUpdate
     */
    onRecordUpdate: function() {
        Tine.Addressbook.ListEditDialog.superclass.onRecordUpdate.apply(this, arguments);
        this.record.set("members", this.memberGridPanel.getMembers());
        this.record.set("memberroles", this.memberGridPanel.getMemberRoles());
    }
});

/**
 * Opens a new List edit dialog window
 * 
 * @return {Ext.ux.Window}
 */
Tine.Addressbook.ListEditDialog.openWindow = function (config) {
    
    // if a container is selected in the tree, take this as default container
    var treeNode = Ext.getCmp('Addressbook_Tree') ? Ext.getCmp('Addressbook_Tree').getSelectionModel().getSelectedNode() : null;
    if (treeNode && treeNode.attributes && treeNode.attributes.container.type) {
        config.forceContainer = treeNode.attributes.container;
    } else {
        config.forceContainer = null;
    }
    
    var id = (config.record && config.record.id) ? config.record.id : 0;
    var window = Tine.WindowFactory.getWindow({
        width: 800,
        height: 610,
        name: Tine.Addressbook.ListEditDialog.prototype.windowNamePrefix + id,
        contentPanelConstructor: 'Tine.Addressbook.ListEditDialog',
        contentPanelConstructorConfig: config
    });
    return window;
};
/*
 * Tine 2.0
 * contacts combo box and store
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Addressbook');

/**
 * contact selection combo box
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.SearchCombo
 * @extends     Ext.form.ComboBox
 * 
 * <p>Contact Search Combobox</p>
 * <p><pre>
 * TODO         make this a twin trigger field with 'clear' button?
 * TODO         add switch to filter for expired/enabled/disabled user accounts
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.SearchCombo
 * 
 * TODO         add     forceSelection: true ?
 */
Tine.Addressbook.ContactSearchCombo = Ext.extend(Tine.Tinebase.widgets.form.RecordPickerComboBox, {
    
    /**
     * @cfg {Boolean} userOnly
     */
    userOnly: false,
    
    /**
     * use account objects/records in get/setValue
     * 
     * @cfg {Boolean} legacy
     * @legacy
     * 
     * TODO remove this later
     */
    useAccountRecord: false,
    allowBlank: false,
    
    itemSelector: 'div.search-item',
    minListWidth: 350,
    
    //private
    initComponent: function(){
        this.app = Tine.Tinebase.appMgr.get('Addressbook');

        if (this.recordClass === null) {
            this.recordClass = Tine.Addressbook.Model.Contact;
            this.recordProxy = Tine.Addressbook.contactBackend;
        }

        this.emptyText = this.emptyText || (this.userOnly ?
            this.app.i18n._('Search for users ...') :
            this.app.i18n._('Search for Contacts ...')
        );

        this.initTemplate();
        Tine.Addressbook.SearchCombo.superclass.initComponent.call(this);
    },
    
    /**
     * is called in accountMode to reset the value
     * @param value
     */
    processValue: function(value) {
        if (this.useAccountRecord) {
            if (value == '') {
                this.accountId = null;
                this.selectedRecord = null;
            }
        }
        return Tine.Addressbook.SearchCombo.superclass.processValue.call(this, value);
    },

    /**
     * use beforequery to set query filter
     * 
     * @param {Event} qevent
     */
    onBeforeQuery: function(qevent){
        Tine.Addressbook.SearchCombo.superclass.onBeforeQuery.apply(this, arguments);

        var contactFilter = {condition: 'AND', filters: this.store.baseParams.filter},
            pathFilter = { field: 'path', operator: 'contains', value: qevent.query };

        this.store.baseParams.filter = [{condition: "OR", filters: [
            contactFilter,
            pathFilter
        ] }];

        if (this.userOnly) {
            this.store.baseParams.filter.push({field: 'type', operator: 'equals', value: 'user'});
        }
    },
    
    /**
     * init template
     * @private
     */
    initTemplate: function() {
        // Custom rendering Template
        if (! this.tpl) {
            this.tpl = new Ext.XTemplate(
                '<tpl for="."><div class="search-item addressbook-search-combo">',
                    '<table>',
                        '<tr>',
                            '<td style="min-width: 20px;">{[Tine.Addressbook.ContactGridPanel.contactTypeRenderer(null, null, values)]}</td>',
                            '<td width="30%"><b>{[Tine.Addressbook.ContactGridPanel.displayNameRenderer(values.n_fileas)]}</b><br/>,' +
                                '{[this.encode(values.org_name)]}</td>',
                            '<td width="25%">{[this.encode(values.adr_one_street)]}<br/>',
                                '{[this.encode(values.adr_one_postalcode)]} {[this.encode(values.adr_one_locality)]}</td>',
                            '<td width="25%">{[this.encode(values.tel_work)]}<br/>{[this.encode(values.tel_cell)]}</td>',
                            '<td width="50px">',
                                '<img width="45px" height="39px" src="{jpegphoto}" />',
                            '</td>',
                        '</tr>',
                    '</table>',
                    '{[Tine.widgets.path.pathsRenderer(values.paths, this.lastQuery)]}',
                '</div></tpl>'
            );
        }
    },
    
    getValue: function() {
        if (this.useAccountRecord) {
            if (this.selectedRecord) {
                return this.selectedRecord.get('account_id');
            } else {
                return this.accountId;
            }
        } else {
            return Tine.Addressbook.SearchCombo.superclass.getValue.call(this);
        }
    },

    setValue: function (value) {
        if (this.useAccountRecord) {
            if (value) {
                if(value.accountId) {
                    // account object
                    this.accountId = value.accountId;
                    value = value.accountDisplayName;
                } else if (typeof(value.get) == 'function') {
                    // account record
                    this.accountId = value.get('id');
                    value = value.get('name');
                }
            } else {
                this.accountId = null;
                this.selectedRecord = null;
            }
        }
        return Tine.Addressbook.SearchCombo.superclass.setValue.call(this, value);
    }

});

// legacy
Tine.Addressbook.SearchCombo = Tine.Addressbook.ContactSearchCombo;

Ext.reg('addressbookcontactpicker', Tine.Addressbook.ContactSearchCombo);
Tine.widgets.form.RecordPickerManager.register('Addressbook', 'Contact', Tine.Addressbook.ContactSearchCombo);
/*
 * Tine 2.0
 * contacts combo box and store
 *
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Addressbook');

Tine.Addressbook.ListSearchCombo = Ext.extend(Tine.Tinebase.widgets.form.RecordPickerComboBox, {

    /**
     * @cfg {Boolean} groupOnly
     */
    groupOnly: false,
    departmentOnly: false,

    //private
    initComponent: function(){
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        this.recordClass = Tine.Addressbook.Model.List;

        this.emptyText = this.emptyText || (this.groupOnly ?
            this.app.i18n._('Search for system groups ...') :
            this.app.i18n._('Search for groups ...')
        );

        this.supr().initComponent.call(this);
    },

    /**
     * use beforequery to set query filter
     *
     * @param {Event} qevent
     */
    onBeforeQuery: function(qevent){
        Tine.Addressbook.SearchCombo.superclass.onBeforeQuery.apply(this, arguments);

        var contactFilter = {condition: 'AND', filters: this.store.baseParams.filter},
            pathFilter = { field: 'path', operator: 'contains', value: qevent.query };

        this.store.baseParams.filter = [{condition: "OR", filters: [
            contactFilter,
            pathFilter
        ] }];

        if (this.groupOnly) {
            this.store.baseParams.filter.push({field: 'type', operator: 'equals', value: 'group'});
        }
        
        if (this.departmentOnly) {
            this.store.baseParams.filter.push({field: 'list_type', operator: 'equals', value: 'department'});
        }
    },

    /**
     * init template
     * @private
     */
    initTemplate: function() {
        if (! this.tpl) {
            this.tpl = new Ext.XTemplate('<tpl for=".">',
                    '<div class="x-combo-list-item">',
                        '<table>',
                            '<tr>',
                                '<td style="min-width: 20px;">{[Tine.Addressbook.ListGridPanel.listTypeRenderer(null, null, values)]}</td>',
                                '<td width="100%">{[this.encode(values.name)]}</td>',
                            '</tr>',
                        '</table>',
                        '{[Tine.widgets.path.pathsRenderer(values.paths, this.lastQuery)]}',
                    '</div>',
                '</tpl>'
            );
        }
    }
});

Tine.widgets.form.RecordPickerManager.register('Addressbook', 'List', Tine.Addressbook.ListSearchCombo);
/* 
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
 */

/*global Ext, Tine*/

Ext.ns('Tine.Addressbook');

Tine.Addressbook.MapPanel = Ext.extend(Ext.Panel, {
    
    /**
     * @property
     */    
    activeMapCard: null,
    
    record: null,
    
    layout: 'fit',
    frame: true,
    
    /**
     * Company address map panel
     * @type {Tine.widgets.MapPanel}
     */
    companyMap: null,
    
    /**
     * Private address map panel
     * @type {Tine.widgets.MapPanel}
     */
    privateMap: null,
    
    initComponent: function () {
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        
        this.title = this.app.i18n._('Map');

        this.defaults = {
            border: false
        };
        
        this.mapCards = new Ext.Panel({
            layout: 'card',
            activeItem: 0,
            items: []
        });

        this.idPrefix = Ext.id();
        
        this.tbar = [{
            id: this.idPrefix + 'tglbtn' + 'companyMap',
            enableToggle: true,
            allowDepress: false,
            text: this.app.i18n._('Company address'),
            handler: this.onMapChange.createDelegate(this, ['companyMap']),
            toggleGroup: this.idPrefix + 'maptglgroup'
        }, ' ', {
            id: this.idPrefix + 'tglbtn' + 'privateMap',
            enableToggle: true,
            allowDepress: false,
            text: this.app.i18n._('Private address'),
            handler: this.onMapChange.createDelegate(this, ['privateMap']),
            toggleGroup: this.idPrefix + 'maptglgroup'
        }];
        
        this.items = this.mapCards;
        
        Tine.Addressbook.MapPanel.superclass.initComponent.call(this);
    },
    
    /**
     * Change active map card
     * 
     * @param {String} map
     */
    onMapChange: function (map) {
        this.mapCards.layout.setActiveItem(this[map]);
        this.mapCards.layout.layout();
        this.activeMapCard = this[map];
    },
    
    /**
     * Called after contact record is loaded to fill map panels
     * 
     * @param {Tine.Addressbook.Model.Contact} record
     */
    onRecordLoad: function (record) {
        this.record = record;
        
        var adrOne = ! Ext.isEmpty(this.record.get('adr_one_lon')) && ! Ext.isEmpty(this.record.get('adr_one_lat')),
            adrTwo = ! Ext.isEmpty(this.record.get('adr_two_lon')) && ! Ext.isEmpty(this.record.get('adr_two_lat')),
            
            btnOne = Ext.getCmp(this.idPrefix + 'tglbtn' + 'companyMap'),
            btnTwo = Ext.getCmp(this.idPrefix + 'tglbtn' + 'privateMap');
        
           // if we have coordinates for company address add map panel
        if (adrOne && ! this.companyMap) {
            Tine.log.debug('Add company address map');
            this.companyMap = new Tine.widgets.MapPanel({
                map: 'companyMap',
                layout: 'fit',
                zoom: 15,
                listeners: {
                    scope: this,
                    'activate': function (p) {
                        if (! p.center) {
                            Tine.log.debug('Loading company address map coordinates: ' + this.record.get('adr_one_lon') + ', ' + this.record.get('adr_one_lat'));
                            p.setCenter(this.record.get('adr_one_lon'), this.record.get('adr_one_lat'));
                        }
                    }
                }
            });
            this.mapCards.add(this.companyMap);
            this.mapCards.doLayout();
        }
        
        // if we have coordinates for private address add map panel
        if (adrTwo && ! this.privateMap) {
            Tine.log.debug('Add private address map');
            this.privateMap = new Tine.widgets.MapPanel({
                map: 'privateMap',
                layout: 'fit',
                zoom: 15,
                listeners: {
                    scope: this,
                    'activate': function (p) {
                        if (! p.center) {
                            Tine.log.debug('Loading private address map coordinates: ' + this.record.get('adr_two_lon') + ', ' + this.record.get('adr_two_lat'));
                            p.setCenter(this.record.get('adr_two_lon'), this.record.get('adr_two_lat'));
                        }
                    }
                }
            });
            this.mapCards.add(this.privateMap);
            this.mapCards.doLayout();
        }
            
        btnOne.toggle(adrOne);
        btnOne.setDisabled(! adrOne);
        
        btnTwo.toggle(! adrOne && adrTwo);
        btnTwo.setDisabled(! adrTwo);
    },
    
    onRecordUpdate: function (record) {
    }
});
/*
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.ns('Tine.Addressbook');

/**
 * render the CardDAV Url into property panel of contianers
 * 
 * @class   Tine.Addressbook.CardDAVContainerPropertiesHookField
 * @extends Ext.form.TextField
 */
Tine.Addressbook.CardDAVContainerPropertiesHookField = Ext.extend(Ext.form.TextField, {

    anchor: '100%',
    readOnly: true,
    
    /**
     * @private
     */
    initComponent: function() {
        this.on('added', this.onContainerAdded, this);

        Tine.Addressbook.CardDAVContainerPropertiesHookField.superclass.initComponent.call(this);
    },
    
    /**
     * @private
     */
    onContainerAdded: function() {
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        this.fieldLabel = this.app.i18n._('CardDAV URL');
        
        this.propertiesDialog = this.findParentBy(function(p) {return !! p.grantContainer});
        this.grantContainer = this.propertiesDialog.grantContainer;
        
        if (this.grantContainer.application_id && this.grantContainer.application_id.name) {
            this.isAddressbook = (this.grantContainer.application_id.name == 'Addressbook');
        } else {
            this.isAddressbook = this.grantContainer.application_id === this.app.id;
        }
        
        this.hidden = ! this.isAddressbook;
        // cardDAV URL
        this.value = [
            window.location.href.replace(/\/?(index\.php.*)?$/, ''),
            '/addressbooks/',
            Tine.Tinebase.registry.get('currentAccount').contact_id,
            '/',
            this.grantContainer.id
        ].join('');
    }
    
});

Ext.ux.ItemRegistry.registerItem('Tine.widgets.container.PropertiesDialog.FormItems.Properties', Tine.Addressbook.CardDAVContainerPropertiesHookField, 100);/*
 * Tine 2.0
 * 
 * @package     Addressbook
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Stefanie Stamer <s.stamer@metaways.de>
 * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.namespace('Tine.Addressbook');

/**
 * admin settings panel
 * 
 * @namespace   Tine.Addressbook
 * @class       Tine.Addressbook.AdminPanel
 * @extends     Ext.TabPanel
 * 
 * <p>Addressbook Admin Panel</p>
 * <p><pre>
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Stefanie Stamer <s.stamer@metaways.de>
 * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Addressbook.AdminPanel
 */
Tine.Addressbook.AdminPanel = Ext.extend(Ext.TabPanel, {

    border: false,
    activeTab: 0,

    /**
     * @private
     */
    initComponent: function() {
        
        this.app = Tine.Tinebase.appMgr.get('Addressbook');
        
        this.items = [
            new Tine.Admin.config.GridPanel({
                configApp: this.app
            })
        ];
        
        Tine.Addressbook.AdminPanel.superclass.initComponent.call(this);
    }
});
    
/**
 * Addressbook Admin Panel Popup
 * 
 * @param   {Object} config
 * @return  {Ext.ux.Window}
 */
Tine.Addressbook.AdminPanel.openWindow = function (config) {
    var window = Tine.WindowFactory.getWindow({
        width: 600,
        height: 470,
        name: 'addressbook-admin-panel',
        contentPanelConstructor: 'Tine.Addressbook.AdminPanel',
        contentPanelConstructorConfig: config
    });
};
