      1 /**
      2  * @class Ozone.layout.ManageWidgetsContainer
      3  * @extends Ext.form.Panel
      4  */
      5 Ext.define('Ozone.layout.ManageWidgetsContainer', {
      6     extend: 'Ext.form.Panel', /** @lends Ozone.layout.ManageWidgetsContainer */
      7     alias: ['widget.owfManageWidgetsContainer', 'widget.Ozone.layout.ManageWidgetsContainer'],
      8     requires: [
      9         'Ext.layout.container.VBox'
     10     ],
     11
     12     layout: {
     13       type: 'vbox',
     14       align: 'stretch'
     15     },
     16
     17     border: false,
     18
     19     cls: 'mwc-manageWidgetsContainer',
     20
     21     mixins: {
     22         focus: 'Ozone.components.focusable.CircularFocus'
     23     },
     24
     25     loadGridData: function(){
     26     this.setLoading(true);
     27     var existingWidgetStore = this.dashboardContainer.widgetStore;
     28
     29         // Add removed flag for delete column
     30         var storeData = [];
     31         for (var i = 0; i < existingWidgetStore.getCount(); i++) {
     32             var widget = existingWidgetStore.getAt(i).data;
     33             widget["removed"] = false;
     34             if (widget.definitionVisible && widget.widgetTypes != null) {
     35               //loop through widget types and find 'standard'
     36               var widgetTypes = widget.widgetTypes;
     37               var isStandard = false;
     38               for (var j = 0 ; j < widgetTypes.length ; j++) {
     39                 var widgetType = widgetTypes[j];
     40                 if (widgetType != null && widgetType.name == 'standard') {
     41                   isStandard = true;
     42                   break;
     43                 }
     44               }
     45
     46               //only show standard and visible widgetdefs
     47               if (isStandard) {
     48                 storeData.push(widget);
     49               }
     50             }
     51         }
     52
     53         this.store.loadData(storeData);
     54         this.setLoading(false);
     55     },
     56
     57     refreshOriginalWidgetNames: function(params) {
     58         this.setLoading(true);
     59         var scope = this;
     60         Ozone.pref.PrefServer.findWidgets({
     61           onSuccess: function(records) {
     62                 // Update originalWidgetNames
     63                 for (var i = 0; i < records.length; i++) {
     64                     scope.dashboardContainer.originalWidgetNames[records[i].path] = records[i].value.namespace;
     65                 }
     66
     67                 if (params.reloadGrid == true) {
     68                     scope.loadGridData();
     69                 } else {
     70                     scope.setLoading(false);
     71                 }
     72                 params.callback();
     73             },
     74
     75             onFailure: function() {
     76                 scope.setLoading(false);
     77                 params.callback();
     78             }
     79         });
     80     },
     81     initComponent: function() {
     82         var scope = this;
     83
     84           // Set up a model to use in our Store
     85         var widgetJsonModel = Ext.define('widgetJsonModel', {
     86             extend: 'Ext.data.Model',
     87             fields: ['widgetGuid', {name:'name', sortType:Ext.data.SortTypes.asUCString}, 'visible', 'removed', 'headerIcon','tags', 'editable', '_tags','_editable']
     88         });
     89
     90           // Create data store
     91         this.store = Ext.create('Ext.data.JsonStore', {
     92             model:'widgetJsonModel',
     93             sorters: [{
     94                 property : 'name',
     95                 direction: 'ASC' // or 'DESC' (case sensitive for local sorting)
     96             }],
     97             listeners : {
     98                 datachanged : {
     99                     fn : function(s) {
    100                         //preprocess store
    101                         var store = s;
    102                         store.suspendEvents();
    103                         for (var i = 0 ; i < store.getCount() ; i++) {
    104                             var rec = store.getAt(i);
    105                             var recData = rec.data;
    106                             if (Ext.isArray(recData.tags) && recData.tags.length > 0) {
    107                                 var _tags = '';
    108                                 var editable = true;
    109                                 for (var j = 0 ; j < recData.tags.length ; j++) {
    110                                     _tags +=recData.tags[j].name;
    111                                     if (j < recData.tags.length -1) {
    112                                         _tags+=', ';
    113                                     }
    114                                     if (recData.tags[j].editable === false) {
    115                                         editable = false;
    116                                     }
    117                                 }
    118                                 rec.set('_tags',_tags);
    119                                 rec.set('_editable',editable);
    120                                 rec.commit();
    121                             }
    122                             else {
    123                                 rec.set('_tags','');
    124                                 rec.set('_editable',true);
    125                                 rec.commit();
    126                             }
    127                         }
    128                         store.resumeEvents();
    129                     },
    130                     scope : this
    131                 }
    132             }
    133         });
    134
    135         // Create editable Dashboard Name field
    136         this.nameField = Ext.create('Ext.form.field.Text', {
    137             maxLength: 200,
    138             allowBlank: false,
    139             blankText: Ozone.layout.DialogMessages.view_dashboardNameField_blankText,
    140             validator: function(value) {
    141                 if (this.value) {
    142                     if (this.value.toUpperCase() != value.toUpperCase()) {
    143                         for (var i = 0; i < store.data.items.length; i++) {
    144                             if(value.charAt(0) === ' ' || value.charAt(value.length-1) === ' ') {
    145                                 return Ozone.util.ErrorMessageString.invalidDashboardNameMsg;
    146                             }
    147                         }
    148                     }
    149                     else {
    150                         if(this.value.charAt(0) === ' ' || this.value.charAt(this.value.length-1) === ' ') {
    151                             return Ozone.util.ErrorMessageString.invalidDashboardNameMsg;
    152                         }
    153                     }
    154                     return true;
    155                 }
    156                 return true;
    157             }
    158         });
    159
    160         this.tagsField = Ext.create('Ext.form.field.Text', {
    161             allowBlank: true,
    162             validator: function(val) {
    163                 if (val.match(Ozone.config.carousel.restrictedTagGroupsRegex) != null) {
    164                 return '"'+val+'"'+Ozone.util.ErrorMessageString.restrictedTagError;
    165                 }
    166                 else {
    167                 return true;
    168                 }
    169             }
    170         });
    171
    172         var resetTitleAction = Ext.create('Ext.Action', {
    173             text: 'Reset Title',
    174             handler: function(widget, event) {
    175                 var rec = scope.widgetGrid.getSelectionModel().getSelection()[0];
    176                 if (rec) {
    177                     scope.refreshOriginalWidgetNames({
    178                         reloadGrid: false,
    179                         callback: function() {
    180                             var originalName = rec.get('originalName');
    181                             rec.set('name', originalName);
    182                         }
    183                     });
    184                 }
    185             }
    186         });
    187         var resetAllTitlesAction = Ext.create('Ext.Action', {
    188             text: 'Reset All Titles',
    189             handler: function(widget, event) {
    190                 var records = scope.widgetGrid.getStore().getRange();
    191                 scope.refreshOriginalWidgetNames({
    192                     reloadGrid: false,
    193                     callback: function() {
    194                         for (var i = 0; i < records.length; i++) {
    195                             if (records[i].data.editable) {
    196                                 // Reset titles
    197                                 var originalName = records[i].get('originalName');
    198                                 records[i].data.name = originalName;
    199
    200                                 // Update tags
    201                                 var tags = [];
    202                                 if (records[i].data._tags.length > 0 && records[i].data._tags != '') {
    203                                     var splits = records[i].data._tags.split(',');
    204                                     for (var j = 0 ; j < splits.length ; j++) {
    205                                         var name = Ext.String.trim(splits[j]);
    206                                         if (name != '') {
    207                                             tags.push({
    208                                                 name: name,
    209                                                 visible: true,
    210
    211                                                 //todo use position to order groups for now just set to -1
    212                                                 position: -1,
    213                                                 editable: records[i].data._editable
    214                                             });
    215                                         }
    216                                     }
    217                                 }
    218                                 records[i].data.tags = tags;
    219                             }
    220                         }
    221                         scope.store.loadRecords(records, {
    222                             addRecords: false
    223                         });
    224                     }
    225                 });
    226             }
    227         });
    228
    229         var contextMenu = Ext.create('Ext.menu.Menu', {
    230             items: [
    231                 resetTitleAction,
    232                 resetAllTitlesAction
    233             ]
    234         });
    235
    236         this.widgetGrid = Ext.create('Ext.grid.GridPanel', {
    237             ddGroup: 'ddWidgets',
    238             cls:'mwc-widgetGridPanel',
    239             store: this.store,
    240             autoScroll: true,
    241             scroll: true,
    242             viewConfig: {
    243                 listeners: {
    244                     itemcontextmenu: {
    245                         fn: function (view, rec, node, index, e) {
    246                             e.stopEvent();
    247                             this.dashboardContainer.modalWindowManager.register(contextMenu);
    248                             contextMenu.showAt(e.getXY());
    249                             this.dashboardContainer.modalWindowManager.bringToFront(contextMenu);
    250                             return false;
    251                         },
    252                         scope: this
    253                     },
    254                     itemkeydown: {
    255                         fn: function (view, record, item, index, evt) {
    256                             switch (evt.getKey()) {
    257                                 case Ext.EventObject.S:
    258                                     record.set('visible', !record.get('visible'));
    259                                     view.refreshNode(index);
    260
    261                                     //Fire the column's appropriate checked/unchecked method
    262                                     var col = this.down('#showCheckColumn');
    263                                     record.get('visible') ? col.onChecked(view, record) : col.onUnchecked(view, record);
    264                                     break;
    265
    266                                 case Ext.EventObject.D:
    267                                     record.set('removed', !record.get('removed'));
    268                                     view.refreshNode(index);
    269
    270                                     //Fire the column's appropriate checked/unchecked method
    271                                     var col = this.down('#deleteCheckColumn');
    272                                     record.get('removed') ? col.onChecked(view, record) : col.onUnchecked(view, record);
    273                                     break;
    274
    275                                 case Ext.EventObject.R:
    276                                     if(!Ext.EventObject.hasModifier()) {
    277                                         //Get the 3rd cell (in the Tags column) of the selected row to get starting point
    278                                         //for XY coordinates to place the context menu inside the selected row appropriately.
    279                                         var thirdCellInSelectedRow = Ext.select('tr[class*=x-grid-row-focused]:first td').item(2);
    280                                         var coordinates = thirdCellInSelectedRow.getXY();
    281                                         coordinates[0] -= 5;
    282                                         coordinates[1] += 10; // Approximates a good location for the context menu to appear
    283
    284                                         this.dashboardContainer.modalWindowManager.register(contextMenu);
    285                                         contextMenu.showAt(coordinates);
    286                                         this.dashboardContainer.modalWindowManager.bringToFront(contextMenu);
    287
    288                                         //Add a listener to the context menu so that when it is closed with ESC it will
    289                                         //move focus back to the widget grid
    290                                         contextMenu.addListener('hide', function(view, record, item) {
    291                                             //Set on a defer to wait until focus is moved to the banner, then pulls focus back
    292                                             Ext.defer(function() {
    293                                                 //Set focus on the widget grid, which is 3 levels down from the manageWidgetsWindow
    294                                                 Ext.getCmp("manageWidgetsWindowId").getComponent(0).getComponent(0).getComponent(0).focus();
    295                                             }, 100, this);
    296
    297                                             //Remove this listener after the context menu is hidden
    298                                             contextMenu.removeListener('hide', arguments.callee);
    299                                         });
    300                                     }
    301                                     break;
    302
    303                                 case Ext.EventObject.ENTER:
    304                                     var header = view.headerCt.getHeaderAtIndex(1);
    305                                     view.editingPlugin.startEdit(record, header);
    306                             }
    307                         },
    308                         scope: this
    309                     },
    310                     afterrender: function (cmp) {
    311                         cmp.getEl().on('keyup', function(evt) {
    312                             //prevent ESC key from escaping to the
    313                             //body.  Since ESC keydown is stopped
    314                             //internally to the grid, we need to to
    315                             //stop the keyup as well
    316                             if (evt.getKey() === evt.ESC)
    317                                 evt.stopPropagation();
    318                         });
    319                     }
    320                 }
    321             },
    322             columns: [
    323                 {
    324                     xtype: 'gridcolumn',
    325                     text: '<span class="gridHdr">Icon</span>',
    326                     dataIndex: 'headerIcon',
    327                     width: 36,
    328                     sortable: false,
    329                     resizable: false,
    330                     menuDisabled: true,
    331                     renderer: function (val, metaData, record, rowIndex, colIndex, store) {
    332                         if (record.get('editable') == false) {
    333                             metaData.attr = 'ext:qtip="This widget belongs to a Group and may not be edited or deleted"';
    334                             metaData.tdCls += ' x-item-disabled';
    335                         }
    336
    337                         var retVal = '<div><img width=\"24px\" height=\"24px\" src="'+val+'">';
    338                         retVal += '</div>';
    339
    340                         return  retVal;
    341                     }
    342                 },
    343                 {
    344                     xtype: 'textcolumn',
    345                     text: '<span class="gridHdr">Widget</span>',
    346                     dataIndex: 'name',
    347                     field: this.nameField,
    348                     width: 176,
    349                     resizable: false,
    350                     menuDisabled: true,
    351                     renderer: function(value, metaData, record, rowIndex, colIndex, store) {
    352                         if(record.get('editable') != false && record.get('_editable')!= false) {
    353                             metaData.tdCls += ' manage-editable';
    354                         }
    355                         if (record.get('editable') == false) {
    356                             metaData.tdAttr = 'data-qtip="This widget belongs to a Group and may not be edited or deleted"';
    357                             metaData.tdCls += ' x-item-disabled';
    358                         }
    359                         return Ext.util.Format.htmlEncode(value);
    360                     }
    361                 },
    362                 {
    363                     xtype:'textcolumn',
    364                     menuDisabled: true,
    365                     text: '<span class="gridHdr">Tags</span>',
    366                     field : this.tagsField,
    367                     dataIndex: '_tags',
    368                     width: 170,
    369                     sortable: false,
    370                     resizable: false,
    371                     renderer: function(value, metaData, record, rowIndex, colIndex, store) {
    372                         metaData.tdCls = 'hd-tags';
    373                         if (record.get('_editable') == false) {
    374                             metaData.tdCls += ' x-item-disabled';
    375                         }
    376                         if (record.get('editable') == false) {
    377                             metaData.tdAttr = 'data-qtip="This widget belongs to a Group and may not be edited or deleted"';
    378                             metaData.tdCls += ' x-item-disabled';
    379                         }
    380                         if(record.get('editable') != false && record.get('_editable')!= false)
    381                         {
    382                             metaData.tdCls += ' manage-editable';
    383                             var encValue = Ext.util.Format.htmlEncode(value);
    384                             //work-around to html-encode html tags - can't just directly htmlEncode stuff
    385                             //so we inject an XTemplate to handle it
    386                             metaData.tdAttr = 'data-qtip="'+new Ext.XTemplate('{foo:htmlEncode}').apply({
    387                                 foo:encValue
    388                             })+'"';
    389                         }
    390                         return Ext.util.Format.htmlEncode(value);
    391                     }
    392                 },
    393                 {
    394                     xtype:'checkcolumn',
    395                     itemId: 'showCheckColumn',
    396                     text: '<span class="gridHdrShow"><span class="x-grid-column-checkbox-off"></span><span class="underline">S</span>how</span>',
    397                     dataIndex: 'visible',
    398                     width: 90,
    399                     sortable: false,
    400                     tdCls:'gridCellShowWidgets-chkbox',
    401                     listeners: {
    402                         afterrender: {
    403                             scope: this,
    404                             single: true,
    405                             fn: function(cmp) {
    406                                 this.store.mon(this.store, 'datachanged', function() {
    407                                     if(this.find('visible', false) === -1) {
    408                                         //All rows are checked on open, so check the column's overall checkbox
    409                                         cmp.toggleHeader(true);
    410                                     }
    411                                 });
    412                             }
    413                         }
    414                     }
    415                 },
    416                 {
    417                     xtype:'checkcolumn',
    418                     itemId: 'deleteCheckColumn',
    419                     text: '<span class="gridHdrDelete"><span class="x-grid-column-checkbox-off"></span><span class="underline">D</span>elete</span>',
    420                     dataIndex: 'removed',
    421                     width: 90,
    422                     sortable: false,
    423                     tdCls:'gridCellDeleteWidgets-chkbox'
    424                 }
    425             ],
    426             selType:'rowmodel',
    427             frame: false,
    428             enableDragDrop: true,
    429             enableHdMenu: false,
    430             enableColumnResize: false,
    431             plugins: [
    432                 Ext.create('Ext.grid.plugin.CellEditing', {
    433                     clicksToEdit: 1
    434                 }),
    435                 new Ozone.components.focusable.FocusableGridPanel()
    436             ],
    437             flex: 1,
    438             listeners: {
    439                 //Handle Before Edit has taken place
    440                 beforeedit: function(e) {
    441                     var isCellEditable = function(record) {
    442                         if (record.get('removed') || record.get('_editable') === false ||  record.get('editable') === false) {
    443                             return false;
    444                         }
    445                         return true;
    446                     };
    447                     return isCellEditable(e.record);
    448                 },
    449                 //Handle After Edit has taken place
    450                 edit: function(editor, e) {
    451                     if(editor.editors.items[e.column.id]) {
    452                         e.record.set('_tags',editor.editors.items[e.column.id].field.value.toLowerCase());
    453                     }
    454                 }
    455             }
    456         });
    457
    458         this.items = [
    459             this.widgetGrid
    460         ];
    461
    462         /***
    463          * IMPLEMENT HEADER CLICKED.
    464          */
    465         this.widgetGrid.headerCt.on('headerclick', function(ct, column, evt, htmlElem) {
    466                 if(typeof column.onGridHeaderCtClick == 'function') {
    467                     column.onGridHeaderCtClick(this.widgetGrid, evt, htmlElem);
    468                 }
    469             },
    470             this
    471         );
    472
    473         this.keys = [];
    474         this.keys.push({
    475             key: Ext.EventObject.ENTER,
    476             fn: function (k,e) {
    477                 var button = this.getFooterToolbar().getComponent('saveButton');
    478                 if (button) {
    479                     e.button = 0;
    480                     button.onClick(e);
    481                 }
    482             },
    483             scope: this
    484         });
    485
    486         this.dockedItems = [{
    487             xtype: 'toolbar',
    488             dock: 'bottom',
    489             defaults: {
    490                 minWidth: this.minButtonWidth
    491             },
    492             layout: {
    493                 type: 'hbox',
    494                 pack: 'end'
    495             },
    496             items: [
    497                 {
    498                     xtype:'button',
    499                     scale: 'small',
    500                     text: Ozone.layout.MessageBoxButtonText.ok,
    501                     itemId: 'saveButton',
    502                     //                iconCls: 'okSaveBtnIcon',
    503                     handler: function() {
    504                         scope.onSaveClick();
    505                     },
    506                     listeners: {
    507                         // afterrender: function(button) {
    508                         //     button.getEl().on('keydown', function(e) {
    509                         //         e.stopPropagation();
    510                         //     });
    511                         // }
    512                     }
    513                 },
    514                 {
    515                     xtype:'button',
    516                     scale: 'small',
    517                     itemId: 'cancelBtn',
    518                     text: Ozone.layout.MessageBoxButtonText.cancel,
    519                     // iconCls: 'cancelBtnIcon',
    520                     handler: function() {
    521                         Ext.getCmp(scope.winId).close();
    522                     }
    523                 }
    524             ]
    525         }];
    526
    527         //Need this to get rid of destory errors with ExtJS
    528         this.on('beforedestroy', function(cmp) {
    529             cmp.dockedItems = null;
    530         });
    531
    532         this.on({
    533             afterrender: {
    534                 fn: function(cmp) {
    535                     var winCmp = Ext.getCmp(cmp.winId);
    536
    537                     winCmp.on('show', function() {
    538                         cmp.loadGridData();
    539                     });
    540
    541                     winCmp.setupFocus(cmp.widgetGrid.getView().getEl(),
    542                     cmp.down('#cancelBtn').getFocusEl());
    543
    544                 },
    545                 scope: this
    546             }
    547         });
    548
    549         //Ensure the shadow follows the window in IE
    550         this.on('resize', function() {
    551             this.syncShadow();
    552         });
    553
    554         this.callParent();
    555     },
    556
    557     onSaveClick: function() {
    558         // Create array of widgets
    559         var gridData = this.widgetGrid.store.data.items;
    560         var widgetsToUpdate = [];
    561         var widgetsToDelete = [];
    562         var widgetGuidsToDelete = [];
    563         //this.dashboardContainer.widgetNames = {};
    564         for (var i = 0; i < gridData.length; i++) {
    565
    566             if (gridData[i].data.editable) {
    567                 if (!gridData[i].data.removed) {
    568                     var recData = gridData[i].data;
    569                     var tags = [];
    570                     if (recData._tags.length > 0 && recData._tags != '') {
    571                         var splits = recData._tags.split(',');
    572                         for (var j = 0 ; j < splits.length ; j++) {
    573                             var name = Ext.String.trim(splits[j]);
    574                             if (name != '') {
    575                                 tags.push({
    576                                     name: name,
    577                                     visible: true,
    578
    579                                     //todo use position to order groups for now just set to -1
    580                                     position: -1,
    581                                     editable: recData._editable
    582                                 });
    583                             }
    584                         }
    585                     }
    586
    587                     var widget = {
    588                         guid: gridData[i].data.widgetGuid,
    589                         visible: gridData[i].data.visible,
    590                         tags: tags
    591                     };
    592                     var originalName = gridData[i].data.originalName;
    593                     if (gridData[i].data.name !== originalName) {
    594                         widget.name = gridData[i].data.name;
    595                     }
    596                     widgetsToUpdate.push(widget);
    597
    598                 } else {
    599                     widgetGuidsToDelete.push(gridData[i].data.widgetGuid);
    600                     widgetsToDelete.push({
    601                         guid: gridData[i].data.widgetGuid,
    602                         name: gridData[i].data.name,
    603                         headerIcon: gridData[i].data.headerIcon,
    604                         image: gridData[i].data.image
    605                     });
    606                 }
    607             }
    608         }
    609
    610         var scope = this;
    611         function onSuccess() {
    612             //Call method to refresh the 'widget launch menu' widgets.
    613             scope.dashboardContainer.retrieveUpdatedWidgets();
    614         }
    615
    616         function onFailure() {
    617             Ozone.Msg.alert(Ozone.util.ErrorMessageString.saveUpdatedWidgets, Ozone.util.ErrorMessageString.saveUpdatedWidgetsMsg,
    618                 null, null, null, scope.dashboardContainer.modalWindowManager);
    619         }
    620
    621         Ext.getCmp(this.winId).close();
    622         Ozone.pref.PrefServer.updateAndDeleteWidgets({
    623             widgetsToUpdate:widgetsToUpdate,
    624             widgetGuidsToDelete:[],
    625             updateOrder:false,
    626             onSuccess:onSuccess,
    627             onFailure:onFailure
    628         });
    629
    630         if (widgetGuidsToDelete.length > 0) {
    631
    632             Ext.create('Ext.window.Window', {
    633                 title: 'Deleting Widgets',
    634                 cls: 'delete-widgets-window',
    635                 height: 550,
    636                 ownerCt: scope.dashboardContainer,
    637                 dashboardContainer: scope.dashboardContainer,
    638                 constrain: Ext.isIE,
    639                 constrainHeader: true,
    640                 width: 600,
    641                 layout: 'fit',
    642                 resizable: false,
    643                 modal: true,
    644                 items: {
    645                     xtype: 'deletewidgetspanel',
    646                     delWidgets: widgetsToDelete,
    647                     dashboardContainer: scope.dashboardContainer
    648                 },
    649                 listeners: {
    650                     show: function (cmp) {
    651                         //Ensure its on top
    652                         cmp.dashboardContainer.modalWindowManager.register(cmp);
    653                         cmp.dashboardContainer.modalWindowManager.bringToFront(cmp);
    654
    655                         /*
    656                          * Needs to be deferred or it will happen before the
    657                          * window close in IE
    658                          */
    659                         Ext.defer(function() {
    660                             this.focus();
    661                         }, 100, cmp.getComponent('topdeletepanel').okBtn);
    662                     }
    663                 }
    664             }).show();
    665
    666         }
    667     }
    668 });