talho/openphin

View on GitHub
app/assets/javascripts/audience/AudiencePanel.js

Summary

Maintainability
F
5 days
Test Coverage
//= require ext_extensions/xActionColumn
//= require ext_extensions/DoNotCollapseActive
//= require ./GroupSelectionGrid
//= require ./JurisdictionsTree
//= require ./RoleSelectionGrid
//= require ./UserSelectionGrid
//= require_self

Ext.ns("Ext.ux");

/**
 * An Ext.Panel that represents the Audience selection form. Will be able to output users, juridictions, and roles from its 3 "public" stores
 * Note: To put in a FormPanel, needs to be contained inside a panel in the form. Not sure why, but I blame the form panel
 */
Ext.ux.AudiencePanel = Ext.extend(Ext.Container, {
    /**
     *    @lends Ext.ux.AudiencePanel.prototype
     */
    showJurisdictions: true,
    showRoles: true,
    showUsers: true,
    
    initComponent: function() {

        this.createTransferableRecord();

        var items = [];

        if( this.showJurisdictions === true) items.push(this._createJurisdictionsTree());
        if( this.showRoles === true) items.push(this._createRoleSelectionGrid());
        if (this.showGroups === true) items.push(this._createGroupSelectionGrid());
        if (this.showUsers === true) items.push(this._createUserSelectionGrid());

        this.accordion = new Ext.Panel({
            fieldLabel: 'Group Membership',
            layout: 'accordion',
            flex: 3,
            layoutConfig: { hideCollapseTool: true, animate: Application.rails_environment !== 'cucumber' ? true : false },
            items: items,
            margins: '0 20 0 0',
            activeItem: 0,
            plugins: ['donotcollapseactive']
        });

        Ext.apply(this, { // override properties that were set in the config that we don't want
            layout: 'hbox',
            layoutConfig: { align: 'stretch' },
            items: [this.accordion, this.createSelectionBreakdownPanel()]
        });

        Ext.ux.AudiencePanel.superclass.initComponent.call(this);
    },

    disable: function(){
        this.accordion.disable();
        this.selectedItemsGridPanel.disable();
        if(!this.selectedItemsGridPanel.getEl())
            this.selectedItemsGridPanel.on('afterrender', function(){this.selectedItemsGridPanel.removeClass('x-masked-relative');}, this, {delay: 10, single: true});
        else
            this.selectedItemsGridPanel.removeClass('x-masked-relative');
    },

    enable: function(){
        this.accordion.enable();
        this.selectedItemsGridPanel.enable();
    },

    destroy: function(){
    },

    findRecordIndexInSelectedItems: function(record, type){
        return this.selectedItemsStore.findBy(function(record, type, check, id){
            return record.get('id') === check.get('id') && check.get('type') === type;
        }.createDelegate(this, [record, type], 0));
    },

    createTransferableRecord: function(){
        this._transferableRecord = Ext.data.Record.create(
            ['name', 'id', 'email', 'title', 'tip', 'type']
        );
        this._transferableRecord.prototype.getData = function(){return this.data};
    },

    _createRoleSelectionGrid: function(){
        return this.roleSelectionGrid = new Talho.ux.RoleSelectionGrid({
            store: this.role_store,
            store_listeners:{
                scope:this,
                'load': function(){
                    // look for all of the roles in the selectedItemsStore
                    var roles = [];
                    this.selectedItemsStore.query('type', 'role').each( function(role){roles.push({id:role.get('id')}); } );
                    this.roleSelectionGrid.load(roles);
                }
            },
            sm_listeners:{
                scope:this,
                'rowselect': function(sm, index, record){
                    if(this.findRecordIndexInSelectedItems(record, 'role') === -1) // If we don't find the record, add it
                        this.selectedItemsStore.addSorted(new this._transferableRecord({name: record.get('name'), id: record.get('id'), type: 'role'}));
                },
                'rowdeselect': function(sm, index, record){
                    var recordIndex = this.findRecordIndexInSelectedItems(record, 'role');
                    if(recordIndex !== -1)
                        this.selectedItemsStore.remove(this.selectedItemsStore.getAt(recordIndex));
                }
            }
        })
    },

    _createGroupSelectionGrid: function(){
        return this.groupSelectionGrid = new Talho.ux.GroupSelectionGrid({
            store: this.group_store,
            store_listeners:{
                scope:this,
                'load': function(){
                    // look for all of the groups in the selectedItemsStore
                    var groups = [];
                    this.selectedItemsStore.query('type', 'group').each( function(group){groups.push({id:group.get('id')}); } );
                    this.groupSelectionGrid.load(groups);
                }
            },
            sm_listeners:{
                scope:this,
                'rowselect': function(sm, index, record){
                    if(this.findRecordIndexInSelectedItems(record, 'group') === -1) // If we don't find the record, add it
                        this.selectedItemsStore.addSorted(new this._transferableRecord({name: record.get('name'), id: record.get('id'), type: 'group'}));
                },
                'rowdeselect': function(sm, index, record){
                    var recordIndex = this.findRecordIndexInSelectedItems(record, 'group');
                    if(recordIndex !== -1)
                        this.selectedItemsStore.remove(this.selectedItemsStore.getAt(recordIndex));
                }
            }
        })
    },

    _createJurisdictionsTree: function(){
        return this.jurisdictionTree = new Talho.ux.JurisdictionsTree({
            store: this.jurisdiction_store,
            store_listeners:{
                scope: this,
                'load': function(){
                     // look for all of the jurisdictions in the selectedItemsStore
                    var jurisdictions = [];
                    this.selectedItemsStore.query('type', 'jurisdiction').each( function(jurisdiction){jurisdictions.push({id:jurisdiction.get('id')}); } );
                    this.jurisdictionTree.load(jurisdictions);
                }
            },
            sm_listeners:{
                scope:this,
                'rowselect': function(sm, index, record){
                    this.selectedItemsStore.clearFilter();
                    if(this.findRecordIndexInSelectedItems(record, 'jurisdiction') === -1) // If we don't find the record, add it
                    {
                        record.isFullySelected = this.jurisdictionTree.nodeHasAllChildrenSelected(record);
                        var ancestors = this.jurisdictionTree.getStore().getNodeAncestors(record);
                        Ext.each(ancestors, function(ancestor){ ancestor.isFullySelected = this.jurisdictionTree.nodeHasAllChildrenSelected(ancestor); }, this);

                        this.selectedItemsStore.addSorted(new this._transferableRecord({name: record.get('name'), id: record.get('id'), type: 'jurisdiction'}));
                    }
                    this.selectedItemsStore.applyFilter();
                },
                'rowdeselect': function(sm, index, record){
                    record.isFullySelected = false;

                    var ancestors = this.jurisdictionTree.getStore().getNodeAncestors(record);
                    Ext.each(ancestors, function(ancestor){ ancestor.isFullySelected = false }, this);

                    this.selectedItemsStore.clearFilter();
                    var recordIndex = this.findRecordIndexInSelectedItems(record, 'jurisdiction');
                    if(recordIndex !== -1)
                        this.selectedItemsStore.removeAt(recordIndex);
                    this.selectedItemsStore.applyFilter();
                },
                'massselectionchange': function(sm, addedRecords, removedRecords){
                    this.selectedItemsStore.clearFilter();
                    Ext.each(removedRecords, function(record){this.selectedItemsStore.removeAt(this.findRecordIndexInSelectedItems(record, 'jurisdiction'))}, this);

                    // clean up the added records to make sure we don't add any duplicates
                    var dontAddThese = [];
                    Ext.each(addedRecords, function(record){if(this.findRecordIndexInSelectedItems(record, 'jurisdiction') !== -1) dontAddThese.push(record);}, this);
                    Ext.each(dontAddThese, function(record){addedRecords.remove(record);});

                    var transferableItems = [];
                    Ext.each(addedRecords, function(selection){transferableItems.push(new this._transferableRecord({name: selection.get('name'), id: selection.get('id'), type: 'jurisdiction'}));}, this);

                    if(transferableItems.length > 0)
                    {
                        this.selectedItemsStore.add(transferableItems);
                        this.selectedItemsStore.sort();
                    }

                    this.selectedItemsStore.applyFilter();
                }
            }
        });
    },

    _createUserSelectionGrid: function() {
        var userAddRecords = function(store, records){
            if(records.length === 1)
                this.selectedItemsStore.addSorted(records[0]);
            else
            {
                this.selectedItemsStore.add(records);
                this.selectedItemsStore.applySort();
            }
        };

        return this.userSelectionGrid = new Talho.ux.UserSelectionGrid({
            record: this._transferableRecord,
            store_listeners:{
                scope: this,
                'add': userAddRecords,
                'load': userAddRecords,
                'remove': function(store, record){
                    this.selectedItemsStore.remove(record);
                }
            }
        });
    },

    createSelectionBreakdownPanel: function(){
        this.selectedItemsStore = new Ext.data.GroupingStore({
            reader: new Ext.data.JsonReader({
                fields: this._transferableRecord,
                idProperty: 'this_is_a_really_long_id_that_we_wont_use'
            }),
            groupField: 'type',
            listeners: {
                scope:this
            },
            filterFn: function(record){
                var type = record.get('type');
                switch(type)
                {
                    case 'user':
                    case 'role':
                    case 'group':
                        return true; // we don't want to filter users, groups, or roles
                    case 'jurisdiction':
                        var store = this.jurisdictionTree.getStore();
                        var rc = store.getAt(store.findExact('id', record.get('id')));
                        if(rc){
                            var parent = store.getNodeParent(rc);
                            return parent === null || !parent.isFullySelected;
                        }
                        else return true;
                    default: return true; // if we can't figure out what to do with it, we don't want to filter it, in case
                }
            },
            sortInfo: {
                field: 'type',
                direction: 'ASC'
            }
        });

        this.selectedItemsStore.applyFilter = function(store){
            store.filterBy(store.filterFn, this);
        }.createDelegate(this, [this.selectedItemsStore]);
        
        this.selectedItemsStore.on('add', this.selectedItemsStore.applyFilter, this, {delay: 50});
        this.selectedItemsStore.on('remove', this.selectedItemsStore.applyFilter, this, {delay: 50});

        this.selectedItemsGridPanel = new Ext.grid.GridPanel({
            sm: false,
            title: "Recipient Preview",
            bodyCssClass: 'selectedItems',
            flex: 2,
            store: this.selectedItemsStore,
            hideHeaders: true,
            colModel: new Ext.grid.ColumnModel({
                columns: [
                    {header: "Name", dataIndex: 'name', sortable:true, id: 'name_column',
                    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
                        if(record.get('type') === 'jurisdiction')
                        {
                            var jts = this.jurisdictionTree.getStore();
                            var node = jts.getAt(jts.findExact('id', record.get('id')));
                        
                            if(node && node.isFullySelected)
                            {
                                metaData.attr = 'style="font-weight:bold;"';
                                value = value + ' (ALL)';
                            }
                        }
                        return value;
                    }.createDelegate(this)},
                    {header: "Type", dataIndex: 'type', renderer: Ext.util.Format.capitalize, groupable: true, hidden: true},
                    {xtype: 'xactioncolumn', icon: '/assets/images/cross-circle.png', iconCls: 'removeBtn', scope: this, handler: function(grid, row){
                        var record = grid.getStore().getAt(row);
                        var type = record.get('type'),
                            id = record.get('id');

                        if(type === 'user'){
                            this.userSelectionGrid.getStore().remove(record);
                            return;
                        }

                        var cgrid;
                        if(type === 'jurisdiction'){
                            cgrid = this.jurisdictionTree;
                            this.jurisdictionTree.clearFilter();
                        }
                        else if(type === 'role'){
                          cgrid = this.roleSelectionGrid;
                        }
                        else if(type === 'group' || type === 'organization'){
                          cgrid = this.groupSelectionGrid;
                        }
                        
                        var sm;
                        if(cgrid && cgrid.rendered && (sm = cgrid.getSelectionModel()) && sm.grid) {
                            sm.deselectRow(cgrid.getStore().findExact('id', id));
                        }
                        else
                           grid.getStore().remove(record);
                    }}
                ],
                defaults:{
                    menuDisabled: true
                }
            }),
            autoExpandColumn: 'name_column',
            view: new Ext.grid.GroupingView({
                groupTextTpl: '{group}s'
            })
        });

        return this.selectedItemsGridPanel;
    },

    getSelectedIds: function(){
        return {role_ids: Ext.invoke(this.selectedItemsStore.query('type', 'role').getRange(), 'get', 'id'),
                jurisdiction_ids: Ext.invoke(this.selectedItemsStore.query('type', 'jurisdiction').getRange(), 'get', 'id'),
                group_ids: Ext.invoke(this.selectedItemsStore.query('type', 'group').getRange(), 'get', 'id'),
                user_ids: Ext.invoke(this.selectedItemsStore.query('type', 'user').getRange(), 'get', 'id')
        };
    },

    getSelectedItems: function(){
        return {roles: Ext.invoke(this.selectedItemsStore.query('type', 'role').getRange(), 'getData'),
                jurisdictions: Ext.invoke(this.selectedItemsStore.query('type', 'jurisdiction').getRange(), 'getData'),
                groups: Ext.invoke(this.selectedItemsStore.query('type', 'group').getRange(), 'getData'),
                users: Ext.invoke(this.selectedItemsStore.query('type', 'user').getRange(), 'getData')
        };
    },

    clear: function(){
        // we need to clear each selection and then clear the selected store. Going to not listen to events as we add in order to speed things up just in case a lot of things were selected

        if(this.jurisdictionTree)
        {
            this.jurisdictionTree.clear();
        }

        this.roleSelectionGrid.clear();

        if(this.groupSelectionGrid)
        {
            this.groupSelectionGrid.clear();
        }

        if(this.userSelectionGrid)
            this.userSelectionGrid.clear();

        this.selectedItemsStore.clearFilter();
        this.selectedItemsStore.removeAll();

        if(this.accordion.rendered)
        {
            this.accordion.getLayout().setActiveItem(0);
        }
    },

    load: function(jurisdictions, roles, users, groups){
        groups = groups || [];
        // first prep each value
        Ext.each(jurisdictions, function(j){j.type = 'jurisdiction';});
        Ext.each(roles, function(r){r.type = 'role'});
        Ext.each(groups, function(r){r.type = 'group'});
        Ext.each(users, function(u){u.type = 'user'; if(!u.name){u.name = u.display_name;} });

        // clear the currently selected items: we're overwriting them
        this.selectedItemsStore.removeAll();

        // Now we need to see if jurisdictions and roles have been loaded: if they have, then we can set selected items now,
        // otherwise the load event handler for the roles and jurisdiction stores should check to see if there are any already
        // selected items
        
        if(this.roleSelectionGrid && this.roleSelectionGrid.getStore().getCount() > 0)
            this.roleSelectionGrid.load(roles);
        else
            this.selectedItemsStore.loadData(roles, true); // append: true

        if(this.groupSelectionGrid && this.groupSelectionGrid.getStore().getCount() > 0)
            this.groupSelectionGrid.load(groups);
        else
            this.selectedItemsStore.loadData(groups, true); // append: true

        if(this.jurisdictionTree && this.jurisdictionTree.getStore().getCount() > 0)
            this.jurisdictionTree.load(jurisdictions);
        else
           this.selectedItemsStore.loadData(jurisdictions, true); // append true

        // finally, let's handle user load. We're going to do this by loading directly into the user store and letting its events take care of things for us
        if(this.userSelectionGrid)
            this.userSelectionGrid.getStore().loadData(users);

        this.selectedItemsStore.applySort();
    }
});
                                                       
Ext.reg('audiencepanel', Ext.ux.AudiencePanel);