Ext.namespace('Zarafa.common.categories.dialogs');
/**
* @class Zarafa.common.categories.dialogs.CategoriesPanel
* @extends Ext.Panel
* @xtype zarafa.categoriespanel
*
* Panel for users to edit the categories on a given {@link Zarafa.core.data.IPMRecord record}
*/
Zarafa.common.categories.dialogs.CategoriesPanel = Ext.extend(Ext.Panel, {
/**
* @cfg {Zarafa.core.data.IPMRecord} record The record(s) which are being
* edited through this panel
*/
record : undefined,
/**
* @cfg {String} categorySeparator The string which must be used to separate the
* various categories.
*/
categorySeparator : ';',
/**
* @constructor
* @param config Configuration structure
*/
constructor : function(config)
{
config = config || {};
Ext.applyIf(config, {
// Override from Ext.Component
xtype : 'zarafa.categoriespanel',
layout: 'fit',
border: false,
items: [
this.createCategoriesGrid(config.record)
]
});
Zarafa.common.categories.dialogs.CategoriesPanel.superclass.constructor.call(this, config);
},
/**
* create grid panel for categories which contains {@link Ext.grid.CheckboxSelectionModel CheckboxSelectionModel}
* which can be used by the user to select/change the category from a predefined list.
* @param {Zarafa.core.data.IPMRecord[]} records The records that are edited with this panel
* @return {Object} The configuration object for the grid panel.
* @private
*/
createCategoriesGrid : function(records)
{
var store = new Zarafa.common.categories.data.CategoriesStore();
store.addCategoriesFromMapiRecords(records);
store.sort([
{field: 'quickAccess', direction: 'DESC'},
{field: 'sortIndex', direction: 'ASC'},
{field: 'category', direction: 'ASC'}
]);
var selectionModel = this.createSelectionModel();
var columnModel = this.createColumnModel(selectionModel);
return {
xtype: 'editorgrid',
trackMouseOver: true,
enableHdMenu: false,
deferRowRender:false,
autoExpandColumn: 'category',
ref: 'categoriesGrid',
cls: 'k-no-column-headers k-categories-grid',
viewConfig: {
forceFit: true,
markDirty: false
},
store: store,
sm: selectionModel,
colModel: columnModel,
listeners: {
scope: this,
afterrender: this.onAfterRender,
cellclick: this.onCellClick,
rowclick: this.onRowClick,
validateedit: this.onValidateEdit,
afteredit : this.onAfterEdit
},
onCellDblClick : Ext.emptyFn
};
},
/**
* Returns the selection model that will be used for the categories grid.
*
* @return {Ext.grid.CheckboxSelectionModel} The selection model
*/
createSelectionModel : function()
{
return new Ext.grid.CheckboxSelectionModel({
checkOnly : true,
moveEditorOnEnter: false
});
},
/**
* Returns the column model that will be used for the categories grid.
*
* @param {Ext.grid.CheckboxSelectionModel} selectionModel The selection model of
* the categories grid.
* @return {Ext.grid.ColumnModel} The column model
*/
createColumnModel : function(selectionModel)
{
return new Ext.grid.ColumnModel({
columns: [
selectionModel,
{
id : 'color',
dataIndex : 'color',
sortable : false,
width : 25,
fixed : true,
renderer : this.categoryColorRenderer
},
{
dataIndex : 'category',
editor : new Ext.form.TextField({
allowBlank: false,
enableKeyEvents: true,
listeners : {
scope: this,
keydown: function(field, e){
var key = e.browserEvent.key;
if ( key === ';' || key === ',' ){ // Don't allow ; or , in a category name
e.preventDefault();
}
}
}
}),
sortable : false,
renderer : this.categoryNameRenderer
}
]
});
},
/**
* Function that will be used to render the category color cell in the grid.
*
* @param {String} color The color in hex rgb format
* @param {Object} metaData An object with some data of the cell that is being
* rendered.
* @param {Ext.data.Record} record The record from the
* {@link Zarafa.common.categories.data.CategoriesStore CategoriesStore} for
* which this cell is rendered.
* @return {String} The html that will be used to render the cell.
*/
categoryColorRenderer : function(color, metaData, record)
{
var html = Zarafa.common.categories.Util.getCategoryIconSVG(color);
var cls = Ext.isNumber(record.get('standardIndex')) ? ' k-category-fixed' : '';
html += '<div class="k-colorpicker-ct' + cls + '" style="background-color:'+color+';"></div>';
return html;
},
/**
* Function that will be used to render the category name cell in the grid.
*
* @param {String} value The name of the category
* @param {Object} metaData An object with some data of the cell that is being
* rendered.
* @param {Ext.data.Record} record The record from the
* {@link Zarafa.common.categories.data.CategoriesStore CategoriesStore} for
* which this cell is rendered.
* @return {String} The html that will be used to render the cell.
*/
categoryNameRenderer : function(value, metaData, record)
{
var cls = record.get('quickAccess') ? ' zarafa-pinned' : '';
cls += Ext.isNumber(record.get('standardIndex')) ? ' k-category-fixed' : '';
return Ext.util.Format.htmlEncode(value) +
'<div class="zarafa-grid-button-container' + cls + '">' +
'<div class="zarafa-grid-button k-grid-button-edit"></div>' +
'<div class="zarafa-grid-button k-grid-button-delete"></div>' +
'<div class="zarafa-grid-button k-grid-button-pin"></div>' +
'</div>';
},
/**
* Handler for afterrender event. Will select the categories that are set on the
* records to which this panel applies, will create the color pickers in the grid
* rows, and will add event listeners.
* listeners
* @param {Ext.grid.EditorGridPanel} grid The grid with all the categories
* @private
*/
onAfterRender : function(grid)
{
grid.getSelectionModel().suspendEvents(false);
var records = this.getAvailableCategories();
if(!Ext.isEmpty(records)) {
grid.getSelectionModel().selectRecords(records);
}
grid.getSelectionModel().resumeEvents();
var view = grid.getView();
this.addColorPickers(view);
this.mon(grid.store, 'add', function(){
grid.store.sort([
{field: 'quickAccess', direction: 'DESC'},
{field: 'sortIndex', direction: 'ASC'},
{field: 'category', direction: 'ASC'}
]);
});
this.mon(view, 'refresh', this.addColorPickers, this);
this.mon(view, 'rowupdated', this.onRowUpdated, this);
},
/**
* Adds a {@link Zarafa.common.ui.ColorPicker} to every color cell.
* @param {Ext.grid.GridView} view The view of the categories grid
*/
addColorPickers : function(view)
{
this.categoriesGrid.store.each(function(record, index){
var el = Ext.get(view.getCell(index, 1).querySelector('.k-colorpicker-ct'));
var color = (record.get('color') || '') + '';
var colorPicker = new Zarafa.common.ui.ColorPicker({
renderTo: el.dom,
value: color.replace('#', '')
});
colorPicker.on('show', this.onColorPickerShowMenu.createDelegate(this, [colorPicker], true));
colorPicker.on('hide', this.onColorPickerHideMenu.createDelegate(this, [colorPicker], true));
colorPicker.on('select', this.onSelectColor.createDelegate(this, [record], true));
record.colorPicker = colorPicker;
}, this);
},
/**
* Event handler for the rowupdated event of the grid view
*
* @param {Ext.grid.GridView} view The view component of the grid
* @param {Number} rowIndex The number of the row that was updated
* @param {Ext.data.Record} record The record of the
* {@link Zarafa.common.categories.data.CategoriesStore CategoriesStore}
* for which the row was updated.
*/
onRowUpdated : function(view, rowIndex, record)
{
// Rerender the colorPicker
if ( record.colorPicker ){
var el = Ext.get(view.getCell(rowIndex, 1).querySelector('.k-colorpicker-ct'));
record.colorPicker.renderTo = el.dom;
record.colorPicker.rendered = false;
record.colorPicker.el = undefined;
record.colorPicker.render(el);
}
},
* Event handler for the show event of the menu of the color pickers. Will
* add the class <i>k-menu-visible</i> to the cell element.
*
* @param {Ext.menu.ColorMenu} menu The menu that is shown.
* @param {Zarafa.common.ui.ColorPicker} colorPicker the color picker that
* fired the event.
*/
onColorPickerShowMenu : function(menu, colorPicker)
{
var cellEl = colorPicker.el.up('div').up('div');
cellEl.addClass('k-menu-visible');
},
* Event handler for the hide event of the menu of the color pickers. Will
* remove the class <i>k-menu-visible</i> to the cell element.
*
* @param {Ext.menu.ColorMenu} The menu that is hidden.
* @param {Zarafa.common.ui.ColorPicker} colorPicker the color picker that
* fired the event.
*/
onColorPickerHideMenu : function(menu, colorPicker)
{
var cellEl = colorPicker.el.up('div').up('div');
cellEl.removeClass('k-menu-visible');
},
/**
* Event handler for the select event of one of the color pickers. Will
* set the selected color on the category record.
*
* @param {Zarafa.common.ui.ColorPicker} colorPicker the color picker that
* fired the event.
* @param {String} color The color that was selected in hex RGB format.
* @param {Ext.data.Record} record The category record that this menu will
* act on.
*/
onSelectColor : function(colorPicker, color, record)
{
record.set('color', '#' + color);
},
/**
* Event handler for the cellclick event of the category grid. Will
* take care of the selection behavior of the grid.
*
* @param {Ext.grid.GridPanel} grid The category grid
* @param {Number} rowIndex The index of the row that was clicked.
* @param {Number} columnIndex The index of the column that was clicked.
* @param {Ext.EventObject} event The event object of the cellclick event.
*/
onCellClick : function(grid, rowIndex, columnIndex, event)
{
var targetEl = Ext.get(event.target);
// Check the class to make sure we don't select when the user clicks
// on one of the buttons (edit, delete, pin)
if ( columnIndex === 2 && targetEl.hasClass('x-grid3-col-category') ){
var sm = grid.getSelectionModel();
if ( sm.isSelected(rowIndex) ){
sm.deselectRow(rowIndex);
} else {
sm.selectRow(rowIndex, true);
}
}
},
/**
* Event handler for the rowclick event of the category grid. Will check
* if one of the pin/edit/delete icons was clicked and handle accordingly.
* also it will open {@link Zarafa.common.categories.dialogs.RenameCategoryPanel RenameCategoryPanel}
* when standard categories like 'Red', 'Green' etc. checkbox is checked for first time.
*
* @param {Ext.grid.GridPanel} grid The category grid
* @param {Number} rowIndex The index of the row that was clicked.
* @param {Ext.EventObject} event The event object of the rowclick event.
*/
onRowClick : function(grid, rowIndex, event)
{
// Make sure the grid will not scroll to the row that was focussed
// before the click by putting the focus on the clicked row.
grid.getView().focusRow(rowIndex);
var targetEl = Ext.get(event.target);
if ( targetEl.hasClass('k-grid-button-pin') && !targetEl.up('div').hasClass('zarafa-pin-fixed') ){
this.toggleCategoryPin(rowIndex);
} else if ( targetEl.hasClass('k-grid-button-edit') ){
this.editCategoryName(rowIndex);
} else if ( targetEl.hasClass('k-grid-button-delete') ){
this.deleteCategory(rowIndex);
} else if(targetEl.hasClass('x-grid3-row-checker')) {
// If check box is unchecked then just return.
if(!grid.getColumnModel().getColumnById('checker').isSelected(rowIndex)) {
return;
}
var category = grid.getStore().getAt(rowIndex);
var standardIndex = category.get('standardIndex');
if(Ext.isEmpty(standardIndex)) {
return;
}
if(category.get('used') === false) {
Zarafa.common.Actions.openRenameCategoryContent({
store: grid.getStore(),
isCategoryGrid : true,
categoryName : category.get('category'),
color : category.get('color')
});
}
}
},
/**
* Will handle clicks on the pin icon of a row.
*
* @param {Number} rowIndex The index of the row in which the pin icon
* was clicked.
*/
toggleCategoryPin : function(rowIndex)
{
var categoryRecord = this.categoriesGrid.getStore().getAt(rowIndex);
categoryRecord.set('quickAccess', !categoryRecord.get('quickAccess'));
},
/**
* Will open the grid editor of the category name of a row.
*
* @param {Number} rowIndex The index of the row in which the pin icon
* was clicked.
*/
editCategoryName : function(rowIndex)
{
this.categoriesGrid.startEditing(rowIndex, 2);
},
/**
* Will delete the category from the store that is bound to the grid.
* <i>ote: It will not be deleted from the settings until the user clicks
* on the 'Apply' button.</i>
*
* @param {Number} rowIndex The number of the row in the grid that shows
* the category that must be deleted.
*/
deleteCategory : function(rowIndex)
{
var store = this.categoriesGrid.getStore();
var categoryRecord = store.getAt(rowIndex);
var categoryName = Ext.util.Format.htmlEncode(categoryRecord.get('category'));
Zarafa.common.dialogs.MessageBox.addCustomButtons({
width: 400,
title: _('Delete Category'),
msg : String.format(_('Are you sure you want to delete the category "{0}"? Items already assigned this category won\'t be affected.'), categoryName),
icon: Ext.MessageBox.QUESTION,
fn : function(buttonName){
if ( buttonName === 'delete' ){
var categoryStore = this.categoriesGrid.getStore();
categoryStore.removeAt(rowIndex);
}
},
customButton: [{
name : 'delete',
text: _('Delete')
}, {
name : 'cancel',
text: _('Cancel')
}],
scope: this
});
},
/**
* Handler for the validateedit event of the grid. Will check if
* the entered category name does not already exist.
*
* @param {Object} event The {@link Ext.grid.EditorGridPanelView.validateedit event object}
*/
onValidateEdit : function(event)
{
var categoryStore = event.grid.getStore();
var categoryExists = false;
categoryStore.each(function(categoryRecord){
if ( categoryRecord === event.record ){
return;
}
if ( categoryRecord.get('category').toLowerCase() === event.value.toLowerCase() ){
categoryExists = true;
return false;
}
}, this);
if ( categoryExists ){
Zarafa.common.dialogs.MessageBox.alert(
_('Rename Category'),
_('A category named "' + Ext.util.Format.htmlEncode(event.value) + '" already exists. Please enter a different category name.'),
function(){
// Open the editor again so the user can change the name again
this.categoriesGrid.startEditing(event.row, 2);
},
this
);
event.cancel = true;
}
},
/**
* Handler for the afteredit event of the grid. Will make sure the new category name will
* be saved.
*
* @param {Object} event The {@link Ext.grid.EditorGridPanelView.afteredit event object}
*/
onAfterEdit : function(event)
{
if ( event.value !== event.originalValue ){
event.record.set('stored', true);
}
},
/**
* return all categories records from the managed categories list
* @return {Array} of {@link Ext.data.Record Records}
* @private
*/
getAvailableCategories : function()
{
var activeAvailableCategories = [];
var categoriesGrid = this.categoriesGrid;
var categories = Zarafa.common.categories.Util.getAllCategories(this.record);
for (var i = 0; i < categories.length; i++) {
for (var j = 0; j < categoriesGrid.getStore().getCount(); j++) {
if (categories[i] == categoriesGrid.getStore().getAt(j).get('category')) {
activeAvailableCategories.push(categoriesGrid.getStore().getAt(j));
break;
}
}
}
return activeAvailableCategories;
},
/**
* Returns an array with the names of all selected categories.
* @return {Array} An array with the names of the selected categories
*/
getSelectedCategories : function()
{
var records = this.categoriesGrid.getSelectionModel().getSelections();
return records.map(function(record) {
return record.get('category');
}, this);
}
});
Ext.reg('zarafa.categoriespanel', Zarafa.common.categories.dialogs.CategoriesPanel);