Ext.namespace('Zarafa.plugins.files');

/**
 * @class Zarafa.plugins.files.FilesContext
 * @extends Zarafa.core.Context
 *
 * This class will add a new context to grommunio Web. The new context
 * offers a filebrowser for the Files backend.
 */
Zarafa.plugins.files.FilesContext = Ext.extend(Zarafa.core.Context, {

	/**
	 * When searching, this property marks the {@link Zarafa.core.Context#getCurrentView view}
	 * which was used before {@link #onSearchStart searching started}.
	 *
	 * @property
	 * @type Mixed
	 * @private
	 */
	oldView: undefined,

	/**
	 * When searching, this property marks the {@link Zarafa.core.Context#getCurrentViewMode viewmode}
	 * which was used before {@link #onSearchStart searching started}.
	 *
	 * @property
	 * @type Mixed
	 * @private
	 */
	oldViewMode: undefined,

	/**
	 * accountsStore which contains all configured
	 * {@link Zarafa.plugins.files.data.AccountRecord accounts}.
	 *
	 * @property
	 * @type Zarafa.plugins.files.data.AccountStore
	 * @private
	 */
	accountsStore: undefined,

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			current_view     : Zarafa.plugins.files.data.Views.LIST,
			current_view_mode: Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW
		});

		this.registerInsertionPoint('context.settings.categories', this.createSettingCategories, this);
		this.registerInsertionPoint('main.maintabbar.left', this.createMainTab, this);
		this.registerInsertionPoint('main.maintoolbar.new.item', this.createNewFilesButton, this);
		this.registerInsertionPoint('main.toolbar.actions.last', this.createMainToolbarButtons, this);
		this.registerInsertionPoint('navigation.center', this.createFilesNavigationPanel, this);

		this.registerInsertionPoint('main.attachment.method', this.createAttachmentDownloadInsertionPoint, this);
		this.registerInsertionPoint('common.contextmenu.attachment.actions', this.createAttachmentUploadInsertionPoint, this);
		this.registerInsertionPoint('context.mail.contextmenu.actions', this.createEmailUploadInsertionPoint, this);

		Zarafa.plugins.files.FilesContext.superclass.constructor.call(this, config);

		var notificationResolver = container.getNotificationResolver();
		if (Ext.isFunction(notificationResolver.addIPFNotificationModule)) {
			notificationResolver.addIPFNotificationModule("fileshierarchynotifier");
		}

		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.attachdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.createfolderdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.createfiledialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.fileinfopanel');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.sharedialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.uploadstatusdialog');
		Zarafa.core.data.SharedComponentType.addProperty('zarafa.plugins.files.treecontextmenu');
		Zarafa.core.data.SharedComponentType.addProperty('common.dialog.attachments.files');
	},

	/**
	 * Adds a new tab item to the top tab bar of grommunio Web.
	 *
	 * @returns {Object} The button for the top tab bar.
	 */
	createMainTab: function () {
		return {
			text         : this.getDisplayName(),
			tabOrderIndex: 7,
			context      : this.getName()
		};
	},

	/**
	 * This method hooks to the attachments chooser button and allows users to add files from
	 * the Files plugin to their emails.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createAttachmentDownloadInsertionPoint: function (include, btn)
	{
		return {
			text: _('Add from Files'),
			handler: this.showFilesDownloadAttachmentDialog,
			scope: btn,
			context: this,
			iconCls: 'icon_files_category',
			disabled: !this.isAccountsConfigured()
		};
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel file chooser panel}.
	 *
	 * @param btn
	 */
	showFilesDownloadAttachmentDialog: function (btn)
	{
		// TODO: Move this function to action.js

		var activeMenuItem = this.menu.activeItem;
		var component = Zarafa.core.data.SharedComponentType['common.dialog.attachments.files'];
		Zarafa.core.data.UIFactory.openLayerComponent(component, this.record, {
			title: _('Add attachment from Files'),
			modal: true,
			model: activeMenuItem.context.getModel()
		});
	},

	/**
	 * Helper function which will return false if no account is configured, True otherwise.
	 * @returns {boolean} True if accounts configured, false otherwise.
	 */
	isAccountsConfigured: function ()
	{
		var accountStore = this.getAccountsStore();
		var foundActiveStore =  accountStore.findBy(function (item) {
			if (item.get("status") === Zarafa.plugins.files.data.AccountRecordStatus.OK) {
				return true;
			}
		});
		return foundActiveStore !== -1;
	},

	/**
	 * This method hooks to the attachment context menu and allows users to store files from
	 * their emails to the  Files plugin.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createAttachmentUploadInsertionPoint: function (include, btn)
	{
		return {
			text   : _('Add to Files'),
			handler: this.showFilesUploadAttachmentDialog,
			scope  : btn,
			iconCls: 'icon_files_category',
			beforeShow : this.onAttachmentUploadBeforeShow.createDelegate(this)
		};
	},

	/**
	 * Function will be called before {@link Zarafa.common.attachment.ui.AttachmentContextMenu AttachmentContextMenu} is shown
	 * so we can decide which item should be disabled.
	 * @param {Zarafa.core.ui.menu.ConditionalItem} item context menu item
	 * @param {Zarafa.core.data.IPMAttachmentRecord} record attachment record on which context menu is shown
	 */
	onAttachmentUploadBeforeShow : function(item, record) {
		// embedded messages can not be downloaded to files
		item.setDisabled(record.isEmbeddedMessage());
		// unsaved attachments can not be added to files without depending on Webapp internals (AttachmentState)
		item.setDisabled(record.isTmpFile());
		// If accounts not configured then disable it.
		item.setDisabled(!this.isAccountsConfigured());
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel folder chooser panel}.
	 */
	showFilesUploadAttachmentDialog: function(button)
	{
		// TODO: Move this function to action.js
		var attachmentRecord = this.records;
		var attachmentStore = attachmentRecord.store;

		var store = attachmentStore.getParentRecord().get('store_entryid');
		var entryid = attachmentStore.getAttachmentParentRecordEntryId();
		var attachNum = [];
		if (attachmentRecord.isUploaded()) {
			attachNum[0] = attachmentRecord.get('attach_num');
		} else {
			attachNum[0] = attachmentRecord.get('tmpname');
		}
		var dialog_attachments = attachmentStore.getId();
		var filename = attachmentRecord.get('name');

		var jsonRecords = [{
			entryid           : entryid,
			store             : store,
			attachNum         : attachNum,
			dialog_attachments: dialog_attachments,
			filename          : filename
		}];

		var configRecord = {
			items: jsonRecords,
			type : "attachment",
			count: jsonRecords.length
		};

		var model = this.activeItem.plugin.getModel();
		Zarafa.plugins.files.data.Actions.openSaveToFilesDialog(model, {response : configRecord});
	},

	/**
	 * This method hooks to the email context menu and allows users to store emails from
	 * to the  Files plugin.
	 *
	 * @param include
	 * @param btn
	 * @returns {Object}
	 */
	createEmailUploadInsertionPoint: function (include, btn)
	{
		return {
			text : _('Add to Files'),
			handler: this.showFilesUploadEmailDialog,
			scope : btn,
			iconCls: 'icon_files_category',
			disabled: !this.isAccountsConfigured()
		};
	},

	/**
	 * This method will open the {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel folder chooser panel}.
	 */
	showFilesUploadEmailDialog: function ()
	{
		// TODO: Move this function to action.js
		var records = this.records;
		if (!Array.isArray(records)) {
			records = [records];
		}

		var jsonRecords = [];
		for (var i = 0, len = records.length; i < len; i++) {
			var fileName = Ext.isEmpty(records[i].get('subject')) ? _('Untitled') : records[i].get('subject');
			jsonRecords[i] = {
				store   : records[i].get('store_entryid'),
				entryid : records[i].get('entryid'),
				filename: fileName + ".eml"
			};
		}

		var configRecord = {
			items: jsonRecords,
			type : "mail",
			count: jsonRecords.length
		};

		var model = this.activeItem.plugin.getModel();
		Zarafa.plugins.files.data.Actions.openSaveToFilesDialog(model, {response : configRecord});
	},

	/**
	 * Create the files {@link Zarafa.settings.SettingsMainCategory Settings Category}
	 * to the {@link Zarafa.settings.SettingsContext}. This will create new
	 * {@link Zarafa.settings.ui.SettingsCategoryTab tabs} for the
	 * {@link Zarafa.plugins.files.settings.SettingsFilesCategory Files Plugin}.
	 * @return {Object} configuration object for the categories to register
	 */
	createSettingCategories: function () {
		return {
			xtype: 'filesplugin.settingsmaincategory',
			model : this.getModel(),
			store : this.getAccountsStore()
		}
	},

	/**
	 * This method returns the context model for the files context.
	 * If the model was not yet initialized, it will create a new model.
	 *
	 * @return {Zarafa.plugins.files.FilesContextModel} The files context model.
	 */
	getModel: function ()
	{
		if (!Ext.isDefined(this.model)) {
			this.model = new Zarafa.plugins.files.FilesContextModel({
				accountStore : this.getAccountsStore()
			});
		}
		return this.model;
	},

	/**
	 * Function will create an object of {@link Zarafa.plugins.files.data.AccountStore AccountStore} if
	 * it is not created yet.
	 * @return {Zarafa.plugins.files.data.AccountStore} return {@link Zarafa.plugins.files.data.AccountStore AccountStore}
	 * object.
	 */
	getAccountsStore : function ()
	{
		if(!Ext.isDefined(this.accountsStore)) {
			this.accountsStore = new Zarafa.plugins.files.data.AccountStore();
		}
		return this.accountsStore;
	},

	/**
	 * Bid for the given {@link Zarafa.hierarchy.data.MAPIFolderRecord folder}
	 * This will bid on any folder of container class 'IPF.Files'.
	 *
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder for which the context is bidding.
	 * @return {Number} 1 when the contexts supports the folder, -1 otherwise.
	 */
	bid: function (folder) {

		if (folder instanceof Zarafa.plugins.files.data.FilesFolderRecord) {
			return 1;
		}

		return -1;
	},

	/**
	 * Bid for the type of shared component and the given record.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @returns {Number}
	 */
	bidSharedComponent: function (type, record) {
		var bid = -1;

		if (Ext.isArray(record)) {
			record = record[0];
		}

		switch (type) {
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.attachdialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.fileinfopanel']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog']:
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfiledialog']:
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles']:
				bid = 1;
				break;
			case Zarafa.core.data.SharedComponentType['common.create']:
			case Zarafa.core.data.SharedComponentType['common.view']:
			case Zarafa.core.data.SharedComponentType['common.preview']:
				if (record instanceof Zarafa.core.data.IPMRecord && record.isMessageClass('IPM.Files', true)) {
					bid = 1;
				}
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.files']:
				if (record instanceof Zarafa.core.data.IPMRecord) {
					if (record.supportsAttachments()) {
						bid = 1;
					}
				}
				break;
			case Zarafa.core.data.SharedComponentType['common.contextmenu']:
				if (record instanceof Zarafa.core.data.IPMRecord && record.isMessageClass('IPM.Files', true)) {
					bid = 1;
				}
				break;
			default :
				break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent: function (type, record) {
		var component;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.fileinfopanel']:
				component = Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.create']:
				component = Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfiledialog']:
				component = Zarafa.plugins.files.ui.dialogs.CreateFileContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog']:
				component = Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog']:
				component = Zarafa.plugins.files.ui.dialogs.ShareContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog']:
				component = Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.view']:
			case Zarafa.core.data.SharedComponentType['common.preview']:
				component = Zarafa.plugins.files.ui.FilesRecordViewPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.contextmenu']:
				component = Zarafa.plugins.files.ui.FilesMainContextMenu;
				break;
			case Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu']:
				component = Zarafa.plugins.files.ui.FilesTreeContextMenu;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.files']:
				component = Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles']:
				component = Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel;
				break;
			default:
				break;
		}
		return component;
	},

	/**
	 * Creates the files tree that is shown when the user selects the files context from the
	 * button panel. It shows a tree of available files folders
	 * @private
	 */
	createFilesNavigationPanel : function()
	{
		return {
			xtype : 'zarafa.contextnavigation',
			context : this,
			store : this.getAccountsStore(),
			restrictToShowAllFolderList : true,
			items : [{
				xtype : 'panel',
				id: 'zarafa-navigationpanel-file-navigation',
				cls: 'zarafa-context-navigation-block',
				ref: 'filesnavigation',
				layout: 'fit',
				items : [{
					xtype : 'filesplugin.navigatortreepanel',
					id: 'zarafa-navigationpanel-files-navigation-tree',
					model: this.getModel(),
					FilesFilter: Zarafa.plugins.files.data.FileTypes.FOLDER,
					hideDeletedFolders : false,
					enableDD : true,
					enableItemDrop : true,
					deferredLoading : true
				}]
			}]
		};
	},

	/**
	 * This method creates the {@link Zarafa.plugins.files.ui.FilesMainPanel main content panel}
	 * which will contain the file browser.
	 *
	 * @returns {Object}
	 */
	createContentPanel: function () {
		return {
			xtype  : 'filesplugin.filesmainpanel',
			title : this.getDisplayName(),
			context: this
		};
	},

	/**
	 * Create "New File" {@link Ext.menu.MenuItem item} for the "New item"
	 * {@link Ext.menu.Menu menu} in the {@link Zarafa.core.ui.MainToolbar toolbar}.
	 * This button should be shown in all {@link Zarafa.core.Context contexts} and
	 * is used to upload a new file.
	 *
	 * @returns {Object}
	 */
	createNewFilesButton: function () {
		return {
			xtype       : 'menuitem',
			text        : _('Upload file'),
			plugins     : 'zarafa.menuitemtooltipplugin',
			iconCls     : 'icon_files_category',
			newMenuIndex: 6,
			context     : this.getName(),
			handler     : function () {
				Zarafa.plugins.files.data.Actions.openCreateFilesContent(this.getModel());
			},
			scope       : this
		};
	},

	/**
	 * Returns the buttons for the dropdown list of the VIEW-button in the main toolbar. It will use the
	 * main.maintoolbar.view.files insertion point to allow other plugins to add their items at the end.
	 *
	 * @return {Array} An array of components.
	 */
	getMainToolbarViewButtons: function () {
		var items = container.populateInsertionPoint('main.maintoolbar.view.files', this) || [];

		var defaultItems = [{
			overflowText : _('No preview'),
			iconCls      : 'icon_previewpanel_off',
			text         : _('No preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.NO_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}, {
			overflowText : _('Right preview'),
			iconCls      : 'icon_previewpanel_right',
			text         : _('Right preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}, {
			overflowText : _('Bottom preview'),
			iconCls      : 'icon_previewpanel_bottom',
			text         : _('Bottom preview'),
			valueViewMode: Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW,
			valueDataMode: Zarafa.plugins.files.data.DataModes.ALL,
			handler      : this.onContextSelectView,
			scope        : this
		}];

		defaultItems.push();

		return defaultItems.concat(items);
	},

	/**
	 * Adds buttons to the main toolbar like the view switcher button.
	 *
	 * @return {Array}
	 */
	createMainToolbarButtons: function () {
		return [{
			xtype    : 'splitbutton',
			ref      : '../../../filesSwitchViewButton',
			tooltip  : _('Switch view'),
			scale    : 'large',
			iconCls  : 'icon_viewswitch',
			handler  : function () {
				this.showMenu();
			},
			menu : {
				xtype : 'menu',
				items: [{
					text        : _('List'),
					overflowText: _('List'),
					iconCls     : 'icon_contact_list',
					valueView   : Zarafa.plugins.files.data.Views.LIST,
					handler     : this.onSwitchView,
					scope       : this
				}, {
					text        : _('Icons'),
					overflowText: _('Icons'),
					iconCls     : 'icon_note_icon_view',
					valueView   : Zarafa.plugins.files.data.Views.ICON,
					handler     : this.onSwitchView,
					scope       : this
				}]
			},
			listeners: {
				afterrender: this.onAfterRenderMainToolbarButtons,
				menuhide : function(splitBtn, viewMenu){
					viewMenu.find().forEach(function(item){
						var hasClass = item.getEl().hasClass('x-menu-item-selected');
						if(hasClass) {
							item.getEl().removeClass('x-menu-item-selected');
						}
					}, this);
				},
				menushow : function (splitBtn, viewMenu) {
					var menuItem = viewMenu.find('valueView', this.getCurrentView())[0];
					if (Ext.isDefined(menuItem)) {
						menuItem.addClass('x-menu-item-selected');
					}
				},
				scope : this
			}
		}]
	},

	/**
	 * Registers to the {@link Zarafa.core.Container#contextswitch contextswitch} event on the
	 * {@link Zarafa.core.Container container} so the visibility of the button can be toggled
	 * whenever the context is switched. We do this after the button is rendered.
	 *
	 * @param {Ext.Button} btn The button
	 */
	onAfterRenderMainToolbarButtons: function (btn) {
		btn.mon(container, 'contextswitch', function (parameters, oldContext, newContext) {
			this.setVisiblityMainToolbarButton(btn, newContext);
		}, this);

		btn.mon(this, 'viewchange', function (context, newView, oldView) {
			this.setVisiblityMainToolbarButton(btn, context);
		}, this);

		this.setVisiblityMainToolbarButton(btn);
	},

	/**
	 * Determines whether the passed button has to be shown or not based on what
	 * {@link Zarafa.core.Context Context} is active. If no Context is supplied as an argument it
	 * will get that from the {@link Zarafa.core.Container container}.
	 *
	 * @param {Ext.Button} btn The button.
	 * @param {Zarafa.core.Context} activeContext (Optional) The active Context.
	 */
	setVisiblityMainToolbarButton: function (btn, activeContext) {
		activeContext = activeContext || container.getCurrentContext();
		if (activeContext === this) {
			btn.show();
			var accountStore = this.getAccountsStore();
			btn.setDisabled(Ext.isEmpty(accountStore.getRange()));
		} else {
			btn.hide();
		}
	},

	/**
	 * Event handler which is fired when one of the view buttons has been pressed.
	 *
	 * @param {Ext.Button} button The button which was pressed
	 */
	onSwitchView: function (button) {
		var viewMode = this.getCurrentViewMode();
		this.switchView(button.valueView, viewMode);
	},

	/**
	 * Event handler which is fired when one of the View buttons
	 * has been pressed. This will call {@link #setView setView}
	 * to update the view.
	 *
	 * @param {Ext.Button} button The button which was pressed
	 */
	onContextSelectView: function (button) {
		this.getModel().setDataMode(button.valueDataMode);

		var view = button.valueView;
		var viewMode = button.valueViewMode;

		if (!Ext.isDefined(button.valueView)) {
			view = this.getCurrentView();
		}
		if (!Ext.isDefined(button.valueViewMode)) {
			viewMode = this.getCurrentViewMode();
		}

		this.switchView(view, viewMode);

		this.getModel().setPreviewRecord(undefined, true);
	}
});

/**
 * This code gets executed after grommunio Web has loaded.
 * It hooks the context to grommunio Web.
 */
Zarafa.onReady(function () {
	if (container.getSettingsModel().get('zarafa/v1/plugins/files/enable') === true) {
		container.registerContext(new Zarafa.core.ContextMetaData({
			name             : 'filescontext',
			displayName      : _('Files'),
			allowUserVisible : false,
			pluginConstructor: Zarafa.plugins.files.FilesContext
		}));
	}
});
Ext.namespace('Zarafa.plugins.files.context');

/**
 * @class Zarafa.plugins.files.FilesContextModel
 * @extends Zarafa.core.ContextModel
 *
 * This class will instantiate a new {@link Zarafa.plugins.files.data.FilesRecordStore files store} object.
 */
Zarafa.plugins.files.FilesContextModel = Ext.extend(Zarafa.core.ContextModel, {

	/**
	 * @cfg {Zarafa.plugins.files.data.BackendStore} backendStore which
	 * contains {@link Zarafa.plugins.files.data.FilesBackendRecord backend} records.
	 */
	backendStore: undefined,

	/**
	 * @cfg {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore} holds
	 * {@link Zarafa.plugins.files.data.FilesStoreRecord FilesStoreRecord} as records, which defines store information
	 * of all opened stores.
	 */
	hierarchyStore: undefined,

	/**
	 * @cfg {@link Zarafa.plugins.files.data.AccountStore AccountStore} holds
	 * {@link Zarafa.plugins.files.data.AccountRecord AccountRecord} as records,
	 */
	accountStore: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object.
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			statefulRecordSelection: true
		});

		if(!Ext.isDefined(config.hierarchyStore)) {
			config.hierarchyStore = new Zarafa.plugins.files.data.FilesHierarchyStore();
		}

		if (!Ext.isDefined(config.store)) {
			config.store = new Zarafa.plugins.files.data.FilesRecordStore(config);
		}

		if(!Ext.isDefined(config.backendStore)) {
			config.backendStore = new Zarafa.plugins.files.data.BackendStore();
		}

		Zarafa.plugins.files.FilesContextModel.superclass.constructor.call(this, config);

		// Hook an event handler to the 'load' event of the
		// hierarchyStore. Call the event handler directly
		// in case the hierarchy has already been loaded.
		var hierarchyStore = this.getHierarchyStore();
		hierarchyStore.on('load', this.onHierarchyLoad, this);
		this.onHierarchyLoad(hierarchyStore);

		if (this.accountStore) {
			this.accountStore.on({
				'write' : this.updateAccountStore,
				scope : this
			});
		}
	},

	/**
	 * Event handler triggered when {@link Zarafa.plugins.files.data.AccountStore AccountStore} has been
	 * updated. it will load the the {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore} to update the
	 * {@link Zarafa.plugins.files.ui.Tree Tree}.
	 */
	updateAccountStore : function()
	{
		this.hierarchyStore.load();
	},

	/**
	 * Create a new {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 *
	 * @param {String} parentid id of the parent folder
	 * @return {Zarafa.plugins.files.data.FilesRecord} The new {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 */
	createRecord: function (parentid) {
		parentid = parentid || "/";

		var record = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Files', {
			store_entryid : "files",
			parent_entryid: parentid
		});
		record.store = this.getStore();
		return record;
	},

	/**
	 * Update the current preview {@link Zarafa.core.data.IPMRecord}
	 * This will fire the event {@link #previewrecordchange}.
	 *
	 * @param {mixed} record The record which is set as preview or false to refresh the old record
	 * @param {Boolean} refresh (optional) true to just refresh the old record
	 */
	setPreviewRecord: function (record, refresh) {
		if (container.getCurrentContext().getName() === "filescontext") {
			var previewPanel = Zarafa.plugins.files.data.ComponentBox.getPreviewPanel();
			var panelConstructor;

			if (refresh && this.previewRecord) {

				panelConstructor = container.getSharedComponent(Zarafa.core.data.SharedComponentType['common.preview'], this.previewRecord);

				previewPanel.removeAll();
				if (Ext.isDefined(panelConstructor)) {
					previewPanel.add(new panelConstructor());
					previewPanel.doLayout();
					previewPanel.fileinfo.update(this.previewRecord);
				}

			} else if (this.previewRecord !== record) {
				this.previewRecord = record;

				if (Ext.isDefined(record)) {
					panelConstructor = container.getSharedComponent(Zarafa.core.data.SharedComponentType['common.preview'], record);

					if (Ext.isDefined(panelConstructor) && previewPanel.fileinfo instanceof panelConstructor) {
						previewPanel.fileinfo.update(record);
					} else {

						previewPanel.removeAll();
						if (panelConstructor) {
							previewPanel.add(new panelConstructor());
							previewPanel.doLayout();
							previewPanel.fileinfo.update(record);
						}
					}
				} else {
					previewPanel.removeAll();
				}
			}
		}
	},

	/**
	 * Resume the loading actions again if {@link #suspendLoading}.
	 * This will allow the {@link #load} function to use the {@link #store}
	 * again to send requests to the server. This will also call {@link #load}
	 * with the last options that were given to it while being suspended.
	 *
	 * @param {Boolean} discard Discard the calls to load while being suspended.
	 */
	resumeLoading : function(discard)
	{
		if (this.suspended === true) {
			this.suspended = false;
			if (discard !== false) {
				var defaultFolder = this.getDefaultFolder();
				if (Ext.isDefined(defaultFolder)) {
					var options = Ext.apply({}, {
						folder : this.getDefaultFolder()
					});
					this.store.load(options);
				}
			}
			delete this.suspendData;
		}
	},

	/**
	 * Function used to get the {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore}
	 * @return {Zarafa.plugins.files.data.FilesHierarchyStore} return the {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore}
	 */
	getHierarchyStore: function ()
	{
		if(!this.hierarchyStore) {
			this.hierarchyStore = new Zarafa.plugins.files.data.FilesHierarchyStore();
		}
		return this.hierarchyStore;
	},

	/**
	 * Removes a folder from the selected folder list.
	 * This function automatically causes the store to
	 * reload its contents. This method triggers the
	 * {@link #folderchange} event if the folder was previously in the list.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder folder to remove.
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} the folder if it was removed,
	 * or undefined otherwise (i.e. it was not in the folder list).
	 */
	removeFolder : function(folder)
	{
		var localFolder = this.getFolder(folder.get('entryid'));

		if (!Ext.isDefined(localFolder)) {
			var isParentFolderOfSelectedFolder = folder.isParentFolderOfSelectedFolder(this.getDefaultFolder());
			if (isParentFolderOfSelectedFolder === true) {
				this.folders.remove(this.getDefaultFolder());
				this.setParentFolderToDefaultFolder(folder);
			} else {
				return undefined;
			}
		} else {
			// Remove the folder from the list.
			this.folders.remove(localFolder);
			this.setParentFolderToDefaultFolder(localFolder);
		}

		this.onFolderChange(this, this.folders);

		// Fire 'folderchange' event.
		this.fireEvent('folderchange', this, this.folders);

		this.load();

		return localFolder;
	},

	/**
	 * Helper function which used to push the parent folder of currently selected into {@link #folders} array.
	 * If parent folder is undefined than it will push the {@link #defaultFolder} into {@link #folders} array.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The folder which is going to be deleted.
	 */
	setParentFolderToDefaultFolder : function(folder)
	{
		// A ContextModel must always have at least
		// 1 folder loaded. When the last folder is
		// being unloaded, load the parent or default folder again.
		if (Ext.isEmpty(this.folders)) {
			var parentFolder = folder.getParentFolder();
			if (parentFolder) {
				this.folders.push(parentFolder);
			} else {
				this.folders.push(this.defaultFolder);
			}
		}
	},

	/**
	 * Event handler which is executed right before the {@link #folderchange}
	 * event is fired. This allows subclasses to update the folders.
	 *
	 * @param {Zarafa.core.ContextModel} model The model which fired the event.
	 * @param {Array} folders selected folders as an array of {@link Zarafa.plugins.files.data.FilesFolderRecord Folder} objects.
	 * @private
	 */
	onFolderChange : Ext.emptyFn,

	/**
	 * Sets {@link #defaultFolder default folder} for the particular {@link Zarafa.core.Context context}.
	 * This will help while opening new item dialog from other contexts
	 * e.g. Create new Contact from Inbox, at this moment we need {@link #defaultFolder} to create the item.
	 *
	 * When the {@link Zarafa.core.Context context} was opened without any folders,
	 * this also means we can now {@link #addFolder load} the {@link #defaultFolder}.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store that holds hierarchy data.
	 * @private
	 */
	onHierarchyLoad : function (hierarchyStore) {
		this.defaultFolder = hierarchyStore.getFolder("#R#");

		// If we haven't any folders yet. We should obtain
		// the previously used folders or the default folder.
		var openfolders = [];

		if (!Ext.isEmpty(this.last_used_folders)) {
			for (var key in this.last_used_folders) {
				var store = hierarchyStore.getById(key);
				if (!store) {
					continue;
				}

				var folders = store.getSubStore('folders');
				var statefolders = this.last_used_folders[key];
				for (var i = 0; i < statefolders.length; i++) {
					var folder = folders.getById(statefolders[i]);
					if (!folder) {
						continue;
					}

					openfolders.push(folder);
				}
			}
		}

		if (Ext.isEmpty(openfolders) && this.defaultFolder) {
			openfolders.push(this.defaultFolder);
		}

		this.setFolders(openfolders);
	}
});
Ext.namespace('Zarafa.plugins.files');

/**
 * @class Zarafa.plugins.files.FilesPlugin
 * @extends Zarafa.core.Plugin
 *
 * This class integrates the Files plugin into grommunio Web.
 * It allows users to set up and manage their Files accounts.
 */
Zarafa.plugins.files.FilesPlugin = Ext.extend(Zarafa.core.Plugin, {

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		this.registerModules();

		Zarafa.plugins.files.FilesPlugin.superclass.constructor.call(this, config);
	},

	/**
	 * This method is called by the parent and will initialize all insertion points
	 * and shared components.
	 */
	initPlugin: function () {
		Zarafa.plugins.files.FilesPlugin.superclass.initPlugin.apply(this, arguments);

		// FIXME: Check we need this?
		Zarafa.core.mapi.IconIndex.addProperty("files");

		Zarafa.core.mapi.IconIndex["folder_note"] = 5378;
		Zarafa.core.mapi.IconIndex["files"] = 5377;

		Zarafa.core.data.SharedComponentType.addProperty('plugins.files.onlyofficepanel');

		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.accountedit');
		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.featurequotainfo');
		Zarafa.core.data.SharedComponentType.addProperty('filesplugin.featureversioninfo');
		Zarafa.core.data.SharedComponentType.addProperty('common.dialog.attachments.savetofiles');
	},

	/**
	 * This method registers the Files module names to grommunio Web.
	 */
	registerModules: function () {
		Zarafa.core.ModuleNames['IPM.FILESACCOUNT'] = {
			list: 'filesaccountmodule',
			item: 'filesaccountmodule'
		};
	},

	/**
	 * Bid for the type of shared component and the given record.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @returns {Number}
	 */
	bidSharedComponent: function (type, record) {
		var bid = -1;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['filesplugin.accountedit']:
			case Zarafa.core.data.SharedComponentType['filesplugin.featurequotainfo']:
			case Zarafa.core.data.SharedComponentType['filesplugin.featureversioninfo']:
			case Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles']:
			case Zarafa.core.data.SharedComponentType['plugins.files.onlyofficepanel']:
				bid = 1;
				break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 *
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent: function (type, record) {
		var component;
		switch (type) {
			case Zarafa.core.data.SharedComponentType['filesplugin.accountedit']:
				component = Zarafa.plugins.files.settings.ui.AccountEditContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featurequotainfo']:
				component = Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['filesplugin.featureversioninfo']:
				component = Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel;
				break;
			case Zarafa.core.data.SharedComponentType['plugins.files.onlyofficepanel']:
				component = Zarafa.plugins.files.ui.ContentPanel;
				break;
		}

		return component;
	}
});

/**
 * This code gets executed after grommunio Web has loaded.
 * It hooks the plugin to grommunio Web.
 */
Zarafa.onReady(function () {
	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name             : 'files',
		displayName      : _('Files Plugin'),
		about            : Zarafa.plugins.files.ABOUT,
		allowUserDisable : true,
		pluginConstructor: Zarafa.plugins.files.FilesPlugin
	}));
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.AccountRecordFields
 *
 * These fields will be available in all 'IPM.FilesAccount' type messages.
 */
Zarafa.plugins.files.data.AccountRecordFields = [
	{name: 'id'},
	{name: 'name'},
	{name: 'status'},
	{name: 'status_description'},
	{name: 'backend'},
	{name: 'backend_config'},
	{name: 'backend_features'},
	{name: 'account_sequence', type: 'number'},
	{name: 'cannot_change', type: 'boolean', defaultValue: false}
];

/**
 * @class Zarafa.plugins.files.data.AccountRecordStatus
 *
 * This object contains all valid status codes that an account can have.
 */
Zarafa.plugins.files.data.AccountRecordStatus = {
	OK     : "ok",
	ERROR  : "err",
	NEW    : "new",
	UNKNOWN: "unk"
};

/**
 * @class Zarafa.plugins.files.data.AccountRecordStatus
 *
 * This object contains all available feature codes that an account can have.
 */
Zarafa.plugins.files.data.AccountRecordFeature = {
	QUOTA       : "Quota",
	VERSION_INFO: "VersionInfo",
	SHARING     : "Sharing",
	STREAMING   : "Streaming",
	OAUTH       : "OAUTH"
};

/**
 * @class Zarafa.plugins.files.data.AccountRecord
 * @extends Zarafa.core.data.IPMRecord
 */
Zarafa.plugins.files.data.AccountRecord = Ext.extend(Zarafa.core.data.IPMRecord, {

	/**
	 * Applies all data from an {@link Zarafa.plugins.files.data.AccountRecord AccountRecord}
	 * to this instance. This will update all data.
	 *
	 * @param {Zarafa.plugins.files.data.AccountRecord} record The record to apply to this
	 * @return {Zarafa.plugins.files.data.AccountRecord} this
	 */
	applyData: function (record) {
		this.beginEdit();

		Ext.apply(this.data, record.data);
		Ext.apply(this.modified, record.modified);

		this.dirty = record.dirty;

		this.endEdit(false);

		return this;
	},

	/**
	 * Check if the account support the given feature.
	 *
	 * @param featureName Should be one of Zarafa.plugins.files.data.AccountRecordFeature children.
	 * @return {boolean}
	 */
	supportsFeature: function (featureName) {
		var features = this.get('backend_features') || [];

		return features.some(function(feature) {
			return feature === featureName;
		});
	},

	/**
	 * Check if the account support the given feature.
	 *
	 * @param featureName Should be one of Zarafa.plugins.files.data.AccountRecordFeature children.
	 * @return {boolean}
	 */
	renewOauthToken: function () {
		if(!this.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.OAUTH)) {
			return false;
		}

		// show the frontend panel
		Zarafa.plugins.files.backend[this.get('backend')].data.OAUTH.reAuthenticate(this.get('id'));

		return true;
	}
});


Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.FilesAccount', Zarafa.plugins.files.data.AccountRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM.FilesAccount', Zarafa.plugins.files.data.AccountRecord);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.AccountStore
 * @extends Zarafa.core.data.ListModuleStore
 * @xtype filesplugin.accountstore
 *
 * This store will hold all Files accounts that a user owns.
 */
Zarafa.plugins.files.data.AccountStore = Ext.extend(Zarafa.core.data.ListModuleStore, {

	/**
	 * @constructor
	 */
	constructor: function (config)
	{
		config = Ext.applyIf(config || {}, {
			preferredMessageClass: 'IPM.FilesAccount',
			autoLoad: true,
			autoSave: true,
			defaultSortInfo: {
				field: 'account_sequence',
				direction: 'asc'
			}
		});

		Zarafa.plugins.files.data.AccountStore.superclass.constructor.call(this, config);

		this.addEvents(
			/**
			 * @event reorder
			 * Fires when order of a configured account is changed in
			 * {@link Zarafa.plugins.files.settings.ui.AccountGrid AccountGrid}
			 *
			 * @param {Zarafa.plugins.files.data.AccountRecord} firstAccount which reorder.
			 * @param {Zarafa.plugins.files.data.AccountRecord} secondAccount which reorder.
			 */
			'reorder'
		);
	}
});

Ext.reg('filesplugin.accountstore', Zarafa.plugins.files.data.AccountStore);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.Actions
 * @singleton
 *
 * Common actions which can be used within {@link Ext.Button buttons}
 * or other {@link Ext.Component components} with action handlers.
 */
Zarafa.plugins.files.data.Actions = {

	/**
	 * The internal 'iframe' which is hidden from the user, which is used for downloading
	 * attachments. See {@link #doOpen}.
	 *
	 * @property
	 * @type Ext.Element
	 */
	downloadFrame: undefined,

	/**
	 * Converts received file information to attachment record.
	 *
	 * @param {Object} record
	 * @private
	 */
	convertDownloadedFileInfoToAttachmentRecord: function (record) {
		var attachmentRecord = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.MAPI_ATTACH);

		attachmentRecord.set('tmpname', record.tmpname);
		attachmentRecord.set('name', record.name);
		attachmentRecord.set('size', record.size);
		return attachmentRecord;
	},

	/**
	 * Open a Panel in which a new {@link Zarafa.core.data.IPMRecord record} can be
	 * further edited.
	 *
	 * @param {Zarafa.core.data.IPMRecord} emailRecord The email record that will be edited.
	 * @param {Array} records Filerecords that will be added as attachments.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateMailContent: function (emailRecord, records, config) {
		var attachmentStore = emailRecord.getAttachmentStore();

		Ext.each(records, function (record) {
			var attachmentRecord = this.convertDownloadedFileInfoToAttachmentRecord(record);
			attachmentStore.add(attachmentRecord);
		}, this);

		Zarafa.core.data.UIFactory.openCreateRecord(emailRecord, config);
	},

	/**
	 * Open a Panel in which a new {@link Zarafa.core.data.IPMRecord record} can be
	 * further edited.
	 *
	 * @param {Zarafa.mail.MailContextModel} model Context Model object that will be used
	 * to {@link Zarafa.mail.MailContextModel#createRecord create} the E-Mail.
	 * @param {Zarafa.addressbook.AddressBookRecord} contacts One or more contact records.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateMailContentForContacts: function (model, contacts, config) {
		var emailRecord = container.getContextByName("mail").getModel().createRecord();

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['zarafa.plugins.files.attachdialog'], undefined, {
			title      : String.format(_('Add attachment from {0}'), container.getSettingsModel().get('zarafa/v1/plugins/files/button_name')),
			emailrecord: emailRecord,
			manager    : Ext.WindowMgr
		});

		var recipientStore = emailRecord.getRecipientStore();
		var tasks = [];

		contacts = Ext.isArray(contacts) ? contacts : [contacts];
		for (var i = 0, len = contacts.length; i < len; i++) {
			var contact = contacts[i];

			if (contact.isOpened()) {

				var recipient = contact.convertToRecipient(Zarafa.core.mapi.RecipientType.MAPI_TO, true);
				recipientStore.add(recipient);
			} else {

				tasks.push({

					fn: function () {

						var contactRecord = contact;
						return function (panel, record, task, callback) {
							var fn = function (store, record) {
								if (record === contactRecord) {
									store.un('open', fn, task);
									var recipient = contactRecord.convertToRecipient(Zarafa.core.mapi.RecipientType.MAPI_TO, true);
									recipientStore.add(recipient);
									callback();
								}
							};

							contactRecord.getStore().on('open', fn, task);
							contactRecord.open();
						}

					}()
				});
			}
		}

		config = Ext.applyIf(config || {}, {
			recordComponentPluginConfig: {
				loadTasks: tasks
			}
		});

		Zarafa.core.data.UIFactory.openCreateRecord(emailRecord, config);
	},

	/**
	 * Create a new item...
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model Context Model.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 */
	openCreateFilesContent: function (model, config) {
		var record = model.createRecord();
		Zarafa.core.data.UIFactory.openCreateRecord(record, config);
	},

	/**
	 * Function is used to delete files or folders.
	 *
	 * @param {Array} records
	 */
	deleteRecords: function (records) {
		var allowDelete = true;
		var folderCount = 0;
		var fileCount = 0;
		var firstFileName, firstFolderName;

		Ext.each(records, function (record) {
			if (record.get('folder_id') === (container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/") || record.get('filename') === "..") {
				allowDelete = false;
			}
			if (record.get('object_type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
				folderCount += 1;
				if (!firstFolderName) {
					firstFolderName = record.get('filename');
				}
			} else if (record.get('object_type') === Zarafa.plugins.files.data.FileTypes.FILE) {
				fileCount += 1;
				if (!firstFileName) {
					firstFileName = record.get('filename');
				}
			}
		}, this);

		var askOnDelete = container.getSettingsModel().get('zarafa/v1/contexts/files/ask_before_delete');

		if (allowDelete) {
			if (askOnDelete) {
				Ext.MessageBox.confirm(_('Confirm deletion'),
					this.createDeletionMessage(fileCount, firstFileName, folderCount, firstFolderName),
					function (button) {
						if (button === 'yes') {
							this.doDelete(records);
						}
					}, this);
			} else {
				this.doDelete(records);
			}
		}
	},

	/**
	 * Create a proper message for the confirm deletion message box
	 * @param {Integer} fileCount number of files to delete
	 * @param {String} fileName name of the file to be deleted
	 * @param {Integer} folderCount number of folders to delete
	 * @param {String} folderName name of the folder to be deleted
	 * @return {String} the string to be shown in the delete confirmation dialog
	 */
	createDeletionMessage: function(fileCount, fileName, folderCount, folderName) {
		//single file
		if (fileCount === 1 && folderCount === 0) {
			return String.format(_('Are you sure you want to delete {0}?'), fileName);
		}
		//single folder
		if (fileCount === 0 && folderCount === 1) {
			 return String.format(_('Are you sure you want to delete {0} and all of its contents?'), folderName);
		}
		//multiple files
		if (fileCount >= 1 && folderCount === 0) {
			 return String.format(_('Are you sure you want to delete {0} files?'), fileCount);
		}
		//multiple folders
		if (fileCount === 0 && folderCount >= 1) {
			return String.format(_('Are you sure want to delete {0} folders and all of their contents?'), folderCount);
		}
		//multiple files and folders
		if (fileCount !== 0 && folderCount !== 0) {
			return _('Are you sure want to delete the selected items and all of their contents?');
		}
	},

	/**
	 * Delete the selected files.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord[]} records The records that must be deleted.
	 * @private
	 */
	doDelete: function (records)
	{
		if (!Array.isArray(records)) {
			records = [records];
		}

		var store = records[0].getStore();
		store.remove(records);
		store.save(records);
	},

	/**
	 * Create the sharing dialog.
	 *
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 * @param {Array} records Selected filerecords
	 */
	createShareDialog: function (records, config) {
		config = Ext.applyIf(config || {}, {
			modal  : true,
			records: records
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.sharedialog'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, config);
	},

	/**
	 * Create a new Folder in {@link Zarafa.core.data.IPMRecord node}.
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model Context Model.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The destination folder in which the new folder will be created.
	 */
	createFolder: function (model, config, folder) {

		config = Ext.applyIf(config || {}, {
			accountFilter: folder.getFilesStore().get('entryid'),
			parentFolder: folder,
			model: model,
			manager: Ext.WindowMgr
		});

		var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER, {
			"object_type": Zarafa.plugins.files.data.FileTypes.FOLDER
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfolderdialog'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, record, config);
	},

	/**
	 * Create a new Folder in {@link Zarafa.core.data.IPMRecord node}.
	 *
	 * @param {Zarafa.plugins.files.context.FilesContextModel} model Context Model.
	 * @param {Object} config (optional) Configuration object used to create
	 * the Content Panel.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The destination folder in which the new folder will be created.
	 */
	createFile: function (model, config, folder, button, filetype) {
		config = Ext.applyIf(config || {}, {
			accountFilter: folder.getFilesStore().get('entryid'),
			parentFolder: folder,
			model: model,
			manager: Ext.WindowMgr,
			button: button,
			filetype: filetype
		});

		var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.createfiledialog'];
		Zarafa.core.data.UIFactory.openLayerComponent(componentType, null, config);
	},

	/**
	 * Callback for the {@link Zarafa.plugins.files.data.FilesRecordStore#load} event.
	 * This function will refresh the view of the main panel.
	 */
	doRefreshIconView: function () {
		if (Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentView() === Zarafa.plugins.files.data.Views.ICON) {
			Zarafa.plugins.files.data.ComponentBox.getItemsView().refresh();
		}
	},

	/**
	 * Move specified records to the new folder.
	 *
	 * @param {Array} records array of records
	 * @param {Object} destination record or treenode
	 * @param {Boolean} overwrite boolean flag - if true, existing dst records will be overwritten, default: true
	 */
	moveRecords: function (records, destination, options)
	{
		if (!Ext.isDefined(destination.data)) {
			var parent = destination.parentNode;
			destination = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.ZARAFA_FILES, {
				id            : destination.id,
				entryid       : destination.id,
				parent_entryid: Ext.isDefined(parent) ? parent.id : "/"
			}, destination.id);
		}

		var isExists = this.isRecordExistsAlready(options.hierarchyStore, records, destination);
		if (isExists) {
			// TODO: Show conflicted folder/file names.
			Ext.MessageBox.confirm(
				_('Confirm overwrite'),
				_('File already exists. Do you want to overwrite it?'),
				function (button) {
					this.doMoveRecords(records, destination, button);
				},
				this
			);
		} else {
			this.doMoveRecords(records, destination);
		}
	},

	/**
	 * Helper function which used to check folder is already exists in destination folder.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} hierarchyStore the hierarchy store.
	 * @param {Ext.data.Record} records The records which needs to move.
	 * @param {Ext.data.Record} destination The destination folder where records going to move.
	 * @returns {boolean}
	 */
	isRecordExistsAlready : function(hierarchyStore, records, destination)
	{
		if (!Array.isArray(records)) {
			records = [records];
		}

		var folderNames = records.map(function (record) {
			return record.get('display_name')
		});

		var folder = hierarchyStore.getFolder(destination.id);
		var childFolder = folder.getChildren();
		return childFolder.some(function (folder) {
			return folderNames.indexOf(folder.get('display_name')) !== -1;
		},this);
	},

	/**
	 * This function will actually move the given files to a new destination.
	 *
	 * @param {Array} files Array of records
	 * @param {Object} destination Destination folder
	 * @param {String} overwrite It will be "yes" to overwrite files, false otherwise.
	 * @private
	 */
	doMoveRecords: function (files, destination, overwrite) {
		var store = files[0].getStore();
		if(!Ext.isDefined(overwrite) || overwrite === 'yes' ) {
			Ext.each(files, function (record) {
				record.beginEdit();
				record.moveTo(destination);
				record.set("deleted", true);
				record.addMessageAction('overwrite', Ext.isDefined(overwrite) ? overwrite : 'no');
				record.addMessageAction('isFolder', record.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER);
				record.dirty = true;
				record.endEdit();
				record.save();
			});
			store.filter("deleted", false);
		} else {
			store.reload();
		}

		if (Zarafa.plugins.files.data.ComponentBox.getContext().getCurrentView() === Zarafa.plugins.files.data.Views.ICON) {
			Zarafa.plugins.files.data.ComponentBox.getItemsView().refresh();
		}
	},

	/**
	 * Open a rename dialog and rename the item
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 */
	openRenameDialog: function (record) {
		Ext.MessageBox.prompt(
			_('Rename'),
			_('Please enter a new name'),
			this.doCheckRenameDuplicate.createDelegate(this, [record], true),
			this,
			false,
			record.get('filename'));
	},

	/**
	 * Check if the new name already exists
	 *
	 * @param {String} button The value of the button
	 * @param {String} text Inputfield value, new name
	 * @param {Object} options Unused
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 * @private
	 */
	doCheckRenameDuplicate: function (button, text, options, record) {
		if (button === "ok") {
			// Show alert message box if new name of file is empty.
			if (Ext.isEmpty(text)) {
				Ext.Msg.alert(
					_('Rename record'),
					_('A file name should not be empty.'),
					function (button) {
						this.openRenameDialog(record);
					},
					this
				);
				return;
			}

			var store = record.getStore();
			var index = 0;
			if (store instanceof Zarafa.plugins.files.data.FilesFoldersSubStore) {
				var parentFolder = record.getParentFolder();
				index = parentFolder.getChildren().findIndex(function(item) {
					return item.get('filename') === text;
				});
			} else {
				index = store.findExact("filename", text);
			}

			if (index > -1) {
				Ext.Msg.alert(
					_('Rename item'),
					String.format(_('A file named \"{0}\" already exists. Please enter a different name.'), text),
					function () {
						this.openRenameDialog(record);
					},
					this
				);
			} else {
				this.doRename(text, record);
			}
		}
	},

	/**
	 * Rename a record on the server
	 *
	 * @param {String} text Inputfield value, new name
	 * @param {Zarafa.plugins.files.data.FilesRecord} record
	 * @private
	 */
	doRename: function (text, record)
	{
		var path = record.get('path');
		record.addMessageAction("source_folder_id", record.get('folder_id'));
		record.addMessageAction("isFolder", record.isFolder());
		record.beginEdit();
		record.set("display_name", text);
		record.set("filename", text);
		record.set("text", text);
		record.set('folder_id', path + text + "/");
		record.endEdit();
		record.save()
	},

	/**
	 * Download the selected items from files.
	 *
	 * @param {Array} records An array of ids
	 */
	downloadItem: function (records)
	{
		if (!Array.isArray(records)) {
			records = [records];
		}

		var downloadFrame = Ext.getBody().createChild({
			tag: 'iframe',
			cls: 'x-hidden'
		});

		if (records.length == 1) {
			var url = this.getDownloadLink(records[0], false);
			downloadFrame.dom.contentWindow.location = url;
		} else if (records.length > 1) {
			var url = this.getDownloadLinkForMultipleFiles(records, false);
			downloadFrame.dom.contentWindow.location = url;
		}
		container.getNotifier().notify('info.files', _('Downloading'), _('Download started... please wait!'));
	},

	/**
	 * Upload the given files to the files backend. This method will use the async XMLHttpRequest to
	 * upload the files to the server.
	 *
	 * @param {Array} files An array of files
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store which
	 * contains {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 * @param {string} destination The destination folder id where file gets uploaded.
	 */
	uploadAsyncItems: function (files, store, destination) {
		if (!Ext.isDefined(destination)) {
			destination = store.folderId;
		}

		var fileStore = store;
		// build the ids:
		var ids = [];
		var fileTooLarge = false;

		Ext.each(files, function (file) {
			if (file.size > Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) {
				fileTooLarge = true;
			}
			var id = destination + file.name;
			ids.push({
				id      : id,
				isFolder: false
			});
		});

		if (fileTooLarge) {
			Zarafa.common.dialogs.MessageBox.show({
				title  : _('Upload'),
				msg    : String.format(_('At least one file is too large! Maximum allowed filesize: {0}.'), Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize())),
				icon   : Zarafa.common.dialogs.MessageBox.ERROR,
				buttons: Zarafa.common.dialogs.MessageBox.OK
			});
		} else {
			// check for duplicates
			container.getRequest().singleRequest(
				'filesbrowsermodule',
				'checkifexists',
				{
					records    : ids,
					destination: destination
				},

				new Zarafa.core.data.AbstractResponseHandler({
					doCheckifexists:  this.checkForExistingFilesDone.createDelegate(this, [files, destination, fileStore, this.doAsyncUpload], true)
				})
			);
		}
	},

	/**
	 * Actually uploads all the files to the server.
	 *
	 * @param {Zarafa.common.dialogs.MessageBox.addCustomButtons} button
	 * @param {Array} files An array of files
	 * @param {string} destination The destination folder id where file gets uploaded.
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store which contains {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 */
	doAsyncUpload: function (button, files, destination, store) {
		if (button === "overwrite" || button === "keepboth") {
			var componentType = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.uploadstatusdialog'];
			Zarafa.core.data.UIFactory.openLayerComponent(componentType, undefined, {
				files : files,
				destination : destination,
				keepBoth : button === "keepboth",
				manager : Ext.WindowMgr,
				store : store,
				callbackAllDone : this.uploadDone.bind(this)
			});
		}
	},

	/**
	 * Callback if the upload has completed.
	 *
	 * @param {Array} files An array of files
	 * @param {string} destination The destination folder id where file gets uploaded.
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store which contains {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 */
	uploadDone: function (files, destination, store)
	{
		this.updateCache(destination);
		if (store.folderId === destination) {
			store.reload();
		}
	},

	/**
	 * This function is called after the {@Zarafa.plugins.files.data.FilesStore} has loaded the target folder.
	 * It will check if one of the selected files already exists in the store. If there is a duplicate file
	 * a warning will be shown.
	 *
	 * @param {Object} response
	 * @param {File[]} files The files that should be uploaded
	 * @param {string} destination record id
 	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store which contains
	 * {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 * @param {Function} callback function which triggers {@link #doAsyncUpload} function.
	 * @private
	 */
	checkForExistingFilesDone: function (response, files, destination, store, callback) {
		if (response.duplicate === true) {
			Zarafa.common.dialogs.MessageBox.addCustomButtons({
				title : _('Confirm overwrite'),
				icon: Ext.MessageBox.QUESTION,
				msg : _('File already exists. Do you want to overwrite it?'),
				fn : callback.createDelegate(this, [files, destination, store], true),
				customButton : [{
					text :  _('Keep both'),
					name : 'keepboth'
				}, {
					text :  _('Overwrite'),
					name : 'overwrite'
				}, {
					text :  _('Cancel'),
					name : 'cancel'
				}],
				scope : this
			});
		} else {
			callback.createDelegate(this, ["overwrite", files, destination, store])();
		}
	},

	/**
	 * Returns a download link for the client.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord} record a file record
	 * @param {Boolean} inline (optional)
	 * @return {String}
	 */
	getDownloadLink: function (record, inline) {
		return (Ext.isDefined(inline) && inline == false) ? record.getAttachmentUrl() : record.getInlineImageUrl();
	},

	/**
	 * Returns a download link for the client to download multiple items.
	 *
	 * @param {Array} records a array of {Zarafa.plugins.files.data.FilesRecord}
	 * @return {String}
	 */
	getDownloadLinkForMultipleFiles: function (records) {
		var link = "";

		var url = document.URL;
		link = url.substring(0, url.lastIndexOf('/') + 1);

		link += "index.php?load=custom&name=download_file";
		Ext.each(records, function (record, index) {
			link = Ext.urlAppend(link, "ids[" + index + "]=" + encodeURIComponent(record.get("folder_id")));
		});

		link = Ext.urlAppend(link, "inline=false");

		return link;
	},

	/**
	 * This will display a messagebox with warning icons to the user.
	 *
	 * @param {String} errorMessage The error message to display
	 */
	msgWarning: function (errorMessage) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : _('Warning'),
			msg    : errorMessage,
			icon   : Zarafa.common.dialogs.MessageBox.WARNING,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});
	},

	/**
	 * Event handler called when the "use Zarafa credentials" checkbox has been modified
	 *
	 * @param {Ext.form.CheckBox} checkbox Checkbox element from which the event originated
	 * @param {Boolean} checked State of the checkbox
	 * @private
	 */
	onCheckCredentials: function (checkbox, checked) {
		if (checked) {
			this.usernameField.hide();
			this.usernameField.setValue("");
			this.usernameField.label.hide();
			this.usernameField.allowBlank = true;
                        this.usernameField.validate();
			this.passwordField.hide();
			this.passwordField.setValue("");
			this.passwordField.label.hide();
			this.passwordField.allowBlank = true;
                        this.passwordField.validate()
		} else {
			this.usernameField.show();
			this.usernameField.label.show();
			this.usernameField.allowBlank = false;
                        this.usernameField.validate();
			this.passwordField.show();
			this.passwordField.label.show();
			this.passwordField.allowBlank = false;
                        this.passwordField.validate()
		}
	},

	/**
	 * Event handler called when the "use ssl connection" checkbox has been modified.
	 * If checked and the server port is 80, switch it to 443, else if the port is unchecked
	 * and the port is not 80, change it to the default.
	 *
	 * @param {Ext.form.CheckBox} checkbox Checkbox element from which the event originated
	 * @param {Boolean} checked State of the checkbox
	 */
	onCheckSSL: function (checkbox, checked) {
		if (checked && this.portField.getValue() === "80") {
			this.portField.setValue("443");
		} else if (!checked && this.portField.getValue() === "443") {
			this.portField.setValue("80");
		}
	},

	/**
	 * Updates the cache for given record
	 *
	 * @param destination record id.
	 */
	updateCache: function (destination)
	{
		container.getRequest().singleRequest(
			'filesbrowsermodule',
			'updatecache',
			{
				id: destination
			}
		);
	},

	/**
	 * Open the folder. This will check if the user has rights to open
	 * the given folder, and will call {@link Zarafa.core.Container#selectFolder}
	 * if that is the case. Otherwise if this is a shared store, it will ask
	 * if the store can be closed.
	 *
	 * @param {Zarafa.plugins.files.FilesContextModel} model The {Zarafa.plugins.files.FilesContextModel FilesContextModel}.
	 * @param {String} entryId The entryId is id of folder which is going to open.
	 */
	openFolder : function(model, entryId)
	{
		var folder = model.getHierarchyStore().getFolder(entryId);
		if (Ext.isDefined(folder)) {
			container.selectFolder(folder);
		}
	},

	/**
	 * Function which open the {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel SaveToFilesContentPanel}
	 * in dialog.
	 *
	 * @param {Zarafa.plugins.files.FilesContextModel} model The model is {@link Zarafa.plugins.files.FilesContextModel FilesContextModel}.
	 * @param {Object} config The configuration object which used to open {@link Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel SaveToFilesContentPanel}.
	 */
	openSaveToFilesDialog : function (model, config)
	{
		config = Ext.applyIf(config||{}, {
			model : model,
			modal : true
		});

		var component = Zarafa.core.data.SharedComponentType['common.dialog.attachments.savetofiles'];
		Zarafa.core.data.UIFactory.openLayerComponent(component, undefined, config);
	},

	/**
	 * Event handler for the click event of the tabbar buttons. It will
	 * open the tab if it already exists, or create it otherwise.
	 * @param {Zarafa.core.ui.MainTab} btn The button in the
	 * {@link Zarafa.core.ui.MainTabBar main tabbar}
	 */
	openTab: function(record)
	{
		var tabIndex;
		var url = record.get('folder_id');
		var displayName = record.get('display_name');
		Ext.each(container.getTabPanel().items.items, function(item, index){
			if (displayName && item.url === url && item.title === displayName) {
				tabIndex = index;
			}
		});

		if ( Ext.isDefined(tabIndex) ){
			// open the existing tab
			var mainContentTabPanel = container.getMainPanel().contentPanel;
			mainContentTabPanel.activate(tabIndex);
		} else {
			// Create a new tab
			var component = Zarafa.core.data.SharedComponentType['plugins.files.onlyofficepanel'];
			Zarafa.core.data.UIFactory.openLayerComponent(
				component,
				record,
				{
					url: url,
					tabId: 'onlyoffice',
					title: displayName || 'office',
					tabOrder: 10
				}
			);
		}
	}
};
/**
 * @class Ext.tree.AsyncTreeNode
 * @overrides Ext.tree.AsyncTreeNode
 *
 * This class overrides Ext.tree.AsyncTreeNode. It adds the functionality to silently load a node without expanding it.
 */
Ext.override(Ext.tree.AsyncTreeNode, {

	/**
	 * This functions loads a node without expanding it.
	 *
	 * @param deep
	 * @param anim
	 * @param callback
	 */
	quietLoad: function(deep, anim, callback) {
		deep = 0;
		if(this.loading){ // if an async load is already running, waiting til it's done
			var timer;
			var f = function(){
				if(!this.loading){ // done loading
					clearInterval(timer);
					//this.expand(deep, anim, callback);
				}
			}.createDelegate(this);
			timer = setInterval(f, 200);
			return;
		}

		if(!this.loaded){
			if(this.fireEvent("beforeload", this) === false){
				return;
			}
			this.loading = true;
			this.ui.beforeLoad(this);
			var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
			if(loader){
				// add a class to hide the loading icon
				this.setCls("x-treenode-load-silent");
				loader.load(this, this.quietLoadComplete.createDelegate(this, [deep, anim, callback]));
				return;
			}
		}
	},

	/**
	 * Callback for the tree loader. It is called on the "load" event.
	 * @param deep
	 * @param anim
	 * @param callback
	 */
	quietLoadComplete : function(deep, anim, callback){
		// remove the silent class again
		this.setCls("");

		if (this.childNodes.length === 0) {
			this.leaf = true;
		}
		this.loading = false;
		this.loaded = true;
		this.ui.afterLoad(this);
		this.fireEvent("load", this);
		this.ui.expand();
	},

	/**
	 * Set the leaf flag for the node
	 * @param {Boolean} value
	 */
	setLeaf: function(value){
		this.leaf = value;
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ComponentBox
 * @singleton
 *
 * The global component box which holds all aliases to important components.
 */
Zarafa.plugins.files.data.ComponentBox = Ext.extend(Object, {

	/**
	 * Get the files context.
	 *
	 * @return {Zarafa.plugins.files.FilesContext}
	 */
	getContext: function () {
		return container.getContextByName("filescontext");
	},

	/**
	 * Get the main panel.
	 *
	 * @return {Zarafa.core.ui.MainViewport}
	 */
	getMainPanel: function () {
		try {
			return container.getContentPanel();
		} catch (e) {
			return container.getTabPanel().get(0).getActiveItem();
		}
	},

	/**
	 * Get the preview panel.
	 *
	 * @return {Zarafa.plugins.files.ui.FilesPreviewPanel}
	 */
	getPreviewPanel: function () {
		return this.getMainPanel().filesPreview;
	},

	/**
	 * Get the tabpanel.
	 *
	 * @return {Zarafa.core.ui.ContextContainer}
	 */
	getTabPanel: function () {
		return container.getTabPanel();
	},

	/**
	 * Get the files viewpanel.
	 *
	 * @return {Zarafa.core.ui.SwitchViewContentContainer}
	 */
	getViewPanel: function () {
		return this.getMainPanel().viewPanel;
	},

	/**
	 * Get the files gridpanel or iconviewpanel.
	 *
	 * @return {Zarafa.plugins.files.ui.FilesRecordGridView} or {Zarafa.plugins.files.ui.FilesRecordIconView}
	 */
	getItemsView: function () {
		return this.getViewPanel().getActiveItem();
	}
});

Zarafa.plugins.files.data.ComponentBox = new Zarafa.plugins.files.data.ComponentBox();Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.DataModes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different data modes of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.DataModes = Zarafa.core.Enum.create({

	/**
	 * DataMode: all
	 *
	 * @property
	 * @type Number
	 */
	ALL: 0
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FileTypes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different file types of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.FileTypes = Zarafa.core.Enum.create({

	/**
	 * Filetype: folder
	 *
	 * @property
	 * @type Number
	 */
	FOLDER: 0,

	/**
	 * Filetype: file
	 *
	 * @property
	 * @type Number
	 */
	FILE: 1
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesBackendRecordFields
 *
 * These fields will be available in all 'IPM.FilesBackend' type messages.
 */
Zarafa.plugins.files.data.FilesBackendRecordFields = [
	{name: 'id'},
	{name: 'name'},
	{name: 'backend', mapping: 'name'},
	{name: 'displayName'},
	{name: 'message_class'}
];

Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.FilesBackend', Zarafa.plugins.files.data.FilesBackendRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM.FilesBackend', Zarafa.core.data.IPMRecord);

Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.BackendStore
 * @extends Zarafa.core.data.ListModuleStore
 * @xtype filesplugin.backendstore
 *
 * The BackendStore class provides a way to connect the 'filesaccountmodule' in the server back-end to an
 * 'Account Type' combo box object which belongs to {@link Zarafa.plugins.files.settings.ui.AccountEditPanel AccountEditPanel}.
 * It provides a means to retrieve supported backend listings asynchronously.
 */
Zarafa.plugins.files.data.BackendStore = Ext.extend(Zarafa.core.data.ListModuleStore, {

	/**
	 * @constructor
	 * @param {Object} config configuration params that should be used to create instance of this store.
	 */
	constructor: function (config)
	{
		config = config || {};

		var recordType = Zarafa.core.data.RecordFactory.getRecordClassByMessageClass('IPM.FilesBackend');

		Ext.applyIf(config, {
			preferredMessageClass: 'IPM.FilesBackend',
			autoLoad: {
				params: {
					list_backend: true
				}
			},
			reader: new Zarafa.core.data.JsonReader({
				id: 'name',
				idProperty: 'name'
			}, recordType),
			proxy: new Zarafa.core.data.IPMProxy({
				listModuleName: 'filesaccountmodule',
				itemModuleName: 'filesaccountmodule'
			})
		});

		Zarafa.plugins.files.data.BackendStore.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.backendstore', Zarafa.plugins.files.data.BackendStore);Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesStoreRecord
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.core.data.IPFRecord IPFRecord} object.
 */
Zarafa.plugins.files.data.FilesFolderRecordFields = [
	{name: 'path'},
	{name: 'folder_id'},
	// TODO: try to remove id property.
	{name: 'id', mapping: 'entryid'},
	// Fixme: change text property to display_name
	{name: 'text'},
	{name: 'object_type'},
	{name: 'entryid'},
	{name: 'parent_entryid'},
	{name: 'store_entryid'},
	{name: 'filename'},
	{name: 'icon_index'},
	{name: 'display_name'},
	{name: 'lastmodified'},
	{name: 'message_size'},
	{name: 'has_subfolder', defaultValue: false}
];

Zarafa.plugins.files.data.FilesFolderRecord = Ext.extend(Zarafa.core.data.IPFRecord, {

	// TODO: Try to remove id property from baseIdProperties list.
	/**
	 * The base array of ID properties which is copied to the {@link #idProperties}
	 * when the record is being created.
	 * @property
	 * @type Array
	 * @private
	 */
	baseIdProperties : [ 'id', 'folder_id', 'entryid', 'store_entryid', 'parent_entryid' ],

	/**
	 * @constructor
	 * @param {Object} data The data which must be applied to this record
	 * @param {Object} id The unique id for this record
	 * @param {Zarafa.core.data.RecordDefinition} definition The record definition used to
	 * construct this record
	 */
	constructor : function(data, id, definition)
	{
		if (!Ext.isDefined(definition)) {
			definition = Zarafa.core.data.RecordFactory.getRecordDefinitionByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER);
		}

		Zarafa.plugins.files.data.FilesFolderRecord.superclass.constructor.call(this, data, id, definition);
	},

	/**
	 * Usually called by the {@link Ext.data.Store} which owns the Record.
	 * Commits all changes made to the Record since either creation, or the last commit operation.
	 * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
	 * to have their code notified of commit operations.</p>
	 * @param {Boolean} silent (optional) True to skip notification of the owning
	 * store of the change (defaults to false)
	 */
	commit : function()
	{
		// Check if the parent folder still is correct.
		if (this.cacheParentFolder) {
			if (!Zarafa.core.EntryId.compareEntryIds(this.get('parent_entryid'), this.cacheParentFolder.get('entryid'))) {
				delete this.cacheParentFolder;
			}
		}
		Zarafa.plugins.files.data.FilesFolderRecord.superclass.commit.apply(this, arguments);
	},

	/**
	 * Helper function to obtain the fully qualified display name. For normal folders, this will return
	 * the same value as {@link #getDisplayName},
	 * @return {String} name of the folder
	 */
	getFullyQualifiedDisplayName : function(){
		return this.getDisplayName();
	},

	/**
	 * Helper function to get display name of {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord}.
	 * it will get it from display_name property.
	 * @return {String} name of the folder.
	 */
	getDisplayName: function () {
		return this.get('display_name');
	},

	/**
	 * @return {Boolean} true if the folder is the subtree folder of its store else false.
	 */
	isSubTreeFolder : function()
	{
		var FilesFolderStore = this.getFilesStore();
		if (FilesFolderStore) {
			return Zarafa.core.EntryId.compareEntryIds(this.get('entryid'), FilesFolderStore.get('subtree_id'));
		}

		return false;
	},

	/**
	 * Returns a {@link Zarafa.plugins.files.data.FilesStoreRecord FilesStoreRecord} for the record
	 * @return {Zarafa.plugins.files.data.FilesStoreRecord} FilesStoreRecord or false if
	 * {@link Zarafa.plugins.files.data.FilesFoldersSubStore FilesFoldersSubStore} is not defined.
	 */
	getFilesStore: function ()
	{
		var store = this.getStore();
		if (store && store instanceof Zarafa.plugins.files.data.FilesFoldersSubStore) {
			return store.getParentRecord();
		}

		return store;
	},

	/**
	 * Returns all child folders of given folder.
	 *
	 * @return {Array} array of child {@link Zarafa.plugins.files.data.FilesFolderRecord folders}
	 */
	getChildren : function ()
	{
		var rs = [];

		this.getFilesFolderStore().each(function(record) {
			if (this === record.getParentFolder()) {
				rs.push(record);
			}
		}, this);

		return rs;
	},

	/**
	 * Function will return parent {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord}
	 * of this {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord}.
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} parent {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord} or
	 * false if parent folder doesn't exist.
	 */
	getParentFolder : function() {
		if (!this.cacheParentFolder) {
			var path = this.get('parent_entryid');
			if (!this.isSubTreeFolder() && !Ext.isEmpty(path)) {
				this.cacheParentFolder = this.getFilesFolderStore().getById(path);
			}

			// Guarantee that the parent folder knows it has children...
			// Don't use record::set() as we don't want to trigger updates.
			if (this.cacheParentFolder && this.get('object_type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
				this.cacheParentFolder.data.has_subfolder = this.get("object_type") === Zarafa.plugins.files.data.FileTypes.FOLDER;
			}
		}

		return this.cacheParentFolder;
	},

	/**
	 * Returns the {@link Zarafa.hierarchy.data.IPFSubStore} which contains all folders
	 * of the store in which the current store is located. This is safer then using {@link #getStore},
	 * as this function will use {@link #getMAPIStore} to obtain the parent {@link Zarafa.hierarchy.data.MAPIStoreRecord}
	 * and is thus safe when the current record is located in the {@link Zarafa.core.data.ShadowStore}.
	 * @return {Zarafa.hierarchy.data.IPFSubStore} The substore containing all folders
	 */
	getFilesFolderStore : function ()
	{
		var store = this.getFilesStore();
		if (store) {
			return store.getSubStore('folders');
		}

		return false;
	},

	/**
	 * Copy the {@link Zarafa.core.data.MAPIRecord Record} to a new instance
	 * @param {String} newId (optional) A new Record id, defaults to the id of the record being copied. See id.
	 * @return {Zarafa.core.data.MAPIRecord} The copy of the record.
	 */
	copy : function(newId)
	{
		var copy = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER, this.data, newId || this.id);

		copy.idProperties = this.idProperties.clone();
		copy.phantom = this.phantom;

		return copy.applyData(this, true);
	},

	/**
	 * Function used to get the icon based on the folder.
	 * @return {string} return the icon which used for hierarchy node.
	 */
	getIcon : function ()
	{
		if (this.isSubTreeFolder()) {
			return "icon_16_logo_" + this.getFilesStore().getBackend();
		}

		if(this.isFolder()) {
			return "icon_folder_note";
		}

		return Zarafa.plugins.files.data.Utils.File.getIconClass(this.get('display_name'), "16");
	},

	/**
	 * Check selected record is folder record or not.
	 *
	 * @return {boolean} return true if selected record is
	 * folder record else false.
	 */
	isFolder : function ()
	{
		return this.get('object_type') === Zarafa.plugins.files.data.FileTypes.FOLDER;
	},

	/**
	 * @return {boolean} true if folder is home folder. home folder is dummy and hidden folder
	 * in {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore} else false.
	 */
	isHomeFolder : function()
	{
		return this.get("entryid") === "#R#";
	},

	/**
	 * @returns {Boolean} true if the folder is the favorites folder else false.
	 */
	isFavoritesFolder : Ext.emptyFn,

	/**
	 * @return {Boolean} true if folder is IPM_Subtree of own store
	 */
	isOwnRoot : Ext.emptyFn,

	/**
	 * Helper function which used to check user is trying to delete the parent folder of currently selected folder or not.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The folder which can be either {@link #getDefaultFolder default folder} or
	 * parent folder of currently deleted folder
	 * @returns {boolean} return true if deleted folder is parent folder of currently selected folder in hierarchy else false.
	 */
	isParentFolderOfSelectedFolder: function(folder)
	{
		if (!Ext.isDefined(folder)) {
			folder = this.getDefaultFolder();
		}

		if (this.get('folder_id') === folder.get('folder_id')) {
			return true;
		} else if (folder.isSubTreeFolder()){
			return false;
		} else {
			return this.isParentFolderOfSelectedFolder(folder.getParentFolder());
		}
		return false;
	}
});

Zarafa.core.data.RecordCustomObjectType.addProperty('FILES_FOLDER');
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER, Zarafa.plugins.files.data.FilesFolderRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER, Zarafa.plugins.files.data.FilesFolderRecord);
Zarafa.core.data.RecordFactory.addListenerToObjectType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER, 'createphantom', function(record) {
	// Phantom records must always be marked as opened (they contain the full set of data)
	record.afterOpen();
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesFolderResponseHandler
 * @extends Zarafa.core.data.ProxyResponseHandler
 *
 * A Simple implementation for a {@link Zarafa.plugins.files.data.FilesFolderResponseHandler ResponseHandler}.
 * This one can only be used by {@link Ext.data.DataProxy proxies} which wish to handle a Response
 * to their Request.
 *
 * This implementation limits itself to firing an {@link Ext.data.DataProxy#exception exception}
 * on error, and calling a callback function when all processing has been completed.
 */
Zarafa.plugins.files.data.FilesFolderResponseHandler = Ext.extend(Zarafa.core.data.ProxyResponseHandler, {

	/**
	 * Handles the list response. Gathers the stores from the response data, converts each entry
	 * into a {@link Zarafa.core.MAPIStore MAPIStore} and pushes them into the collectedItems.
	 * @param {Object} response The response object belonging to the given command.
	 * @return {Boolean} False when action could not be handled successfully. This will
	 * not cancel the transaction itself, but rather causes the 'success' argument for the
	 * {@link #done} function to be false.
	 */
	doList: function(response)
	{
		this.receivedRecords = this.readRecordsFromResponse(response, 'item');
	},

	/**
	 * Handles the updatelist response. Gathers the stores from the response data, converts each entry
	 * into a {@link Zarafa.core.MAPIStore MAPIStore} and pushes them into the collectedItems.
	 *
	 * @param {Object} response The response object belonging to the given command.
	 * @return {Boolean} False when action could not be handled successfully. This will
	 * not cancel the transaction itself, but rather causes the 'success' argument for the
	 * {@link #done} function to be false.
	 */
	doUpdatelist: function(response)
	{
		this.receivedRecords = this.readRecordsFromResponse(response, 'item');
	},

	/**
	 * Handles the 'update' response. This will check if the item in the response is
	 * inside the {@link #sendRecords} and will convert the Response data into the
	 * updated {@link Ext.data.Record record} using {@link #correlateRecordFromResponse},
	 * and pushes them into the {@link #receivedRecords records list}.
	 * @param {Object} response The response object belonging to the given command.
	 * @return {Boolean} False when action could not be handled successfully. This will
	 * not cancel the transaction itself, but rather causes the 'success' argument for the
	 * {@link #done} function to be false.
	 */
	doUpdate: function(response)
	{
		this.receivedRecords = this.receivedRecords.concat(this.correlateRecordFromResponse({ item: response }));
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesFoldersSubStore
 * @extends Zarafa.core.data.IPFStore
 */
Zarafa.plugins.files.data.FilesFoldersSubStore = Ext.extend(Zarafa.core.data.IPFStore, {

	/**
	 * The {@link Zarafa.core.data.MAPIRecord MAPIRecord} that is the parent of this store.
	 * @property
	 * @type Zarafa.core.data.MAPIRecord
	 */
	parentRecord: null,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function(config)
	{
		config = config || {};

		var recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER);

		Ext.applyIf(config, {
			proxy: new Zarafa.plugins.files.data.FilesHierarchyProxy(),
			writer : new Zarafa.core.data.JsonWriter(),
			reader : new Zarafa.core.data.JsonReader({
				dynamicRecord : false,
			}, recordType)
		});

		Zarafa.plugins.files.data.FilesFoldersSubStore.superclass.constructor.call(this, config);
	},

	/**
	 * Called when member is added on store. Should not be used directly.
	 * It's called by Store#add automatically
	 * @FIXME now IPFSubStore is not child of NoSyncStore,
	 * so we don't need to mark parentrecord dirty, remove this.
	 * @param {Store} store
	 * @param {Ext.data.Record/Ext.data.Record[]} record
	 * @param {Number} index
	 * @private
	 */
	createRecords : function(store, record, index)
	{
		var parentRecord = this.getParentRecord();
		if(parentRecord) {
			// this will add parent record to modified array of associated store
			// and will mark the record as dirty.
			parentRecord.afterEdit();
		}

		Zarafa.plugins.files.data.FilesFoldersSubStore.superclass.createRecords.call(this, store, record, index);
	},

	/**
	 * Destroys a record or records. Should not be used directly.
	 * It's called by Store#remove automatically
	 * @param {Store} store
	 * @param {Ext.data.Record/Ext.data.Record[]} record
	 * @param {Number} index
	 * @private
	 */
	destroyRecord : function(store, record, index)
	{
		if(this.getParentRecord()){
			this.getParentRecord().markDirty();
		}
		Zarafa.plugins.files.data.FilesFoldersSubStore.superclass.destroyRecord.call(this, store, record, index);
	},

	/**
	 * Get the {@link Zarafa.core.data.IPFRecord IPFRecord} that is the parent of this store.
	 * @return {Zarafa.core.data.IPFRecord} The parent IPFRecord.
	 */
	getParentRecord : function()
	{
		return this.parentRecord;
	},

	/**
	 * Set the {@link Zarafa.core.data.IPFRecord IPFRecord} that is the parent of this store.
	 * @param {Zarafa.core.data.IPFRecord} record The parent IPFRecord.
	 */
	setParentRecord : function(record)
	{
		this.parentRecord = record;
	},

	/**
	 * Notification handler called by {@link #onNotify} when
	 * a {@link Zarafa.core.data.Notifications#objectDeleted objectDeleted}
	 * notification has been received.
	 *
	 * This will remove the folder from the store.
	 *
	 * @param {Zarafa.core.data.Notifications} action The notification action
	 * @param {Ext.data.Record/Array} records The record or records which have been affected by the notification.
	 * @param {Object} data The data which has been received from the PHP-side which must be applied
	 * to the given records.
	 * @param {Number} timestamp The {@link Date#getTime timestamp} on which the notification was received
	 * @param {Boolean} success The success status, True if the notification was successfully received.
	 * @private
	 */
	onNotifyObjectdeleted : function(action, records, data, timestamp, success)
	{
		if (!Array.isArray(records)) {
			records = [ records ];
		}

		// To prevent the Deletion to be saved back to the server again,
		// we mark all the records which we are about to delete as phantom.
		for (var i = 0, len = records.length; i < len; i++) {
			records[i].setEventPropagation(false);
			records[i].phantom = true;
		}

		this.remove(records);
	},

	/**
	 * Notification handler called by {@link #onNotify} when
	 * a {@link Zarafa.core.data.Notifications#objectModified objectModified}
	 * notification has been received.
	 *
	 * This will update the folder in the store.
	 *
	 * @param {Zarafa.core.data.Notifications} action The notification action
	 * @param {Ext.data.Record/Array} records The record or records which have been affected by the notification.
	 * @param {Object} data The data which has been received from the PHP-side which must be applied
	 * to the given records.
	 * @param {Number} timestamp The {@link Date#getTime timestamp} on which the notification was received
	 * @param {Boolean} success The success status, True if the notification was successfully received.
	 * @private
	 */
	onNotifyObjectmodified : function(action, records, data, timestamp, success)
	{
		if (!Array.isArray(records)) {
			records = [ records ];
		}

		// Temporarily disable event propagation, every store (which contains the provided records)
		// will receive this notification. So we have to disable event propagation to prevent
		// bouncing events around.
		for (var i = 0, len = records.length; i < len; i++) {
			var record = records[i];
			var singleData =  (Array.isArray(data)) ? data[i] : data;

			record.setEventPropagation(false);

			if (singleData instanceof Ext.data.Record) {
				// Merge the changes into the record without using the JSONReader.
				record.applyData(singleData);
			} else {
				// Simply merge the record using the JsonReader, this will cause a 'update' event to be fired with
				// a COMMIT action. Because it is a commit, this store will not mark the record as dirty.
				this.reader.update(record, singleData);
			}

			record.setEventPropagation(true);
		}
	},

	/**
	 * Checks whether any of the stores that were included in the parameters during the last load,
	 * matches the supplied entryid argument.
	 *
	 * @param {String|Array} entryidList Entryid of the folder
	 * @return {Boolean} Returns true when entryid matches, false when it does not.
	 */
	containsStoreInLastLoad: function(entryidList)
	{
		if (!Array.isArray(entryidList)) {
			entryidList = [ entryidList ];
		}

		if (this.parentRecord) {
			var entryId = this.parentRecord.get('store_entryid');

			for (var i = 0, len = entryidList.length; i < len; i++) {
				if (Zarafa.core.EntryId.compareStoreEntryIds(entryId, entryidList[i])) {
					return true;
				}
			}
		}

		return false;
	},

	/**
	 * Notification handler called by {@link #onNotify} when
	 * a {@link Zarafa.core.data.Notifications#objectCreated objectCreated}
	 * notification has been received.
	 *
	 * This will add the folder to the store.
	 *
	 * @param {Zarafa.core.data.Notifications} action The notification action
	 * @param {Ext.data.Record/Array} records The record or records which have been affected by the notification.
	 * @param {Object} data The data which has been received from the PHP-side which must be applied
	 * to the given records.
	 * @param {Number} timestamp The {@link Date#getTime timestamp} on which the notification was received
	 * @param {Boolean} success The success status, True if the notification was successfully received.
	 * @private
	 */
	onNotifyObjectcreated : function(action, records, data, timestamp, success)
	{
		if (!Array.isArray(data)) {
			data = [ data ];
		}

		if (data[0] instanceof Ext.data.Record) {
			this.loadRecords({ records : data }, { add : true });
		} else {
			this.loadData({ item : data }, true);
		}
	},

	/**
	 * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
	 * <br> Function just adds 'list' as actionType in options and calls parent {@link Zarafa.core.data.IPFStore#load} method.
	 * <br> Check documentation of {@link Ext.data.Store#load} for more information.
	 *
	 * @param {Object} options An object containing properties which control loading.
	 * @return {Boolean} true if super class load method will call successfully, false otherwise.
	 */
	load : function(options)
	{
		// FIXME: In grommunio Web sub store are not allow to send request to server but files is
		// a special case where we dont load all the folder of hierarchy in advance so here
		// we have to give privilege to substore so it can able to send a request to server
		// to load the child folders
		if (!Ext.isObject(options)) {
			options = {};
		}

		if (!Ext.isObject(options.params)) {
			options.params = {};
		}

		// Load should always cancel the previous actions.
		if (!Ext.isDefined(options.cancelPreviousRequest)) {
			options.cancelPreviousRequest = true;
		}

		// Reload the files hierarchy panel.
		if (options.reload) {
			options.params = {
				reload : options.reload
			};
		}

		if (options.folder) {
			var folder = options.folder;

			Ext.applyIf(options.params||{}, {
				entryid : folder.get('entryid'),
				store_entryid : folder.get('store_entryid'),
				parent_entryid : folder.get('parent_entryid'),
				folder_id: folder.get('folder_id')
			});
		}

		Ext.applyIf(options, {
			actionType : Zarafa.core.Actions['list']
		});

		return Zarafa.plugins.files.data.FilesFoldersSubStore.superclass.load.call(this, options);
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesHierarchyProxy
 * @extends Zarafa.core.data.MAPIProxy
 */
Zarafa.plugins.files.data.FilesHierarchyProxy = Ext.extend(Zarafa.core.data.MAPIProxy, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};
		Ext.applyIf(config, {
			listModuleName: 'hierarchylistmodule',
			itemModuleName: 'hierarchylistmodule'
		});

		Zarafa.plugins.files.data.FilesHierarchyProxy.superclass.constructor.call(this, config);
	},

	/**
	 * This will create a {@link Zarafa.core.data.ProxyResponseHandler ProxyResponseHandler} object
	 * which will be used by the {@link Zarafa.core.data.ResponseRouter ResponseRouter} when the
	 * response for the given request has returned.
	 *
	 * @param {String} modulename The modulename which is being accessed with this request
	 * @param {Zarafa.core.Actions} serverAction The action to perform on the server.
	 * @param {Ext.data.Api.action} action name of the action to perform.
	 * @param {Ext.data.Record[]} records list of records to operate on.
	 * @param {Object} parameters object containing user parameters such as range (pagination) information, sorting information, etc.
	 * @param {Ext.data.DataReader} reader data reader. Converts raw JavaScript objects (in our case) to instances of {@link Ext.data.Record}
	 * @param {Function} callback call back function to call when the request has finished successfully.
	 * @param {Object} scope scope for the call back function.
	 * @param {Object} args arguments object. This will be passed to the call back function on successful read.
	 * @return {Object} An instance of the {@link Zarafa.plugins.files.data.FilesFolderResponseHandler ProxyResponseHandler}
	 * which should be used for this request.
	 * @private
	 */
	getResponseHandlerForRequest: function (modulename, serverAction, action, records, parameters, reader, callback, scope, args)
	{
		return new Zarafa.plugins.files.data.FilesFolderResponseHandler({
			proxy: this,
			action: action,
			reader: reader,
			sendRecords: records,
			options: args,
			callback: callback,
			scope: scope
		});
	}
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesHierarchyStore
 * @extends Zarafa.core.data.IPFStore
 * @xtype filesplugin.hierarchystore
 *
 * {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore} holds
 * {@link Zarafa.plugins.files.data.FilesStoreRecord FilesStoreRecord} as records, which defines store information
 * of all opened stores.
 */
Zarafa.plugins.files.data.FilesHierarchyStore = Ext.extend(Zarafa.core.data.IPFStore, {
	/**
	 * @cfg {Boolean} stateful A flag which causes the store to create a {@link #state} object in which
	 * the state for the various {@link Zarafa.plugins.files.data.FilesFolderRecord folders} can be saved.
	 * This class will not automatically get/set the state, but assumes that other {@link Ext.Component components}
	 * or {@link Zarafa.core.data.StatefulObservable stateful objects} will {@link #getState get} or
	 * {@link #applyState apply} the state.
	 */
	stateful: true,

	/**
	 * When {@link #stateful} the interaction object in which the state can be saved or obtained from.
	 * Typically access to this object can be done through {@link #getState} and {@link #applyState}.
	 * @property
	 * @type Zarafa.hierarchy.data.HierarchyState
	 */
	state: undefined,

	/**
	 * True to indicate the {@link Zarafa.plugins.files.data.FilesHierarchyStore store} is currently loading.
	 * else false
	 * @property
	 * @type Boolean
	 */
	loading: false,

	/**
	 * @cfg {Zarafa.core.data.RecordCustomObjectType} customObjectType The custom object type
	 * which represents the {@link Ext.data.Record records} which should be created using
	 * {@link Zarafa.core.data.RecordFactory#createRecordObjectByCustomType}.
	 */
	customObjectType: Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = config || {};

		Ext.applyIf(config, {
			standalone: true,
			autoLoad: true,
			proxy: new Zarafa.plugins.files.data.FilesHierarchyProxy(),
			writer: new Zarafa.core.data.JsonWriter(),
			reader: new Zarafa.plugins.files.data.FilesJsonReader({
				customObjectType: Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE
			})
		});

		this.addEvents(
			/**
			 * @event addFolder
			 * Fires when a folder has been created.
			 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store
			 * @param {Zarafa.plugins.files.data.FilesStoreRecord} storeRecord
			 * @param {Zarafa.hierarchy.data.IPFRecord/Zarafa.hierarchy.data.IPFRecord[]}
			 * folder record that is added to the hierarchy store.
			 */
			'addFolder',
			/**
			 * @event removeFolder
			 * Fires when a folder has been removed.
			 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store
			 * @param {Zarafa.plugins.files.data.FilesStoreRecord} storeRecord
			 * @param {Zarafa.hierarchy.data.IPFRecord}
			 * folder which is removed from the hierarchy store.
			 */
			'removeFolder',
			/**
			 * @event updateFolder
			 * Fires when a folder has been updated
			 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store
			 * @param {Zarafa.plugins.files.data.FilesStoreRecord} storeRecord
			 * @param {Zarafa.hierarchy.data.IPFRecord} record folder which is updated in the hierarchy store.
			 * @param {String} operation The update operation being performed. Value may be one of
			 * {@link Ext.data.Record#EDIT}, {@link Ext.data.Record#REJECT}, {@link Ext.data.Record#COMMIT}.
			 */
			'updateFolder'
		);

		Zarafa.plugins.files.data.FilesHierarchyStore.superclass.constructor.call(this, config);
		// Register store with the store manager
		this.on({
			'add' : this.onAdd,
			'beforeload': this.onBeforeLoad,
			'load' : this.onAfterLoad,
			'remove' : this.onRemove,
			scope: this
		});
	},

	/**
	 * Event handler which is fired when the Hierarchy has loaded,
	 * In all cases, all stores which are loaded will be hooked using
	 * {@link #hookStoreRecord}.
	 *
	 * @param {Ext.data.Store} store The store which fired the event
	 * @param {Ext.data.Record[]} records The records which were loaded
	 * @param {Object} options The options which were send with the load request
	 * @private
	 */
	onAfterLoad: function(store, records, options)
	{
		this.hookStoreRecord(records);
		this.loading = false;
	},

	/**
	 * Event handler triggered when {@link Ext.data.Store#beforeload} event fire.
	 * It will make the {@link #loading} to true.
	 */
	onBeforeLoad: function ()
	{
		this.loading = true;
	},

	/**
	 * @return {Boolean} true if store is loading else false.
	 */
	isLoading: function ()
	{
		return this.loading;
	},

	/**
	 * Event handler fired when a {@link Zarafa.plugins.files.data.FilesStoreRecord store}
	 * has been added to the hierarchy. This will call {@link #hookStoreRecord}.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store This store
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord[]} records The record which is added
	 * @param {Number} index The index from where the record was added
	 * @private
	 */
	onAdd: function(store, records, index)
	{
		this.hookStoreRecord(records);
	},

	/**
	 * Event handler fired when a {@link Zarafa.hierarchy.data.MAPIStoreRecord store}
	 * has been added to the hierarchy. This will call {@link #unhookStoreRecord}.
	 *
	 * @param {Zarafa.hierarchy.data.HierarchyStore} store This store
	 * @param {Zarafa.hierarchy.data.MAPIStoreRecord} record The record which is removed
	 * @param {Number} index The index from where the record was removed
	 * @private
	 */
	onRemove: function(store, record, index)
	{
		this.unhookStoreRecord(record);
	},

	/**
	 * This will hook the event handlers {@link #onFolderAdd}, {@link #onFolderRemove} and {@link #onFolderUpdate},
	 * to the {@link Ext.data.Store#add}, {@link Ext.data.Store#remove} and {@link Ext.data.Store#update} events
	 * for the 'folders' substore for each store which is inside the hierarchy. This allows us to fire
	 * the {@link #addFolder}, {@link #removeFolder}, {@link #updateFolder} events.
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord[]} records The records which are hooked
	 * @private
	 */
	hookStoreRecord: function(records)
	{
		for (var i = 0, len = records.length; i < len; i++) {
			var folders = records[i].getSubStore('folders');
			folders.on({
				'add': this.onFolderAdd,
				'remove': this.onFolderRemove,
				'update': this.onFolderUpdate,
				scope: this
			});
		}
	},

	/**
	 * This will unhook the events from {@link #hookStoreRecord}.
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord} record The record to unhook
	 * @private
	 */
	unhookStoreRecord: function(record)
	{
		var folders = record.getSubStore('folders');
		folders.un('add', this.onFolderAdd, this);
		folders.un('remove', this.onFolderRemove, this);
		folders.un('update', this.onFolderUpdate, this);
	},

	/**
	 * Event handler fired when a folder in one of the {@link Zarafa.plugins.files.data.FilesStoreRecord stores}
	 * in this store has been {@link Ext.data.Store#add added}. This will fire the
	 * {@link #addFolder} event.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFoldersSubStore} substore
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder which is added to the hierarchy
	 * @param {Number} index The index in the substore from where the folder was added
	 * @private
	 */
	onFolderAdd: function(store, records, index)
	{
		this.fireEvent('addFolder', this, store.getParentRecord(), records, index);
	},

	/**
	 * Event handler fired when a folder in one of the {@link Zarafa.hierarchy.data.MAPIStoreRecord stores}
	 * in this store has been {@link Ext.data.Store#update updated}. This will fire the
	 * {@link #updateFolder} event.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFoldersSubStore} store
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} record which is updated in the hierarchy.
	 * @param {String} operation The update operation being performed. Value may be one of
	 * {@link Ext.data.Record#EDIT}, {@link Ext.data.Record#REJECT}, {@link Ext.data.Record#COMMIT}.
	 * @private
	 */
	onFolderUpdate: function(store, record, operation)
	{
		this.fireEvent('updateFolder', this, store.getParentRecord(), record, operation);
	},

	/**
	 * Event handler fired when a folder in one of the {@link Zarafa.hierarchy.data.MAPIStoreRecord stores}
	 * in this store has been {@link Ext.data.Store#remove removed}. This will fire the
	 * {@link #removeFolder} event.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFoldersSubStore} substore
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder which is removed from the hierarchy
	 * @param {Number} index The index in the substore from where the folder was removed
	 * @private
	 */
	onFolderRemove: function(store, record, index)
	{
		this.fireEvent('removeFolder', this, store.getParentRecord(), record, index);
	},

	/**
	 * Retrieves a folder by MAPI ID from list of stores
	 * @param {String} id MAPI ID of the folder to search for.
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} a folder matching the given ID, or undefined if not found.
	 */
	getFolder: function(id)
	{
		for(var i = 0, len = this.getCount(); i < len; i++) {
			var folderStore = this.getAt(i).getFolderStore();

			if(folderStore) {
				var folder = folderStore.getById(id);
				if(folder) {
					return folder;
				}
			}
		}
		return undefined;
	},

	/**
	 * Retrieves a folder by MAPI ID from list of stores
	 * @param {String} id MAPI ID of the folder to search for.
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} a folder matching the given ID, or undefined if not found.
	 */
	getFolderByFolderId: function(id)
	{
		for(var i = 0, len = this.getCount(); i < len; i++) {
			var folderStore = this.getAt(i).getFolderStore();

			if(folderStore) {
				var folderIndex = folderStore.findExact('folder_id', id);
				if(folderIndex > -1) {
					return folderStore.getAt(folderIndex);
				}
			}
		}
		return undefined;
	},

	/**
	 * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
	 * <br> Function just adds 'list' as actionType in options and calls parent {@link Zarafa.core.data.IPFStore#load} method.
	 * <br> Check documentation of {@link Ext.data.Store#load} for more information.
	 */
	load: function(options)
	{
		if (!Ext.isObject(options)) {
			options = {};
		}

		if (!Ext.isObject(options.params)) {
			options.params = {};
		}

		// Load should always cancel the previous actions.
		if (!Ext.isDefined(options.cancelPreviousRequest)) {
			options.cancelPreviousRequest = true;
		}

		// Reload the files hierarchy panel.
		if (options.reload) {
			options.params = {
				reload: options.reload
			};
		}

		Ext.applyIf(options, {
			actionType: Zarafa.core.Actions['list']
		});

		return Zarafa.plugins.files.data.FilesHierarchyStore.superclass.load.call(this, options);
	}
});



Ext.reg('filesplugin.hierarchystore', Zarafa.plugins.files.data.FilesHierarchyStore);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesJsonReader
 * @extends Zarafa.core.data.JsonReader
 */
Zarafa.plugins.files.data.FilesJsonReader = Ext.extend(Zarafa.core.data.JsonReader, {
	/**
	 * @cfg {Zarafa.core.data.RecordCustomObjectType} customObjectType The custom object type
	 * which represents the {@link Ext.data.Record records} which should be created using
	 * {@link Zarafa.core.data.RecordFactory#createRecordObjectByCustomType}.
	 */
	customObjectType : Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE,

	/**
	 * @constructor
	 * @param {Object} meta Metadata configuration options.
	 * @param {Object} recordType (optional) Optional Record type matches the type
	 * which must be read from response. If no type is given, it will use the
	 * record type for the {@link Zarafa.core.data.RecordCustomObjectType#FILES_FOLDER_STORE}.
	 */
	constructor : function(meta, recordType)
	{
		meta = Ext.applyIf(meta || {}, {
			dynamicRecord : false,
			id : 'store_entryid',
			idProperty : 'store_entryid',
			customObjectType : meta.customObjectType || Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE
		});

		// If no recordType is provided, force the type to be a Distlist Member
		if (!Ext.isDefined(recordType)) {
			recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(meta.customObjectType);
		}

		Zarafa.plugins.files.data.FilesJsonReader.superclass.constructor.call(this, meta, recordType);
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesProxy
 * @extends Zarafa.core.data.IPMProxy
 *
 * Special Proxy for the {@link Zarafa.plugins.files.data.FilesRecordStore FilesRecord Store}.
 */
Zarafa.plugins.files.data.FilesProxy = Ext.extend(Zarafa.core.data.IPMProxy, {
	/**
	 * This will create a {@link Zarafa.core.data.ProxyResponseHandler ProxyResponseHandler} object
	 * which will be used by the {@link Zarafa.core.data.ResponseRouter ResponseRouter} when the
	 * response for the given request has returned.
	 *
	 * @param {String} modulename The modulename which is being accessed with this request
	 * @param {Zarafa.core.Actions} serverAction The action to perform on the server.
	 * @param {Ext.data.Api.action} action name of the action to perform.
	 * @param {Ext.data.Record[]} records list of records to operate on.
	 * @param {Object} parameters object containing user parameters such as range (pagination) information, sorting information, etc.
	 * @param {Ext.data.DataReader} reader data reader. Converts raw JavaScript objects (in our case) to instances of {@link Ext.data.Record}
	 * @param {Function} callback call back function to call when the request has finished successfully.
	 * @param {Object} scope scope for the call back function.
	 * @param {Object} args arguments object. This will be passed to the call back function on successful read.
	 * @return {Object} An instance of the {@link Zarafa.core.data.ProxyResponseHandler ProxyResponseHandler}
	 * which should be used for this request.
	 * @private
	 */
	getResponseHandlerForRequest : function(modulename, serverAction, action, records, parameters, reader, callback, scope, args)
	{
		return new Zarafa.plugins.files.data.ResponseHandler({
			proxy: this,
			action: action,
			reader: reader,
			sendRecords: records,
			options: args,
			callback: callback,
			scope: scope
		});
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.FilesRecordFields
 *
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.core.data.IPMRecord IPMRecord} object.
 * These fields will be available in all 'IPM.Files' type messages.
 */
Zarafa.plugins.files.data.FilesRecordFields = [
	// FIXME : try to remove this id proper.
	{name: 'id', mapping:"entryid"},
	{name: 'folder_id'},
	{name: 'fileid'},
	{name: 'path'},
	{name: 'type', type: 'int', defaultValue: Zarafa.plugins.files.data.FileTypes.FOLDER},
	{name: 'filename'},
	{name: 'display_name', mapping: 'filename'},
	{name: 'isshared', type: 'boolean', defaultValue: false},
	{name: 'sharedid'},
	{name: 'lastmodified', type: 'int', defaultValue: null},
	{name: 'message_size', type: 'int', defaultValue: 0},
	{name: 'deleted', type: 'boolean', defaultValue: false},
];

/**
 * @class Zarafa.plugins.files.data.FilesRecord
 * @extends Zarafa.core.data.IPMRecord
 */
Zarafa.plugins.files.data.FilesRecord = Ext.extend(Zarafa.core.data.IPMRecord, {

	/**
	 * @cfg {Boolean} Record state.
	 */
	disabled: false,

	/**
	 * The base array of ID properties which is copied to the {@link #idProperties}
	 * when the record is being created.
	 * @property
	 * @type Array
	 * @private
	 */
	baseIdProperties : [ 'folder_id', 'entryid', 'store_entryid', 'parent_entryid' ],

	/**
	 * Applies all data from an {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}
	 * to this instance. This will update all data.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecord} record The record to apply to this
	 * @return {Zarafa.plugins.files.data.FilesRecord} this
	 */
	applyData: function (record) {
		this.beginEdit();

		Ext.apply(this.data, record.data);
		Ext.apply(this.modified, record.modified);

		this.dirty = record.dirty;

		this.endEdit(false);

		return this;
	},

	/**
	 * Builds and returns inline image URL to download inline images,
	 * it uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 *
	 * @return {String} URL for downloading inline images.
	 */
	getInlineImageUrl: function () {
		return container.getBasePath() + "index.php?load=custom&name=download_file&" + Ext.urlEncode({
			id    : this.get('folder_id'),
			inline: true
		});
	},

	/**
	 * Builds and returns attachment URL to download attachment,
	 * it uses {@link Zarafa.core.data.IPMRecord IPMRecord} to get store and message entryids.
	 *
	 * @return {String} URL for downloading attachment.
	 */
	getAttachmentUrl: function () {
		return container.getBasePath() + "index.php?sessionid=" + container.getUser().getSessionId() + "&load=custom&name=download_file&" + Ext.urlEncode({
			id    : this.get('folder_id'),
			inline: false
		});
	},

	/**
	 * Set the disabled flag.
	 *
	 * @param {Boolean} state
	 */
	setDisabled: function (state) {
		this.disabled = state;
	},

	/**
	 * Get the disabled flag.
	 *
	 * @return {Boolean}
	 */
	getDisabled: function () {
		return this.disabled;
	},

	/**
	 * Get the file id.
	 *
	 * @return {Boolean}
	 */
	getFileid: function () {
		return this.get('fileid') || "-1";
	},

	/**
	 * Get the account object for the record.
	 *
	 * @return {Zarafa.plugins.files.data.AccountRecord} an IPM.FilesAccount record
	 */
	getAccount: function ()
	{
		// FixME : Create function called getAccountFromRecord in
		// Files context model.
		var accId = Zarafa.plugins.files.data.Utils.File.getAccountId(this.get('folder_id'));
		var store = container.getCurrentContext().getAccountsStore();

		// look up the account
		var account = store.getById(accId);

		return account;
	},

	/**
	 * Check selected record is folder record or not.
	 *
	 * @return {boolean} return true if selected record is
	 * folder record else false.
	 */
	isFolder : function ()
	{
		return this.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER;
	},

	/**
	 * Move the {@link Zarafa.plugins.files.data.FilesRecord record} to a different
	 * {@link Zarafa.plugins.files.data.FilesFolderRecord folder}.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The folder to copy the record to
	 */
	moveTo : function(folder)
	{
		this.addMessageAction('action_type', 'move');
		this.addMessageAction('parent_entryid', folder.get('entryid'));
		this.addMessageAction('destination_folder_id', folder.get('folder_id'));
	}
});

Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_FILES');

Zarafa.core.data.RecordFactory.addFieldToMessageClass('IPM.Files', Zarafa.plugins.files.data.FilesRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToMessageClass('IPM.Files', Zarafa.plugins.files.data.FilesRecord);

Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, Zarafa.plugins.files.data.FilesRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, Zarafa.plugins.files.data.FilesRecord);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesRecordStore
 * @extends Zarafa.core.data.ListModuleStore
 *
 * The FilesStore class provides a way to connect the 'filesbrowsermodule' in the server back-end to an
 * Ext.grid.GridPanel object. It provides a means to retrieve files listings asynchronously.
 * The store has to be initialised with a store Id, which corresponds (somewhat confusingly) to
 * a MAPI store id. The FilesStore object, once instantiated, will be able to retrieve and list
 * files from a single specific store only.
 *
 * @constructor
 */
Zarafa.plugins.files.data.FilesRecordStore = Ext.extend(Zarafa.core.data.ListModuleStore, {

	/**
	 * @cfg folderId The folderId of selected folder.
	 */
	folderId: undefined,

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

		Ext.applyIf(config || {}, {
			remoteGroup: true,
			preferredMessageClass: 'IPM.Files',
			defaultSortInfo: {
				field: 'filename',
				direction: 'asc'
			},
			entryId:'#R#',
			// FIXME: try to remove folderId and id props from FilesFolderRecord.js
			folderId: "#R#",
			listeners: {
				load: this.onLoad,
				exception: this.onLoadException
			},
			proxy: new Zarafa.plugins.files.data.FilesProxy({
				listModuleName: 'filesbrowsermodule',
				itemModuleName: 'filesbrowsermodule'
			})
		});

		this.addEvents(
			/**
			 * @event createfolder
			 * Fires when a folder has been created.
			 * @param {Zarafa.plugins.files.data.FilesRecordStore} store store in which folder record was created.
			 * @param {String} parentFolderId The parentFolderId under which folder was created.
			 * @param {Object} data The data contains the information about newly created folder.
			 */
			'createfolder'
		);

		Zarafa.plugins.files.data.FilesRecordStore.superclass.constructor.call(this, config);
	},

	/**
	 * Initialize events which {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore} will listen to.
	 * @protected
	 */
	initEvents: function () {
		Zarafa.plugins.files.data.FilesRecordStore.superclass.initEvents.apply(this, arguments);

		if (Ext.isDefined(this.hierarchyStore)) {
			this.hierarchyStore.on('addFolder', this.onHierarchyAddFolder, this);
		}
	},

	/**
	 * Event handler triggers when folder was added in hierarchy. function was
	 * responsible to save the search criteria in settings.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store The store which fired the event
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord} mapiStore mapi store in which new folders are added.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord/Zarafa.plugins.files.data.FilesFolderRecord[]} record folder record(s) which are added in hierarchy.
	 * @private
	 */
	onHierarchyAddFolder: function(store, mapiStore, records)
	{
		var reloadStore = false;

		Ext.each(records, function(item, index, obj) {
			if (item.get('parent_entryid') === this.entryId) {
				reloadStore = true;
				return false;
			}
		}, this);

		if(reloadStore) {
			this.reload();
		}
	},

	/**
	 * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
	 * <br><p>Notes:</p><div class="mdetail-params"><ul>
	 * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
	 * loaded. To perform any post-processing where information from the load call is required, specify
	 * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
	 * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
	 * properties in the <code>options.params</code> property to establish the initial position within the
	 * dataset, and the number of Records to cache on each read from the Proxy.</li>
	 * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
	 * will be automatically included with the posted parameters according to the specified
	 * <code>{@link #paramNames}</code>.</li>
	 * </ul></div>
	 * @param {Object} options An object containing properties which control loading options:<ul>
	 * <li><b><tt>params</tt></b>: Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
	 * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
	 * <code>{@link #baseParams}</code> of the same name.</p>
	 * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
	 * <li><b>callback</b>: Function<div class="sub-desc"><p>A function to be called after the Records
	 * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
	 * <li>r: Ext.data.Record[] An Array of Records loaded.</li>
	 * <li>options: Options object from the load call.</li>
	 * <li>success: Boolean success indicator.</li></ul></p></div></li>
	 * <li><b>scope</b>: Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
	 * to the Store object)</p></div></li>
	 * <li><b>add</b>: Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
	 * replace the current cache.  <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
	 * </ul>
	 * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
	 * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
	 */
	load: function(options)
	{
		if (!Ext.isObject(options)) {
			options = {};
		}

		if (!Ext.isObject(options.params)) {
			options.params = {};
		}

		if(!Ext.isEmpty(options.folder)) {
			// If a folder was provided in the options, we apply the folder
			this.setFolder(options.folder);
		} else if(Ext.isDefined(options.params.entryid) && Ext.isDefined(options.params.store_entryid)){
			// If the entryid was provided in the parameters we apply the params
			this.setEntryId(options.params.entryid, false);
			this.setStoreEntryId(options.params.store_entryid, false);

			if(Ext.isDefined(options.params.folderid)) {
				this.setFolderId(options.params.folderid, false);
			}
		}

		// Override the given entryid and store entryid.
		Ext.apply(options.params, {
			entryid: this.entryId,
			id: this.folderId,
			store_entryid: this.storeEntryId
		});

		/*
		 * these options can be passed in arguments, or it can be set by setter methods of
		 * {@link Zarafa.core.data.ListModuleStore ListModuleStore}, like {@link #setRestriction}
		 * and {@link #setActionType}, advantage of using setter methods would be that
		 * all consecutive requestswill use that options if its not passed in arguments.
		 * but load method doesn't store these options automatically (like in case of entryids), so
		 * you have to call setter methods to actually set these options.
		 */
		Ext.applyIf(options, {
			actionType: this.actionType
		});

		return Zarafa.plugins.files.data.FilesRecordStore.superclass.load.call(this, options);
	},


	/**
	 * Function will set folder entryid and store entryid.
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord[]} mapiFolder mapi folder that should be used to load data.
	 */
	setFolder: function(mapiFolder)
	{
		Ext.each(mapiFolder, function(folder, index) {
			this.setEntryId(folder.get('entryid'), index !== 0);
			this.setStoreEntryId(folder.get('store_entryid'), index !== 0);
			this.setFolderId(folder.get('folder_id'), index !== 0);
		}, this);
	},

	setFolderId: function(folderId, add)
	{
		if(!Ext.isEmpty(add) && add) {
			// multiple entryids
			if(Ext.isEmpty(this.folderId)) {
				this.folderId = [];
			}

			if(!Ext.isEmpty(this.folderId) && !Array.isArray(this.folderId)) {
				this.folderId = [ this.folderId ];
			}

			this.folderId.push(folderId);
		} else {
			// single entryid
			this.folderId = folderId;
		}
	},

	/**
	 * Eventhandler that handles the beforeload event of the store.
	 * It will switch the viewmode if necessary.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store
	 * @param {Zarafa.plugins.files.data.FilesRecord} records
	 * @param {Object} options
	 */
	onLoad: function (store, records, options)
	{
		// TODO: Move to files main panel.
		var path = options.params.id;
		var componentBox = Zarafa.plugins.files.data.ComponentBox;
		var viewPanel = componentBox.getViewPanel();
		var disabledSwitchViewButton = false;

		if (Ext.isEmpty(path) || path === "#R#" ) {
			// switch to the account overview!
			viewPanel.switchView('files-accountview');
			componentBox.getPreviewPanel().topToolbar.disable();
			disabledSwitchViewButton = true;
		} else if (componentBox.getItemsView() instanceof Zarafa.plugins.files.ui.FilesRecordAccountView) {
			switch (componentBox.getContext().getCurrentView()) {
				case Zarafa.plugins.files.data.Views.LIST:
					viewPanel.switchView('files-gridview');
					break;
				case Zarafa.plugins.files.data.Views.ICON:
					viewPanel.switchView('files-iconview');
					break;
			}
		}
		container.getMainPanel().filesSwitchViewButton.setDisabled(disabledSwitchViewButton);
	},

	/**
	 * Eventhandler that handles the exception event of the store.
	 *
	 * @param proxy
	 * @param type
	 * @param action
	 * @param options
	 * @param response
	 * @param arg
	 */
	onLoadException: function (proxy, type, action, options, response, arg) {
		// handle unauthorized messages of plugins that need fronten authorization (dropbox, google,...)
		if(response.error && response.error.info.code) {
			if(parseInt(response.error.info.code) == 401) {
				// recall the auth procedure

				// first we need to get the account
				var failedID = options.params.id;
				var accID = Zarafa.plugins.files.data.Utils.File.getAccountId(failedID);
				var accStore = container.getCurrentContext().getAccountsStore();
				// look up the account
				var account = accStore.getById(accID);
				if (Ext.isDefined(account) && account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.OAUTH)) {
					account.renewOauthToken();
				}
			}
		}
	},

	/**
	 * Reloads the Record cache from the configured Proxy. See the superclass {@link Zarafa.core.data.ListModuleStore#reload documentation}
	 * for more details.
	 * During reload we add an extra option into the {@link #load} argument which marks the action as a reload
	 * action.
	 *
	 * @param {Object} options
	 */
	reload: function (options) {
		var currentPath = this.getPath();

		// set the reload flag - then the backend will reload the cache!
		options = Ext.applyIf(options || {}, {
			params: {
				reload: true,
				id: currentPath
			},
			reload: true
		});

		Zarafa.plugins.files.data.FilesRecordStore.superclass.reload.call(this, options);
	},

	/**
	 * Returns the current root directory.
	 *
	 * @return {String} The current root directory.
	 */
	getPath: function () {
		return this.entryId;
	}
});
Ext.reg('filesplugin.filesrecordstore', Zarafa.plugins.files.data.FilesRecordStore);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesShadowProxy
 * @extends Zarafa.core.data.ShadowProxy
 *
 * The ShadowProxy is an extension of the {@link Zarafa.core.data.MAPIProxy MAPIProxy}, The
 * {@link Zarafa.core.data.ShadowProxy ShadowProxy} works by dynamically detecting the
 * names for the listmodule and itemmodule by which we are communication with the
 * PHP side. The names are determined based on the {@link Zarafa.core.data.IPMRecords records}
 * which are being send to the server. This implies that the proxy only works while
 * directly working with {@link Zarafa.core.data.IPMRecord records}, as such listing items
 * is not possible. For that purpose the {@link Zarafa.core.data.IPMProxy} must be
 * used with the corresponding list module names.
 */
Zarafa.plugins.files.data.FilesShadowProxy = Ext.extend(Zarafa.core.data.ShadowProxy, {

	/**
	 * This will create a {@link Zarafa.core.data.ProxyResponseHandler ProxyResponseHandler} object
	 * which will be used by the {@link Zarafa.core.data.ResponseRouter ResponseRouter} when the
	 * response for the given request has returned.
	 * @param {String} modulename The modulename which is being accessed with this request
	 * @param {Zarafa.core.Actions} serverAction The action to perform on the server.
	 * @param {Ext.data.Api.action} action name of the action to perform.
	 * @param {Ext.data.Record[]} records list of records to operate on.
	 * @param {Object} parameters object containing user parameters such as range (pagination) information, sorting information, etc.
	 * @param {Ext.data.DataReader} reader data reader. Converts raw JavaScript objects (in our case) to instances of {@link Ext.data.Record}
	 * @param {Function} callback call back function to call when the request has finished successfully.
	 * @param {Object} scope scope for the call back function.
	 * @param {Object} args arguments object. This will be passed to the call back function on successful read.
	 * @return {Object} An instance of the {@link Zarafa.core.data.ProxyResponseHandler ProxyResponseHandler}
	 * which should be used for this request.
	 * @private
	 */
	getResponseHandlerForRequest : function(modulename, serverAction, action, records, parameters, reader, callback, scope, args)
	{
		return new Zarafa.plugins.files.data.FilesFolderResponseHandler({
			proxy: this,
			action: action,
			reader: reader,
			sendRecords: records,
			options: args,
			callback: callback,
			scope: scope
		});
	},

	/**
	 * Implementation of {@link Zarafa.core.data.MAPIProxy#getListModuleName} which returns
	 * the listModuleName for the given {@link Zarafa.core.data.IPMRecord record}. If no moduleName
	 * could be detected, it defaults to the configured {@link #listModuleName}.
	 * @param {Zarafa.core.data.IPMRecord} record the record for which the listModuleName is requested
	 * @return {String} the listModuleName
	 * @private
	 */
	getListModuleName : function(record)
	{
		return "hierarchylistmodule";
	},

	/**
	 * Implementation of {@link Zarafa.core.data.MAPIProxy#getItemModuleName} which returns
	 * the itemModuleName for the given {@link Zarafa.core.data.IPMRecord record}. If no moduleName
	 * could be detected, it defaults to the configured {@link #itemModuleName}.
	 * @param {Zarafa.core.data.IPMRecord} record the record for which the itemModuleName is requested
	 * @return {String} the itemModuleName
	 * @private
	 */
	getItemModuleName : function(record)
	{
		return "hierarchylistmodule";
	}

});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ShadowStore
 * @extends Zarafa.core.data.ShadowStore
 * @xtype zarafa.shadowstore
 *
 * A store which holds all items which are being created or edited within a {@link Zarafa.core.ui.ContentPanel}
 * This store only contains references of {@link Zarafa.core.data.IPMRecord} elements which have
 * been retrieved from the server by a regular {@link Zarafa.core.data.ListModuleStore}.
 * <p>
 * Each {@link Zarafa.core.ui.ContentPanel} will register the {@link Zarafa.core.data.MAPIRecord} on which it is working
 * to this {@link Zarafa.core.data.ShadowStore}
 *
 * A store that communicates with a list module on the php side. It supports listing items,
 * pagination, etc.
 * <p>
 * Pagination is not properly supported since there is no way to pass the desired page size
 * to the server side. Therefore the page size has to be hard-coded to 50 items.
 */
Zarafa.plugins.files.data.FilesShadowStore = Ext.extend(Zarafa.core.data.ShadowStore, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		var recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER);

		Ext.applyIf(config, {
			batch : false,
			proxy : new Zarafa.plugins.files.data.FilesShadowProxy(),
			writer : new Zarafa.core.data.JsonWriter(),
			reader : new Zarafa.core.data.JsonReader({
				dynamicRecord : false,
			}, recordType)
		});

		Zarafa.plugins.files.data.FilesShadowStore.superclass.constructor.call(this, config);
	}
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.FilesStoreRecord
 * Array of {@link Ext.data.Field field} configurations for the
 * {@link Zarafa.core.data.IPFRecord IPFRecord} object.
 */
Zarafa.plugins.files.data.FilesStoreRecordFields = [
	{name: 'path'},
	{name: 'entryid'},
	{name: 'store_entryid'},
	// Fixme :
	{name: 'text'},
	{name: 'object_type'},
	{name: 'status'},
	{name: 'status_description'},
	{name: 'backend'},
	{name: 'backend_config'},
	{name: 'backend_features'},
	{name: 'cannot_change'},
	{name: 'filename'},
	{name: 'subtree_id'},
	{name: 'display_name'},
	{name: 'account_sequence'}
];

Zarafa.plugins.files.data.FilesStoreRecord = Ext.extend(Zarafa.core.data.IPFRecord, {
	/**
	 * The base array of ID properties which is copied to the {@link #idProperties}
	 * when the record is being created.
	 * @property
	 * @type Array
	 * @private
	 */
	baseIdProperties : [ 'store_entryid'],

	/**
	 * @constructor
	 * @param {Object} data The data which must be applied to this record
	 * @param {Object} id The unique id for this record
	 * @param {Zarafa.core.data.RecordDefinition} definition The record definition used to
	 * construct this record
	 */
	constructor : function(data, id, definition)
	{
		if (!Ext.isDefined(definition)) {
			definition = Zarafa.core.data.RecordFactory.getRecordDefinitionByCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE);
		}

		Zarafa.plugins.files.data.FilesStoreRecord.superclass.constructor.call(this, data, id, definition);
	},

	/**
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} subtree folder.
	 */
	getSubtreeFolder : function()
	{
		return this.getFolder(this.get('subtree_id'));
	},

	/**
	 * Retrieves a folder by MAPI id.
	 * @param {String} id the id of the folder.
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord} folder object or undefined if not found.
	 */
	getFolder : function(id)
	{
		var store = this.getFolderStore();

		if (store) {
			return store.getById(id);
		}
	},

	/**
	 * Get the Folder store for the {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord} (See {@link #getSubStore}).
	 * @return {Zarafa.plugins.files.data.FilesFoldersSubStore} The Folder store.
	 */
	getFolderStore : function()
	{
		return this.getSubStore('folders');
	},

	getBackend : function ()
	{
		return this.get('backend');
	}
});

Zarafa.core.data.RecordCustomObjectType.addProperty('FILES_FOLDER_STORE');

Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE, Zarafa.plugins.files.data.FilesStoreRecordFields);
Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE, Zarafa.plugins.files.data.FilesStoreRecord);
Zarafa.core.data.RecordFactory.setSubStoreToCustomType(Zarafa.core.data.RecordCustomObjectType.FILES_FOLDER_STORE, 'folders',Zarafa.plugins.files.data.FilesFoldersSubStore);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.NavigatorTreeLoader
 * @extends Ext.tree.TreeLoader
 *
 * Files directory loader. Extends Ext treeloader to use grommunio
 * specific requests.
 */
Zarafa.plugins.files.data.NavigatorTreeLoader = Ext.extend(Ext.tree.TreeLoader, {

	/**
	 * When {@link #deferredLoading} is true, this property indicates if a call to
	 * {@link #doHierarchyLoad} has been made and has been scheduled. This implies
	 * that no events from the {@link #store} need to be handled, as a full refresh
	 * is pending.
	 * @property
	 * @type Boolean
	 * @private
	 */
	isDeferred : false,

	/**
	 * @cfg {Boolean} deferredLoading True to defer updating the Hierarchy when the panel
	 * is currently not visible. The parent {@link Ext.Container} which contains the
	 * {@link Ext.layout.CardLayout}, is stored in the {@link #deferredLoadingActiveParent}.
	 */
	deferredLoading : false,

	/**
	 * When {@link #deferredLoading} is true, this property contains the {@link Ext.Container}
	 * to which this loader will be listening to determine which Container is active.
	 * This will be initialized during {@link #onTreeAfterRender}.
	 * @property
	 * @type Ext.Container
	 * @private
	 */
	deferredLoadingActiveParent : undefined,

	/**
	 * @cfg {Object} config option for {@link Zarafa.plugins.files.ui.FilesFolderNode FilesFolderNode}
	 */
	nodeConfig : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			directFn : this.directFn.createDelegate(this),
		});

		Zarafa.plugins.files.data.NavigatorTreeLoader.superclass.constructor.call(this, config);
		// If the tree is already rendered, call onTreeAfterRender directly,
		// otherwise add the event handler.
		if (this.tree.rendered) {
			this.onTreeAfterRender();
		} else {
			this.tree.on('afterrender', this.onTreeAfterRender, this, { single : true });
		}
	},

	onTreeAfterRender : function()
	{
		this.bindStore(this.store, true);

		// If deferred loading is enabled, then we are going to need the parent
		// container which will have to be activated before we will load it.
		// If we can't find the parent, then don't defer loading.
		if (this.deferredLoading === true) {
			// Search for the desired container which can be activated.
			if (this.isParentCardLayout(this.tree)) {
				this.deferredLoadingActiveParent = this.tree;
			} else {
				this.deferredLoadingActiveParent = this.tree.findParentBy(this.isParentCardLayout, this);
				if (!this.deferredLoadingActiveParent) {
					this.deferredLoading = false;
				}
			}
		}
	},

	/**
	 * Returned true if the {@link Ext.Component#ownerCt owner} of the given {@link Ext.Container}
	 * contains the {@link Ext.layout.CardLayout}. This function is given in
	 * {@link #onTreeAfterRender} to determine the {@link #deferredLoadingActiveParent}.
	 * @param {Ext.Container} ct The container to check
	 * @return {Boolean} True if the parent of the given container has the CardLayout
	 * @private
	 */
	isParentCardLayout : function(ct)
	{
		return ct.ownerCt && ct.ownerCt.layout && ct.ownerCt.layout.type === 'card';
	},

	/**
	 * Bind a store to this loader. This will initialize all required event handlers.
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store The store to bind
	 * @param {Boolean} init True when this is called during initialization.
	 * @private
	 */
	bindStore : function(store, init)
	{
		if (init !== true && this.store === store) {
			return;
		}

		if (this.store) {
			this.store.un('load', this.onHierarchyLoad, this);
			this.store.un('remove', this.onHierarchyStoreRemove, this);
			this.store.un('addFolder', this.onHierarchyAddFolder, this);
			this.store.un('updateFolder', this.onHierarchyUpdateFolder, this);
			this.store.un('removeFolder', this.onHierarchyRemoveFolder, this);
		}

		this.store = store;
		if (this.store) {
			this.store.on({
				'load' : this.onHierarchyLoad,
				'remove' : this.onHierarchyStoreRemove,
				'addFolder' : this.onHierarchyAddFolder,
				'updateFolder' : this.onHierarchyUpdateFolder,
				'removeFolder' : this.onHierarchyRemoveFolder,
				'scope' : this
			});
		}
	},

	onHierarchyLoad: function(){
		var parentCt = this.deferredLoadingActiveParent;

		if (parentCt === parentCt.ownerCt.layout.activeItem) {
			this.doHierarchyLoad();
		} else {
			// We are going to defer to doHierarchyLoad() action,
			// set isDeferred to true, so we don't need to perform
			// update event handlers.
			this.isDeferred = true;
			this.deferredLoadingActiveParent.on('activate', this.doHierarchyLoad, this, { single : true });
		}
	},

	/**
	 * Called by {@link #onHierarchyLoad} to start (re)loading the hierarchy.
	 * @private
	 */
	doHierarchyLoad : function()
	{
		var rootNode = this.tree.getRootNode();

		if (this.fireEvent('beforeload', this, rootNode, this.directFn) !== false) {
			this.directFn(rootNode.id, this.doHierarchyLoadCallback.createDelegate(this));
		}

		// The deferred action has been completed,
		// we can now listen to update events again.
		this.isDeferred = false;
	},

	/**
	 * Callback function for {@link #directFn} as used by {@link #doHierarchyLoad}.
	 * @param {Object} data The data as returned by the server
	 * @param {Object} response The response as returned by the server
	 * @private
	 */
	doHierarchyLoadCallback : function(data, response)
	{
		var rootNode = this.tree.getRootNode();

		for (var i = 0, len = data.length; i < len; i++) {
			var item = data[i];
			var folder = item.folder;
			// Check if the node already exists or not.
			var treeNode = rootNode.findChildByEntryId(folder.get('id'));
			if (!treeNode) {
				var node = this.createNode(item);
				rootNode.appendChild(node);
			} else if (treeNode.attributes.folder !== folder) {
				treeNode.attributes.folder = folder;
				treeNode.reload();
			}
		}
		// when we close the shared store suggested contact folder
		// of shared store was removed but node was not removed from
		// contact context tree panel because we don't refresh/reload the tree panel
		// nodes when we switch the context so here we just reload the root node.
		if(rootNode.childNodes.length !== data.length) {
			rootNode.reload();
		}

		this.fireEvent('load', this, rootNode, response);
	},

	/**
	 *
	 */
	onHierarchyStoreRemove : function(){
		// TODO: Currently we reload the hierarchy store to remove the backend account from hierarchy
		//  rather to do so we can simple remove the backend account from hierarchy.
		console.log(" onHierarchyStoreRemove called");
	},

	/**
	 *
	 * @param store
	 * @param mapiStore
	 * @param record
	 */
	onHierarchyAddFolder : function(store, mapiStore, record) {
		// A call to doHierarchyLoad is pending,
		// no need to execute this event handler.
		if (this.isDeferred === true) {
			return;
		}

		if (Array.isArray(record)) {
			for (var i = 0, len = record.length; i < len; i++) {
				this.onHierarchyAddFolder(store, mapiStore, record[i]);
			}
			return;
		}

		if (record.phantom !== true) {
			if (this.tree.nodeFilter(record)) {
				var treeNode = this.tree.getNodeById(record.get('id'));

				if (!treeNode) {
					var parentNode = this.getFilteredParentNode(record);
					var nodeType = 'filesfolder';

					if (!parentNode) {
						parentNode = this.tree.getRootNode();
						nodeType = 'filesrootfolder';
					}

					var newNode = this.createNode(Ext.apply({ nodeType : nodeType, folder: record }, this.nodeConfig));
					parentNode.loading = false;
					parentNode.appendChild(newNode);
					parentNode.expand(false, true);
				}
			}
		}
	},

	/**
	 *
	 * @param store
	 * @param mapiStore
	 * @param record
	 */
	onHierarchyUpdateFolder : function(store, mapiStore, record) {
		// A call to doHierarchyLoad is pending,
		// no need to execute this event handler.
		if (this.isDeferred === true) {
			return;
		}

		var treeNode = this.tree.getNodeById(record.get('entryid'));
		if (!treeNode) {
			// Don't add new node in hierarchy if its parent node is
			// not expanded yet. As Extjs follow the lazy rendering so when we expand the
			// parent node, tree automatically creates respective child nodes
			var parentNode = this.getFilteredParentNode(record);
			if (Ext.isDefined(parentNode) && (!parentNode || !parentNode.isExpanded())) {
				return;
			}
			// treeNode not found, so apparently the folder change might
			// have made this folder visible in the current hierarchy.
			// Let the 'addFolder' event handler handle this case.
			this.onHierarchyAddFolder(store, mapiStore, record);
		} else if(!record.isHomeFolder()) {
			treeNode.updateUI(record);
		}
	},

	/**
	 *
	 * @param store
	 * @param mapiStore
	 * @param record
	 */
	onHierarchyRemoveFolder : function(store, mapiStore, record) {
		// A call to doHierarchyLoad is pending,
		// no need to execute this event handler.
		if (this.isDeferred === true) {
			return;
		}

		var treeNode = this.tree.getNodeById(record.get('id'));
		if (treeNode) {
			treeNode.remove(true);
		}
	},

	/**
	 *
	 * @param folder
	 * @param base
	 * @return {boolean}
	 */
	getFilteredParentNode : function(folder, base)
	{
		var parentfolder = folder.getParentFolder();

		var node = false;

		if (parentfolder) {
			if (parentfolder === base) {
				node = base;
			} else if (this.tree.nodeFilter(parentfolder)) {
				node = this.tree.getNodeById(parentfolder.get('id'));
			}

			if (!node) {
				node = this.getFilteredParentNode(parentfolder);
			}
		}

		return node;
	},

	/**
	 * This is called when a node in the HierarchyTree is being expanded, this will read
	 * the {@link Zarafa.plugins.files.data.FilesFolderRecord FilesFolderRecord} to find the child nodes which
	 * are positioned below the expanded node. But if folder is not expanded already then trigger the
	 * {@link Zarafa.core.Actions#updatelist update list} request to fetch the child folder to selected folder.
	 *
	 * @param {String} node The ID of the node which is being expanded
	 * @param {Function} fn The function which must be called with the JSON data of
	 * the nodes below the provided node.
	 * @private
	 */
	directFn: function (node, fn) {
		var treeNode = this.tree.getNodeById(node);
		var data = [];

		if (treeNode.isRoot) {
			var stores = this.store.getRange();
			for (var i = 0, len = stores.length; i < len; i++) {
				var store = stores[i];
				var folder = store.getSubtreeFolder();
				if (folder) {
					if (this.tree.nodeFilter(folder)) {
						data.push(Ext.apply({ nodeType : 'filesrootfolder', folder: folder }, this.nodeConfig));
					}
				}
			}
		} else {
			data = this.getFilteredChildNodes(treeNode.getFolder(), 'filesfolder');
			if (Ext.isEmpty(data) && treeNode.isExpandable() && !treeNode.isExpanded()) {
				var folder = treeNode.getFolder();
				var store = folder.getStore();
				store.load({
					folder : folder,
					actionType : Zarafa.core.Actions['updatelist'],
					cancelPreviousRequest : false,
					add:true
				});
				return;
			}
		}
		fn(data, {status: true});
	},

	/**
	 * Obtain the list of nodes which are positioned below the given {@link Zarafa.plugins.files.data.FilesFolderRecord folder}
	 * also subfolder will be used for a recursive call to {@link #getFilteredChildNodes} to see if the sub-subfolders
	 * do match the filter.
	 *
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} folder The folder which is clicked
	 * @param {String} nodeType The nodeType which must be applied to each node
	 * @return {Object[]} The array of nodes which must be created as subfolders
	 * @private
	 */
	getFilteredChildNodes : function(folder, nodeType)
	{
		var subfolders = folder.getChildren();
		var nodes = [];

		for (var i = 0, len = subfolders.length; i < len; i++) {
			var subfolder = subfolders[i];
			if (this.tree.nodeFilter(subfolder)) {
				nodes.push(Ext.apply({ nodeType : nodeType, folder: subfolder }, this.nodeConfig));
			}
		}

		return nodes;
	},

	/**
	 * Add extra attributes for a new {@link Zarafa.hierarchy.ui.FolderNode folderNode} which is about
	 * to be created. This will check the {@link Zarafa.hierarchy.ui.FolderNode#folder folder} to
	 * see what properties must be set.
	 * @param {Object} attr The attributes which will be used to create the node
	 * @return {Zarafa.hierarchy.ui.FolderNode} The created node
	 */
	createNode : function(attr)
	{
		var folder = attr.folder;

		if (folder) {
			attr.extendedDisplayName = attr.nodeType === 'filesrootfolder';
		}

		attr.uiProvider = Zarafa.plugins.files.ui.FolderNodeUI;
		attr.leaf = !folder.get('has_subfolder');

		return Zarafa.plugins.files.data.NavigatorTreeLoader.superclass.createNode.apply(this, arguments);
	},

	/**
	 * Update the reload flag.
	 *
	 * @param reload
	 */
	setReload: function (reload) {
		this.reload = reload;
	},

	/**
	 * Destroys the TreeLoader
	 */
	destroy : function()
	{
		this.bindStore(null);
		Zarafa.plugins.files.data.NavigatorTreeLoader.superclass.destroy.apply(this, arguments);
	}
});Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ResponseHandler
 * @extends Zarafa.core.data.AbstractResponseHandler
 * @xtype filesplugin.responsehandler
 *
 * Files plugin specific response handler.
 */
Zarafa.plugins.files.data.ResponseHandler = Ext.extend(Zarafa.core.data.IPMResponseHandler, {

	/**
	 * @cgf {String} The id of the opened node in fuile tree received from the Files
	 */
	nodeId: undefined,

	/**
	 * @cfg {Function} successCallback The function which
	 * will be called after success request.
	 */
	successCallback: null,

	/**
	 * @cfg {Function} failureCallback The function which
	 * will be called after a failed request.
	 */
	failureCallback: null,

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetquota: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetbackends: function (response) {

		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetversion: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doGetfilestree: function (response) {
		this.successCallback(response.items, response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doCheckifexists: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doDownloadtotmp: function (response) {
		this.successCallback(response.items, response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doCreatedir: function (response) {
		this.successCallback(response);
	},

	/**
	 * Call the successCallback callback function.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doUploadtobackend: function (response) {
		this.successCallback(response);
	},

	/**
	 * In case exception happened on server, server will return
	 * exception response with the code of exception.
	 *
	 * @param {Object} response Object contained the response data.
	 */
	doError: function (response) {
		Zarafa.common.dialogs.MessageBox.show({
			title  : _('Error'),
			msg    : response.info.original_message,
			icon   : Zarafa.common.dialogs.MessageBox.ERROR,
			buttons: Zarafa.common.dialogs.MessageBox.OK
		});
	}
});

Ext.reg('filesplugin.responsehandler', Zarafa.plugins.files.data.ResponseHandler);
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.Utils
 * @singleton
 *
 * This class contains some static helper methods.
 */
Zarafa.plugins.files.data.Utils = {

	/**
	 * Base64 methods.
	 */
	Base64: {

		_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

		/**
		 * Encode the given string to base64.
		 *
		 * @param {String} input
		 * @return {String}
		 */
		encode: function (input) {
			var output = "";
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
			var i = 0;

			input = this._utf8_encode(input);

			while (i < input.length) {

				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output +
				this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
				this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

			}

			return output;
		},

		/**
		 * Decode the given base64 encoded string.
		 *
		 * @param {String} input
		 * @return {String}
		 */
		decode: function (input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			while (i < input.length) {

				enc1 = this._keyStr.indexOf(input.charAt(i++));
				enc2 = this._keyStr.indexOf(input.charAt(i++));
				enc3 = this._keyStr.indexOf(input.charAt(i++));
				enc4 = this._keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}

			}

			output = this._utf8_decode(output);

			return output;

		},

		/**
		 * Decodes the given base64 encoded utf-8 string.
		 *
		 * @param {String} input
		 * @return {String}
		 * @private
		 */
		_utf8_decode: function (utftext) {
			var string = "";
			var i = 0;
			var c = 0
			var c1 = 0;
			var c2 = 0;
			var c3 = 0;

			while (i < utftext.length) {

				c = utftext.charCodeAt(i);

				if (c < 128) {
					string += String.fromCharCode(c);
					i++;
				}
				else if ((c > 191) && (c < 224)) {
					c2 = utftext.charCodeAt(i + 1);
					string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
					i += 2;
				}
				else {
					c2 = utftext.charCodeAt(i + 1);
					c3 = utftext.charCodeAt(i + 2);
					string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
					i += 3;
				}

			}

			return string;
		},

		/**
		 * Encode the given string to base64 in utf-8.
		 *
		 * @param {String} input
		 * @return {String}
		 * @private
		 */
		_utf8_encode: function (string) {
			string = string.replace(/\r\n/g, "\n");
			var utftext = "";

			for (var n = 0; n < string.length; n++) {

				var c = string.charCodeAt(n);

				if (c < 128) {
					utftext += String.fromCharCode(c);
				} else if ((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				}
				else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}

			}

			return utftext;
		}
	},

	/**
	 * Rendering methods
	 */
	Renderer: {

		/**
		 * Typerenderer for folders or files.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		typeRenderer: function (value, p, record) {
			switch (value) {
				case Zarafa.plugins.files.data.FileTypes.FOLDER:
					p.css = "zarafa-files-listview-icon " + Zarafa.plugins.files.data.Utils.File.getIconClass("folder", "16");
					break;
				case Zarafa.plugins.files.data.FileTypes.FILE:
					p.css = "zarafa-files-listview-icon " + Zarafa.plugins.files.data.Utils.File.getIconClass(record.get('filename'), "16");
					break;
				default :
					break;
			}

			p.css += ' zarafa-grid-empty-cell';

			return '';
		},

		/**
		 * Sharedrenderer for folders or files.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		sharedRenderer: function (value, p, record) {
			if (value) {
				p.css = "zarafa-files-listview-icon files_icon_16_share";
			}

			p.css += ' zarafa-grid-empty-cell';

			return '';
		},

		/**
		 * Renders timestamps.
		 *
		 * @param {Object} value The data value for the cell.
		 * @param {Object} p An object with metadata
		 * @param {Ext.data.record} record The {Ext.data.Record} from which the data was extracted.
		 * @return {String} The formatted string
		 */
		datetimeRenderer: function (value, p, record) {
			p.css = 'mail_date';

			value = new Date(value);
			return Ext.isDate(value) ? value.format(_('l d/m/Y G:i')) : _('Never');
		}
	},

	/**
	 * Formatting methods
	 */
	Format: {

		/**
		 * Simple format for a file size (xxx bytes, xxx KB, xxx MB, xxx GB).
		 *
		 * @param {Number/String} size The numeric value to format
		 * @return {String} The formatted file size
		 */
		fileSize: function (size) {
			if (size === -1 || size === "none") {
				return "";
			} else {
				if (size < 1024) {
					return size + " bytes";
				} else if (size < 1048576) {
					return (Math.round(((size * 10) / 1024)) / 10) + " KB";
				} else if (size < 1073741824) {
					return (Math.round(((size * 10) / 1048576)) / 10) + " MB";
				} else {
					return (Math.round(((size * 10) / 1073741824)) / 10) + " GB";
				}
			}
		},

		/**
		 * Simple format for a file size (xxx KB, xxx MB, xxx GB, xxx TB).
		 * It will display bytes in KB.
		 *
		 * @param {Number/String} size The numeric value to format
		 * @return {String} The formatted file size
		 */
		fileSizeList: function (size) {
			if (size === -1 || size === "none") {
				return "";
			} else {
				if (size < 1024) {
					if (size <= 100 && size != 0) {
						return "0.1 &nbsp;KB";
					} else {
						return (Math.round(((size * 10) / 1024)) / 10) + " &nbsp;KB";
					}
				} else if (size < 1048576) {
					return (Math.round(((size * 10) / 1024)) / 10) + " &nbsp;KB";
				} else if (size < 1073741824) {
					return (Math.round(((size * 10) / 1048576)) / 10) + " &nbsp;MB";
				} else if (size < 1099511627776) {
					return (Math.round(((size * 10) / 1073741824)) / 10) + " &nbsp;GB";
				} else {
					return (Math.round(((size * 10) / 1099511627776)) / 10) + " &nbsp;TB";
				}
			}
		},

		/**
		 * This functions truncates a string that is longer then the given length and appends
		 * three dots to the end of the truncated string.
		 *
		 * @param {String} string
		 * @param {Number} length
		 * @return {string}
		 */
		truncate: function (string, length) {
			return string.length > length ? string.substr(0, length - 1) + '&hellip;' : string;
		}
	},

	/**
	 * File/Path methods
	 */
	File: {

		/**
		 * Get the extension from the filename.
		 *
		 * @param {String} filename
		 * @return {String} Extension string without dot
		 */
		getExtension: function (filename) {
			var i = filename.lastIndexOf('.');
			return (i < 0) ? '' : filename.substr(i + 1);
		},

		/**
		 * Get the icon class for the filetype.
		 *
		 * @param {String} filename
		 * @param {String} size Iconsize without px
		 * @return {String} Icon class
		 */
		getIconClass: function (filename, size) {
			if (!Ext.isDefined(size)) {
				size = "48";
			}
			var existingIcons = ["aac", "aif", "cda", "aiff", "mpa", "ogg", "wma", "mp3", "mid", "midi", "ai",
				"apk", "avi", "flv", "mp4", "mov", "mpeg", "wmv", "vob", "swf", "mkv", "m4v",
				"h264", "3g2", "3gp", "mpg", "bmp", "ico", "icns", "gif", "jpg", "jpeg", "png",
				"tif", "tiff", "tga", "c", "cpp", "cc", "h", "cs", "class", "swift", "hpp", "java",
				"js", "php", "asp", "aspx", "jsp", "rb", "qt", "py", "pyc", "sh", "tcsh", "ksh",
				"pl", "vbs", "css", "sass", "scss", "xml", "yaml", "htm", "html", "xhtml",
				"rss", "csv", "xls", "xlsm", "xlsx", "ods", "ots", "zip", "tgz", "gz", "7z",
				"lzma", "cab", "rar", "deb", "rpm", "z", "pkg", "arj", "doc", "docx", "dotx",
				"odt", "ott", "rtf", "wpd", "tex", "eps", "dwg", "dxf", "svg", "ics", "vcf", "iso",
				"vhd", "vhdx", "ova", "img", "bin", "dmg", "toast", "vcd", "pdf", "ps", "ppt",
				"pptx", "odp", "pps", "key", "psd", "sql", "mdb", "mde", "db", "sqlite", "txt",
				"log", "cfg", "conf", "ini", "tmp", "lnk", "wav", "eml", "emlx", "msg", "oft",
				"ost", "pst", "fnt", "fon", "otf", "ttf", "exe", "bat", "ps1", "cgi", "jar",
				"wsf", "com"];

			if (filename === "folder") {
				return "files" + size + "icon_folder";
			}

			var extension = this.getExtension(filename).toLowerCase();
			var exists = existingIcons.indexOf(extension);

			if (Ext.isEmpty(extension) || exists === -1) {
				return "files" + size + "icon_blank";
			}

			return "files" + size + "icon_" + extension;
		},

		/**
		 * Check if the filename (or foldername) includes unwanted characters.
		 *
		 * @param {String} filename
		 * @return {Boolean} true if filename is valid
		 */
		isValidFilename: function (filename) {

			// empty filenames are not allowed
			if (Ext.isEmpty(filename)) {
				return false;
			}

			// filename must not contain a slash
			if (filename.indexOf("/") !== -1) {
				return false;
			}

			// this symbols are not allowed in owncloud
			if (filename.indexOf("\\") !== -1
				|| filename.indexOf("<") !== -1
				|| filename.indexOf(">") !== -1
				|| filename.indexOf(":") !== -1
				|| filename.indexOf("'") !== -1
				|| filename.indexOf("|") !== -1
				|| filename.indexOf("?") !== -1
				|| filename.indexOf("*") !== -1
			) {
				return false;
			}

			return true;
		},

		/**
		 * Parse the account ID from the complete file ID.
		 * File ID might be something like "#R#89621e82/folder/file.png"
		 * so the account ID is "89621e82" then.
		 *
		 * @param fileid
		 * @return {string|null}
		 */
		getAccountId: function (fileid) {
			if (!Ext.isDefined(fileid)) {
				return null;
			}

			if(fileid.indexOf("/") === -1) {
				return null;
			}

			var startpos = 0;
			if (fileid.indexOf("#R#") == 0) {
				startpos = 3;
			}
			return fileid.substr(startpos, fileid.indexOf('/') - startpos);
		},

		/**
		 * Remove the account ID from the complete file ID.
		 * File ID might be something like "#R#89621e82/folder/file.png"
		 *
		 * @param fileid
		 * @return {string}
		 */
		stripAccountId: function (fileid) {
			if (!Ext.isDefined(fileid)) {
				return false;
			}

			if(fileid.indexOf("/") === -1) {
				return '#R#';
			}

			return fileid.substr(fileid.indexOf('/'));
		},

		/**
		 * Return the filename for the given path.
		 *
		 * @param path
		 * @returns {String}
		 */
		getFileName: function (path) {
			return path.replace(/^.*[\\\/]/, '');
		},

		/**
		 * Return the parent foldername for the given path.
		 *
		 * @param path
		 * @returns {String}
		 */
		getDirName: function (path) {
			return path.replace(/\\/g, '/').replace(/\/[^\/]*\/?$/, '');
		},

		getNewFileUrl: function (folderId) {
			var accId = Zarafa.plugins.files.data.Utils.File.getAccountId(folderId);
			var store = container.getCurrentContext().getAccountsStore();
			if(!accId || !store) {
				return false;
			}
			var account = store.getById(accId);
			if(!account) {
				return false;
			}
			const backendConfig = account.get("backend_config") || {};
			const { server_address, server_path, server_ssl } = backendConfig;
			var owncloudLocation = server_path.substr(1);
			owncloudLocation = owncloudLocation.substr(0, owncloudLocation.indexOf('/'));
			const url = (server_ssl ? 'https:' : 'http:') + '//' + server_address + "/" + owncloudLocation + "/index.php/apps/onlyoffice/ajax/new";
			return url;
		}
	},

	/**
	 * grommunio Web Core methods
	 */
	Core: {
		/**
		 * This will return the maximum upload size.
		 *
		 * @return {Number} Maximum upload size in bytes.
		 */
		getMaxUploadFilesize: function () {

			return container.getServerConfig().getMaxAttachmentSize(); // TODO: make it variable
		}
	},

	/**
	 * Validator methods
	 */
	Validator: {
		/**
		 * This filter should be applied to all action buttons. It will for example hide the button if
		 * a special functionality is not available.
		 *
		 * @param records
		 * @param singleSelectOnly
		 * @param fileOnly
		 * @param folderOnly
		 * @param noRoot
		 * @returns {boolean}
		 */
		actionSelectionVisibilityFilter: function (records, singleSelectOnly, fileOnly, folderOnly, noRoot) {

			if(!Ext.isDefined(records) || Ext.isEmpty(records)) {
				return false;
			}

			if (singleSelectOnly) {
				if (Ext.isArray(records) && records.length != 1) {
					return false;
				}
			}

			if (fileOnly || folderOnly || noRoot) {
				for (var i = 0; i < records.length; i++) {
					if ( 
						(fileOnly && records[i].get('type') == Zarafa.plugins.files.data.FileTypes.FOLDER) ||
						(folderOnly && records[i].get('type') !== Zarafa.plugins.files.data.FileTypes.FOLDER) ||
						(noRoot && records[i].get('filename') === '..')
					) {
						return false;
					}
				}
			}

			return true;
		}
	}
};
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.ViewModes
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different viewing modes of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.ViewModes = Zarafa.core.Enum.create({

	/**
	 * Don't show the preview panel
	 * @property
	 * @type Number
	 */
	NO_PREVIEW: 0,

	/**
	 * Show the preview panel to the right
	 * @property
	 * @type Number
	 */
	RIGHT_PREVIEW: 1,

	/**
	 * Show the preview panel in the bottom
	 * @property
	 * @type Number
	 */
	BOTTOM_PREVIEW: 2
});
Ext.namespace('Zarafa.plugins.files.data');

/**
 * @class Zarafa.plugins.files.data.Views
 * @extends Zarafa.core.Enum
 *
 * Enum containing the different views of the files context.
 *
 * @singleton
 */
Zarafa.plugins.files.data.Views = Zarafa.core.Enum.create({

	/**
	 * View all files items from the selected folder(s) in the 'list' view.
	 *
	 * @property
	 * @type Number
	 */
	LIST: 0,

	/**
	 * View all files items from the selected folder(s) in the 'icon' view.
	 *
	 * @property
	 * @type Number
	 */
	ICON: 1,

	/**
	 * View all store items in the 'account' view.
	 *
	 * @property
	 * @type Number
	 */
	ACCOUNT : 2
});
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.files.settings.SettingsAccountsWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype filesplugin.settingsaccountswidget
 *
 * The {@link Zarafa.settings.ui.SettingsWidget widget} for configuring
 * the general files options in the {@link Zarafa.files.settings.SettingsFilesCategory files category}.
 */
Zarafa.plugins.files.settings.SettingsAccountsWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title : _('Manage Accounts'),
			xtype : 'filesplugin.settingsaccountswidget',
			height: 400,
			layout: 'fit',
			items : [{
				xtype: "filesplugin.accountpanel",
				model : config.model,
				store : config.store
			}]
		});

		Zarafa.plugins.files.settings.SettingsAccountsWidget.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.settingsaccountswidget', Zarafa.plugins.files.settings.SettingsAccountsWidget);
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.plugins.files.settings.SettingsMainCategory
 * @extends Zarafa.settings.ui.SettingsCategory
 * @xtype filesplugin.settingsmaincategory
 *
 * The files category for users which will
 * allow the user to configure Files related settings
 */
Zarafa.plugins.files.settings.SettingsMainCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title        : _('Files'),
			categoryIndex: 1,
			iconCls      : 'icon_files_category',
			items        : [{
				xtype: 'filesplugin.settingsaccountswidget',
				model : config.model,
				store : config.store
			}, {
				xtype: 'filesplugin.settingsresetwidget'
			},
				container.populateInsertionPoint('context.settings.category.files', this)
			]
		});

		Zarafa.plugins.files.settings.SettingsMainCategory.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.settingsmaincategory', Zarafa.plugins.files.settings.SettingsMainCategory);
Ext.namespace('Zarafa.plugins.files.settings');

/**
 * @class Zarafa.plugins.files.settings.SettingsResetWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype filesplugin.settingsresetwidget
 *
 * The Reset Settings widget
 */
Zarafa.plugins.files.settings.SettingsResetWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * The loadMask object which will be shown when reset request is being sent to the server.
	 * @property
	 * @type Zarafa.common.ui.LoadMask
	 */
	loadMask: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			title : _('Reset Files settings'),
			layout: 'form',
			items : [{
				xtype    : 'displayfield',
				hideLabel: true,
				value    : _('Resets Files settings to their original defaults')
			}, {
				xtype  : 'button',
				text   : _('Reset Files settings'),
				width  : 150,
				handler: this.onResetSettings,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.SettingsResetWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Event handler when the "Reset Files Settings" button was clicked.
	 * This will {@link Zarafa.settings.SettingsModel#reset reset} the
	 * {@link Zarafa.settings.data.SettingsDefaultValue values} of the settings.
	 * @private
	 */
	onResetSettings: function () {
		var message = _('Are you sure to remove all settings and accounts?');
		message += '<br/>';
		message += '<i>' + _('Accounts marked by your administrator as "cannot be changed" will not be removed.') + '</i>';
		message += '<br/><br/>';
		message += _('grommunio Web will automatically restart in order for these changes to take effect.');
		message += '<br/>';

		Zarafa.common.dialogs.MessageBox.addCustomButtons({
			title       : _('Reset Files settings'),
			msg         : message,
			fn          : this.resetDefaultSettings,
			customButton: [{
				text: _('Reset'),
				name: 'reset'
			}, {
				text: _('Cancel'),
				name: 'cancel'
			}],
			scope       : this
		});

	},

	/**
	 * Event handler for {@link #onResetSettings}. This will check if the user
	 * wishes to reset the default settings or not.
	 * @param {String} button The button which user pressed.
	 * @private
	 */
	resetDefaultSettings: function (button) {
		if (button === 'reset') {
			var settingsModel = container.getSettingsModel();
			var accounts = settingsModel.getSettingsObject('zarafa/v1/plugins/files/accounts');
			for (var account in accounts) {
				if (accounts[account].cannot_change !== true) {
					settingsModel.reset('zarafa/v1/plugins/files/accounts/' + account);
				}
			}
			settingsModel.reset('zarafa/v1/contexts/files');
			settingsModel.save();

			this.loadMask = new Zarafa.common.ui.LoadMask(Ext.getBody(), {
				msg: '<b>' + _('grommunio Web is reloading, Please wait.') + '</b>'
			});

			this.loadMask.show();

			this.mon(settingsModel, 'save', this.onSettingsSave, this);
			this.mon(settingsModel, 'exception', this.onSettingsException, this);
		}

	},

	/**
	 * Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#save save}
	 * event to indicate the settings were successfully saved and it will forcefully reload grommunio Web.
	 * @param {Zarafa.settings.SettingsModel} model The model which fired the event.
	 * @param {Object} parameters The key-value object containing the action and the corresponding
	 * settings which were saved to the server.
	 * @private
	 */
	onSettingsSave: function (model, parameters) {
		if (parameters.action === Zarafa.core.Actions['reset']) {
			this.mun(model, 'save', this.onSettingsSave, this);
			this.mun(model, 'exception', this.onSettingsException, this);
			Zarafa.core.Util.reloadWebapp();
		}
	},

	/**
	 * Called when the {@link Zarafa.settings.SettingsModel} fires the {@link Zarafa.settings.SettingsModel#exception exception}
	 * event to indicate the settings were not successfully saved.
	 * @param {Zarafa.settings.SettingsModel} model The settings model which fired the event
	 * @param {String} type The value of this parameter will be either 'response' or 'remote'.
	 * @param {String} action Name of the action (see {@link Ext.data.Api#actions}).
	 * @param {Object} options The object containing a 'path' and 'value' field indicating
	 * respectively the Setting and corresponding value for the setting which was being saved.
	 * @param {Object} response The response object as received from the PHP-side
	 * @private
	 */
	onSettingsException: function (model, type, action, options, response) {
		if (options.action === Zarafa.core.Actions['reset']) {
			this.loadMask.hide();

			this.mun(model, 'save', this.onSettingsSave, this);
			this.mun(model, 'exception', this.onSettingsException, this);
		}
	}
});

Ext.reg('filesplugin.settingsresetwidget', Zarafa.plugins.files.settings.SettingsResetWidget);
Ext.namespace('Zarafa.plugins.files.settings.data');

/**
 * @class Zarafa.plugins.files.settings.data.AccountRenderUtil
 * @singleton
 *
 * This class offers some basic utils for rendering account specific values.
 */
Zarafa.plugins.files.settings.data.AccountRenderUtil = {
	/**
	 * Renderer for the status column of the accountgrid
	 * @param {Object} value The data value for the cell.
	 * @param {Object} p An object with metadata
	 * @param {Ext.data.Record} record The {Ext.data.Record} from which the data was extracted.
	 * @return {String} The formatted string
	 */
	statusRenderer: function (value, p, record) {
		switch (value) {
			case Zarafa.plugins.files.data.AccountRecordStatus.OK:
				p.css = 'zarafa-files-listview-icon zarafa-files-account-ok';
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.NEW:
				p.css = 'zarafa-files-listview-icon zarafa-files-account-new';
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.ERROR:
				p.css = 'zarafa-files-listview-icon zarafa-files-account-error';
				break;
			case Zarafa.plugins.files.data.AccountRecordStatus.UNKNOWN:
				p.css = 'zarafa-files-listview-icon zarafa-files-account-unknown';
				break;
			default:
				break;
		}

		// add extra css class for empty cell
		p.css += ' zarafa-grid-empty-cell';
		p.attr = 'ext:qtip="' + record.get('status_description') + '"';

		return '';
	},

	/**
	 * Renderer for the feature column of the accountgrid
	 * @param value
	 * @param metadata
	 * @param record
	 * @param rowIndex
	 * @param colIndex
	 * @param store
	 * @param feature
	 * @returns {string}
	 */
	featureRenderer: function (
		value,
		metadata,
		record,
		rowIndex,
		colIndex,
		store,
		feature,
	) {
		var availableFeatures = record.get('backend_features');

		// hide this feature if account does not support it
		if (
			!Ext.isDefined(availableFeatures) ||
			availableFeatures === null ||
			!Zarafa.plugins.files.settings.data.AccountRenderUtil.arrayContains(
				availableFeatures,
				feature,
			)
		) {
			return 'x-hide-display';
		}
		return 'zarafa-files-feature-spacer';
	},

	/**
	 * Renderer for the backend column of the accountgrid
	 * @param {String} value The value holds backend name.
	 * @returns {string} return HTML markups
	 */
	backendRenderer: function (value) {
		if (Ext.isEmpty(value)) {
			return '';
		}
		var backendKey = value === 'Owncloud' ? 'Default' : value;
		var backendName = backendKey;
		if (backendKey === 'Default') {
			backendName = _('Default');
		}
		return (
			'<span class="icon_16_' +
			backendKey +
			' files_backend_selector">&nbsp;</span>' +
			backendName
		);
	},

	/**
	 * Check if a array contains the needle
	 * @param array
	 * @param needle
	 * @returns {boolean}
	 */
	arrayContains: function (array, needle) {
		var i = array.length;
		while (i--) {
			if (array[i] === needle) {
				return true;
			}
		}
		return false;
	},
};
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountEditContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.accounteditcontentpanel
 */
Zarafa.plugins.files.settings.ui.AccountEditContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.accounteditcontentpanel',

			layout: 'fit',
			modal: true,
			width: 400,
			height: 250,
			stateful: false,
			title: _('Edit Account'),
			items: [{
				xtype: 'filesplugin.accounteditpanel',
				item: config.item,
				backendStore : config.backendStore
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountEditContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.accounteditcontentpanel', Zarafa.plugins.files.settings.ui.AccountEditContentPanel);Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountEditPanel
 * @extends Ext.Panel
 * @xtype filesplugin.accounteditpanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.AccountEditContentPanel AccountEditContentPanel}.
 */
Zarafa.plugins.files.settings.ui.AccountEditPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg {Object} The current loaded account record.
	 */
	currentItem: undefined,

	/**
	 * @constructor
	 * @param config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.currentItem = config.item;
		}

		this.backendStore = new Zarafa.plugins.files.data.BackendStore();
		this.currentBackend = this.normalizeBackendName(
			this.currentItem ? this.currentItem.get('backend') : null,
		);

		Ext.applyIf(config, {
			xtype: 'filesplugin.accounteditpanel',
			layout: 'anchor',
			autoScroll: true,
			border: false,
			items: this.createPanelItems(config),
			buttons: [
				{
					text: _('Save'),
					ref: '../saveBtn',
					cls: 'zarafa-action',
					handler: this.doSave,
					scope: this,
				},
				{
					text: _('Cancel'),
					handler: this.doClose,
					scope: this,
				},
			],
		});

		Zarafa.plugins.files.settings.ui.AccountEditPanel.superclass.constructor.call(
			this,
			config,
		);
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * If there is no existing record, this function will create a new @see Zarafa.plugins.files.data.AccountRecord
	 * and fill it with the form values.
	 * Afterwards the record will be saved to the server-backend.
	 */
	doSave: function () {
		var store = this.dialog.store;
		var accountName = this.dialog.accName;
		var accountBackend = this.normalizeBackendName(
			this.backendSelector
				? this.backendSelector.getValue()
				: this.currentBackend,
		);
		var metaFormItems = this.dialog.metaForm.form.items.items;
		var formValid = true;

		// build the configuration object from the formfields
		var backendConfig = {};

		// also do some sanity checks on the values
		Ext.each(metaFormItems, function (formRecord) {
			backendConfig[formRecord.getName()] = formRecord.getValue();

			// check if record is valid
			if (!formRecord.isValid()) {
				formValid = false;
				return false;
			}
		});

		if (formValid) {
			if (!this.currentItem) {
				// create new record if none exists yet
				this.currentItem = new store.recordType({
					id: -1,
					name: accountName.getValue(),
					status: Zarafa.plugins.files.data.AccountRecordStatus.NEW,
					backend: accountBackend,
					backend_config: backendConfig,
				});

				store.add(this.currentItem);
			} else {
				// edit the existing record
				this.currentItem.beginEdit();
				this.currentItem.set('name', accountName.getValue());
				this.currentItem.set(
					'status',
					Zarafa.plugins.files.data.AccountRecordStatus.NEW,
				);
				this.currentItem.set('backend', accountBackend);
				this.currentItem.set('backend_config', backendConfig);
				this.currentItem.endEdit();
			}

			// close the dialog after success.
			this.doClose();
		} else {
			// TODO: print error
		}
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.AccountEditPanel AccountEditPanel}.
	 *
	 * @param {Object} config
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function (config) {
		// default values
		var name = _('Files');

		if (Ext.isDefined(config.item)) {
			// set defaultvalues if available
			name = config.item.get('name');
		}

		var backendCombo = new Ext.form.ComboBox({
			xtype: 'combo',
			mode: 'local',
			triggerAction: 'all',
			editable: false,
			forceSelection: true,
			store: this.backendStore,
			displayField: 'displayName',
			valueField: 'name',
			fieldLabel: _('Backend'),
			labelAlign: 'top',
			allowBlank: false,
			ref: '../../../backendSelector',
			value: this.currentBackend,
			listeners: {
				select: this.onBackendSelectionChange,
				scope: this,
			},
		});

		this.backendStore.on(
			'load',
			function (store) {
				var backend = this.normalizeBackendName(this.currentBackend);
				var index = store.find('name', backend);
				var record = index >= 0 ? store.getAt(index) : store.getAt(0);
				if (record) {
					backendCombo.setValue(record.get('name'));
					this.currentBackend = record.get('name');
				}
				this.updateMetaFormBackend(this.currentBackend, true);
			},
			this,
		);

		return [
			{
				xtype: 'fieldset',
				checkboxToggle: false,
				title: _('Account Information'),
				defaultType: 'textfield',
				ref: 'accInfo',
				collapsed: false,
				items: [
					{
						xtype: 'panel',
						layout: 'form',
						border: false,
						flex: 1,
						defaults: {
							anchor: '100%',
							style: 'margin-bottom: 10px;',
						},
						defaultType: 'textfield',
						items: [
							{
								fieldLabel: _('Account name'),
								labelAlign: 'top',
								ref: '../../../accName',
								value: name,
								name: 'accountName',
							},
							backendCombo,
						],
					},
				],
			},
			{
				xtype: 'fieldset',
				checkboxToggle: false,
				title: _('Account Configuration'),
				autoHeight: true,
				defaultType: 'textfield',
				ref: 'metaInfo',
				collapsed: false,
				items: [
					{
						xtype: 'metaform',
						autoInit: true,
						method: 'GET',
						flex: 1,
						defaults: {
							anchor: '100%',
							style: 'margin-bottom: 10px;',
						},
						ref: '../../metaForm',
						url: this.buildMetaFormUrl(this.currentBackend),
						listeners: {
							actioncomplete: this.onMetaFormReady.createDelegate(this),
						},
					},
				],
			},
		];
	},

	/**
	 * Fired after meta data is processed and form fields are created.
	 */
	onMetaFormReady: function () {
		if (Ext.isDefined(this.item)) {
			var saveButton = this.saveBtn;

			// initialize metaform values
			this.dialog.metaForm.bindData(this.item.data.backend_config);

			// enable the save button
			saveButton.setText(_('Update', 'files_plugin'));
			saveButton.enable();
		} else {
			this.saveBtn.enable();
		}

		// FIXME: this is a workaround for IE 9, IE 10 and IE 11
		// chrome and ff will work without this re-layouting
		this.dialog.metaForm.on(
			'afterlayout',
			function () {
				this.dialog.metaForm.doLayout();
				var win = Ext.WindowMgr.getActive();
				win.setHeight(
					this.accInfo.getHeight() + this.metaInfo.getHeight() + 90,
				);
			},
			this,
			{ single: true },
		);
	},

	/**
	 * Normalize backend names for legacy configurations.
	 *
	 * @param {String|null} backendName
	 * @return {String}
	 */
	normalizeBackendName: function (backendName) {
		if (!backendName) {
			return 'Default';
		}
		if (backendName === 'Owncloud') {
			return 'Default';
		}
		return backendName;
	},

	/**
	 * Build the URL used to load backend configuration metadata.
	 *
	 * @param {String} backendName
	 * @return {String}
	 */
	buildMetaFormUrl: function (backendName) {
		return Ext.urlAppend(
			container.getBaseURL(),
			'load=custom&name=form&backend=' + backendName,
		);
	},

	/**
	 * Handle backend selection changes.
	 *
	 * @param {Ext.form.ComboBox} combo
	 */
	onBackendSelectionChange: function (combo) {
		var backend = this.normalizeBackendName(combo.getValue());
		this.currentBackend = backend;
		this.updateMetaFormBackend(backend, false);
	},

	/**
	 * Update the metaform configuration for the selected backend.
	 *
	 * @param {String} backend
	 * @param {Boolean} skipReload When true, avoid reloading the metaform (used during initialisation).
	 */
	updateMetaFormBackend: function (backend, skipReload) {
		var metaForm = this.dialog.metaForm;
		if (!metaForm) {
			return;
		}

		metaForm.url = this.buildMetaFormUrl(backend);
		if (skipReload) {
			return;
		}

		metaForm.load({
			url: metaForm.url,
			params: { meta: true },
		});
		if (this.saveBtn) {
			this.saveBtn.disable();
		}
	},
});

Ext.reg(
	'filesplugin.accounteditpanel',
	Zarafa.plugins.files.settings.ui.AccountEditPanel,
);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountGrid
 * @extends Ext.grid.GridPanel
 * @xtype filesplugin.accountgrid
 *
 * The main gridpanel for our account list.
 */
Zarafa.plugins.files.settings.ui.AccountGrid = Ext.extend(Zarafa.common.ui.grid.GridPanel, {

	/**
	 * @cfg {Object} The account store.
	 */
	store: null,

	/**
	 * @cfg {Zarafa.plugins.files.data.BackendStore} backendStore which
	 * contains {@link Zarafa.plugins.files.data.FilesBackendRecord backend} records.
	 */
	backendStore : undefined,

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.accountgrid',
			store : config.store,
			border : false,
			baseCls : 'accountGrid',
			enableHdMenu: false,
			loadMask : this.initLoadMask(),
			viewConfig : {
				forceFit : true,
				deferEmptyText : false,
				emptyText : '<div class="emptytext">' + _('No account created!') + '</div>'
			},
			sm  : this.initSelectionModel(),
			cm : this.initColumnModel(),
			listeners : {
				rowdblclick : this.onRowDblClick,
				scope : this
			},
			tbar : [{
				iconCls: 'filesplugin_icon_add',
				text   : _('Add Account'),
				ref    : '../addAccountBtn',
				handler: this.onAccountAdd,
				scope : this
			}, {
				iconCls : 'filesplugin_icon_delete',
				text : _('Remove Account'),
				disabled: true,
				ref : '../removeAccountBtn',
				handler : this.onAccountRemove,
				scope : this
			}, {
				xtype : 'spacer',
				width : 10
			}, {
				xtype : 'button',
				iconCls : 'zarafa-rules-sequence-up',
				disabled : true,
				ref : '../upButton',
				handler : this.onAccountSequenceUp,
				scope : this,
				width : 20
			}, {
				xtype : 'spacer',
				width : 10
			}, {
				xtype : 'button',
				iconCls : 'zarafa-rules-sequence-down',
				disabled : true,
				ref : '../downButton',
				handler : this.onAccountSequenceDown,
				scope : this,
				width : 20
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountGrid.superclass.constructor.call(this, config);
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.loadMask} field.
	 *
	 * @return {Ext.LoadMask} The configuration object for {@link Ext.LoadMask}
	 * @private
	 */
	initLoadMask: function ()
	{
		return {
			msg: _('Loading accounts') + '...'
		};
	},

	/**
	 * Initialize event handlers
	 * @private
	 */
	initEvents : function ()
	{
		this.on('afterrender', this.onAfterRender, this);
	},

	/**
	 * Event handler triggered after rendering account grid.
	 * which reload the account store so we can get updated status
	 * for all configured accounts.
	 */
	onAfterRender : function ()
	{
		this.store.reload();
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.sm SelectionModel} field.
	 *
	 * @return {Ext.grid.RowSelectionModel} The subclass of {@link Ext.grid.AbstractSelectionModel}
	 * @private
	 */
	initSelectionModel: function () {
		return new Ext.grid.RowSelectionModel({
			singleSelect: true,
			listeners   : {
				selectionchange: this.onRowSelected
			}
		});
	},

	/**
	 * Initialize the {@link Ext.grid.GridPanel.cm ColumnModel} field.
	 *
	 * @return {Ext.grid.ColumnModel} The {@link Ext.grid.ColumnModel} for this grid
	 * @private
	 */
	initColumnModel: function () {
		return new Zarafa.plugins.files.settings.ui.AccountGridColumnModel();
	},

	/**
	 * Function is called if a row in the grid gets selected.
	 *
	 * @param selectionModel
	 */
	onRowSelected: function (selectionModel) {
		if (selectionModel.getCount() < 1) {
			return;
		}
		var remButton = this.grid.removeAccountBtn;
		var isAdministrativeAccount = selectionModel.getSelections()[0].get("cannot_change");
		remButton.setDisabled(isAdministrativeAccount);

		this.grid.upButton.setDisabled(!selectionModel.hasPrevious());
		this.grid.downButton.setDisabled(!selectionModel.hasNext());
	},

	/**
	 * Function is called if a row in the grid gets double clicked.
	 *
	 * @param grid
	 * @param rowIndex
	 */
	onRowDblClick: function (grid, rowIndex)
	{
		var accountStore = this.getStore();
		var accountRecord = accountStore.getAt(rowIndex);
		if (accountRecord.get("cannot_change")) {
			return;
		}
		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['filesplugin.accountedit'], undefined, {
			store  : grid.getStore(),
			item   : grid.getStore().getAt(rowIndex),
			backendStore : this.backendStore,
			manager: Ext.WindowMgr
		});
	},

	/**
	 * Clickhandler for the "add account" button.
	 */
	onAccountAdd: function ()
	{
		var component = Zarafa.core.data.SharedComponentType['filesplugin.accountedit'];
		Zarafa.core.data.UIFactory.openLayerComponent(component, undefined, {
			store  : this.getStore(),
			backendStore : this.backendStore,
			modal : true
		});
	},

	/**
	 * Clickhandler for the "remove account" button.
	 */
	onAccountRemove: function ()
	{
		var selections = this.getSelectionModel().getSelections();

		// Warn user before deleting the account!
		if (Ext.isDefined(selections[0])) { // as we have single select, remove the first item
			Ext.MessageBox.confirm(
				_('Confirm deletion'),
				String.format(_('Do you really want to delete the account "{0}"?'), selections[0].get("name")),
				this.doRemove.createDelegate(this, [selections[0]], true),
				this
			);
		}
	},

	/**
	 * Actually removes the given account from the backend.
	 *
	 * @param {String} button
	 * @param {String} value Unused
	 * @param {Object} options Unused
	 * @param {AccountRecord} account
	 */
	doRemove: function (button, value, options, account) {
		if (button === "yes") {
			this.getStore().remove(account);
		}
	},

	/**
	 * Handler function will be called when user clicks on 'Up' button
	 * This will determine which accounts to swap and call {@link #swapAccounts}.
	 * @private
	 */
	onAccountSequenceUp : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var account = sm.getSelected();

		/*
		 * Start looking for the first sequence number which is lower then
		 * the current sequence number. Note that we want the account_sequence
		 * which is closest to the current account_sequence, hence the account:
		 *    account.get('account_sequence') > record.get('account_sequence') > swapAccount.get('account_sequence')
		 */
		var swapAccount;
		store.each(function(record) {
			if (account.get('account_sequence') > record.get('account_sequence')) {
				if (!swapAccount || record.get('account_sequence') > swapAccount.get('account_sequence')) {
					swapAccount = record;
				}
			}
		}, this);

		this.swapAccounts(account, swapAccount);
	},

	/**
	 * Handler function will be called when user clicks on 'Down' button
	 * This will determine which accounts to swap and call {@link #swapAccounts}.
	 * @private
	 */
	onAccountSequenceDown : function()
	{
		var store = this.getStore();
		var sm = this.getSelectionModel();
		var account = sm.getSelected();

		/*
		 * Start looking for the first sequence number which is higher then
		 * the current sequence number. Note that we want the account_sequence
		 * which is closest to the current account_sequence, hence the account:
		 *    account.get('account_sequence') < record.get('account_sequence') < swapAccount.get('account_sequence')
		 */
		var swapAccount;
		store.each(function(record) {
			if (account.get('account_sequence') < record.get('account_sequence')) {
				if (!swapAccount || record.get('account_sequence') < swapAccount.get('account_sequence')) {
					swapAccount = record;
				}
			}
		}, this);

		this.swapAccounts(account, swapAccount);
	},

	/**
	 * Swap two accounts by changing the 'account_sequence' property
	 * for both accounts, and {@link Ext.data.Store#sort sort}
	 * the {@link #store}.
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 * @private
	 */
	swapAccounts : function(a, b)
	{
		var aSeq = parseInt(a.get('account_sequence'));
		var bSeq = parseInt(b.get('account_sequence'));

		// Disable UI buttons to prevent race conditions
		this.upButton.setDisabled(true);
		this.downButton.setDisabled(true);

		// Swap the 2 accounts
		this.store.suspendEvents(); // do not use the autoSave feature of the store
		a.set('account_sequence', bSeq);
		b.set('account_sequence', aSeq);
		this.store.resumeEvents();

		// store both accounts in one request
		this.store.save(this.store.getModifiedRecords());

		// Reapply the sorting, this will update the UI
		this.store.on('update', this.onAfterSequenceChanged.createDelegate(this, [a,b], true), null, {single: true});
	},

	/**
	 * Eventhandler, called after the store has been updated.
	 * This will reload the store to update the ordering.
	 *
	 * @param store
	 * @param record
	 * @param operation
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 */
	onAfterSequenceChanged : function(store, record, operation, a, b)
	{
		// Reapply the sorting, this will update the UI
		store.reload();
		// Update the UI when the store has been reloaded
		store.on('load', this.onAfterSequenceReload.createDelegate(this, [a,b], true), this, {single: true});
	},

	/**
	 * This updates the UI after the store has been reloaded.
	 *
	 * @param store
	 * @param record
	 * @param operation
	 * @param {Zarafa.plugins.files.data.AccountRecord} a The first account
	 * @param {Zarafa.plugins.files.data.AccountRecord} b The second account
	 */
	onAfterSequenceReload: function(store, record, operation, a, b)
	{
		 // Update the 'up'/'down' button
		var sm = this.getSelectionModel();
		this.upButton.setDisabled(!sm.hasPrevious());
		this.downButton.setDisabled(!sm.hasNext());

		// fire the reorder event
		store.fireEvent('reorder', a, b);
	}
});

Ext.reg('filesplugin.accountgrid', Zarafa.plugins.files.settings.ui.AccountGrid);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountGridColumnModel
 * @extends Zarafa.common.ui.grid.ColumnModel
 */
Zarafa.plugins.files.settings.ui.AccountGridColumnModel = Ext.extend(Zarafa.common.ui.grid.ColumnModel, {

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			columns : this.createDefaultColumns(),
			defaults: {
				sortable: true
			}
		});

		Zarafa.plugins.files.settings.ui.AccountGridColumnModel.superclass.constructor.call(this, config);
	},

	/**
	 * Create an array of {@link Ext.grid.Column columns} which must be visible within
	 * the default view of this {@link Ext.grid.ColumnModel ColumnModel}.
	 *
	 * @return {Ext.grid.Column[]} The array of columns
	 * @private
	 */
	createDefaultColumns: function () {
		return [
			new Ext.grid.RowNumberer(),
			{
				header   : _('ID'),
				dataIndex: 'id',
				width    : 50,
				hidden   : true,
				sortable : false,
				tooltip  : _('Sort by: ID')
			}, {
				header   : _('Status'),
				dataIndex: 'status',
				width    : 40,
				sortable : false,
				renderer : Zarafa.plugins.files.settings.data.AccountRenderUtil.statusRenderer,
				tooltip  : _('Sort by: Status')
			}, {
				header   : _('Name'),
				dataIndex: 'name',
				flex     : 1,
				sortable : false,
				tooltip  : _('Sort by: Name')
			}, {
				header   : _('Backend'),
				dataIndex: 'backend',
				width    : 40,
				sortable : false,
				renderer : Zarafa.plugins.files.settings.data.AccountRenderUtil.backendRenderer,
				tooltip  : _('Sort by: Backend')
			}, {
				xtype    : 'actioncolumn',
				header   : _('Features'),
				dataIndex: 'backend_features',
				width    : 40,
				sortable : false,
				tooltip  : _('Shows all available features of the backend.'),
				items    : [{
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.QUOTA], true),
					icon    : 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPScxNicgaGVpZ2h0PScxNicgdmlld0JveD0iMCAwIDEzNS40MiAxMjIuODgiPjxwYXRoIGQ9Ik02NS42MiwxNC4wOEg4NS44NWEyLDIsMCwwLDEsMiwyVjk1LjU2YTIsMiwwLDAsMS0yLDJINjUuNjJhMiwyLDAsMCwxLTItMlYxNmEyLDIsMCwwLDEsMi0yWm02OS44LDEwOC44SDkuOTN2MEE5Ljg5LDkuODksMCwwLDEsMCwxMTNIMFYwSDEyLjY5VjExMC4xOUgxMzUuNDJ2MTIuNjlaTTEwMy4wNSw1My44aDIwLjIzYTIsMiwwLDAsMSwyLDJWOTUuNTZhMiwyLDAsMCwxLTIsMkgxMDMuMDVhMiwyLDAsMCwxLTItMlY1NS43NWEyLDIsMCwwLDEsMi0yWk0yOC4xOSwyOS40NEg0OC40MmEyLDIsMCwwLDEsMS45NSwxLjk1Vjk1LjU2YTIsMiwwLDAsMS0xLjk1LDJIMjguMTlhMiwyLDAsMCwxLTItMlYzMS4zOWEyLDIsMCwwLDEsMi0xLjk1WiIgZmlsbD0iIzZkNmQ3MCIvPjwvc3ZnPgo=',
					tooltip : _('Show quota information'),
					handler : this.showDialog.createDelegate(this, ['filesplugin.featurequotainfo'], 2)
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.VERSION_INFO], true),
					icon    : 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPScxNicgaGVpZ2h0PScxNicgdmlld0JveD0iMCAwIDEyMi44OCAxMjIuODgiPjxwYXRoIGQ9Ik02MS40NCA5LjIzbC01LjM1LjI1LTUuMTcuNzdjLTEuNzIuMzQtMy4zOS43Ny01IDEuMjYtMS41OS40OS0zLjE2IDEuMDctNC43IDEuNzJsLS4xLjA1aDBsLTQuNjcgMi4yNS00LjMxIDIuNi0zLjk5IDIuOTctMy42OCAzLjM1LS4wNC4wNC0zLjMzIDMuNjYtMi45NyAzLjk5Yy0uOTIgMS4zNi0xLjc4IDIuOC0yLjU5IDQuM2wtMi4yNiA0LjY3LS4wNS4xMWgwbC0uMDcuMTRjLS42MyAxLjUtMS4xOSAzLjA0LTEuNjcgNC42MS0uNDkgMS41OS0uOSAzLjIzLTEuMjQgNC45My0uMzUgMS43My0uNiAzLjQ1LS43NyA1LjE3YTU0LjM4IDU0LjM4IDAgMCAwLS4yNSA1LjM1bC4yNSA1LjM1Ljc3IDUuMTcgMS4yNyA1IDEuNyA0LjY2Yy4wNC4wNy4wNy4xNC4xLjIxbDIuMjMgNC42IDIuNTkgNC4zIDIuOTggNCAzLjI2IDMuNTljLjA3LjA2LjEzLjEyLjE5LjE5bDMuNTkgMy4yNiAzLjk5IDIuOTdjMS4zNi45MiAyLjggMS43OCA0LjMgMi41OWw0LjY3IDIuMjYuMjEuMWE0OC44OSA0OC44OSAwIDAgMCA0LjY3IDEuNjljMS41OC40OSAzLjIzLjkgNC45MiAxLjI0IDEuNzMuMzUgMy40NS42IDUuMTcuNzdhNTQuMzggNTQuMzggMCAwIDAgNS4zNS4yNWw1LjM1LS4yNSA1LjE3LS43NyA1LTEuMjdhNDguNDQgNDguNDQgMCAwIDAgNC43MS0xLjczbC4wOS0uMDR2LS4wMWw0LjY3LTIuMjUgNC4zMS0yLjYgMy45OS0yLjk3IDMuNjItMy4yOS4xOC0uMTggMy4yNS0zLjU4IDIuOTgtNGMuOTItMS4zNiAxLjc4LTIuOCAyLjU5LTQuM2wyLjI2LTQuNjcuMS0uMjJhNDguNTEgNDguNTEgMCAwIDAgMS42OS00LjY1Yy40OS0xLjU5LjktMy4yMyAxLjI0LTQuOTMuMzUtMS43My42LTMuNDUuNzctNS4xN2E1NC4zOCA1NC4zOCAwIDAgMCAuMjUtNS4zNWwtLjI1LTUuMzUtLjc3LTUuMTctMS4yNy01YTQ4LjQ0IDQ4LjQ0IDAgMCAwLTEuNzMtNC43MWwtLjA0LS4wOWgwbC0yLjI1LTQuNjctMi41OS00LjMtMi45Ny0zLjk5LTMuMjgtMy42MWMtLjA3LS4wNi0uMTMtLjEyLS4xOC0uMThsLTMuNTktMy4yNi0zLjk5LTIuOTctNC4zLTIuNTktNC42Ny0yLjI1LS4xOS0uMDljLTEuNTItLjY0LTMuMDgtMS4yMS00LjY4LTEuNy0xLjU4LS40OS0zLjIzLS45LTQuOTMtMS4yNC0xLjczLS4zNS0zLjQ1LS42LTUuMTctLjc3bC01LjM3LS4yMmgwem0xLjkgMjEuOTRjMi4xOSAwIDMuOTEuNjMgNS4xNiAxLjg4IDEuMjQgMS4yNSAxLjg3IDIuOTYgMS44NyA1LjE3IDAgMi4yNC0xLjA3IDQuMjItMy4yMSA1Ljk3LTIuMTYgMS43NC00LjY2IDIuNjItNy41MiAyLjYyLTIuMTQgMC0zLjg1LS42LTUuMTYtMS44LTEuMzEtMS4yMi0xLjk2LTIuNzktMS45Ni00Ljc1IDAtMi40NyAxLjA3LTQuNTkgMy4xOS02LjQgMi4xMS0xLjggNC42Ni0yLjY5IDcuNjMtMi42OWgwIDB6bTEyLjIxIDYwLjU0SDQ3LjMzdi00Ljc1aDUuM1Y1Ny45NWgtNS4zdi0zLjc4aDEzLjE5YzMuMzMgMCA2LjYxLS40IDkuODUtMS4yMnYzNC4wMWg1LjE4djQuNzVoMCAwek00OS4xMiAxLjJDNTEuMDguODEgNTMuMTEuNTEgNTUuMi4zMWE2Mi4zNyA2Mi4zNyAwIDAgMSAxMi40OCAwbDYuMDguODlhNTkuMSA1OS4xIDAgMCAxIDUuODIgMS40OGMxLjg4LjU4IDMuNzQgMS4yNiA1LjU4IDIuMDRsLjE5LjA3aDBsLjA2LjAyIDUuMzggMi42MSA1LjA3IDMuMDcgNC43MyAzLjUyYTU2LjE3IDU2LjE3IDAgMCAxIDQuMzQgMy45NWwzLjk1IDQuMzQgMy41MiA0LjczIDMuMDYgNS4wNmgwIC4wMWwyLjYxIDUuMzkuMDYuMTVhNTguNTggNTguNTggMCAwIDEgMi4wNSA1LjZjLjYxIDEuOTYgMS4xMSAzLjkyIDEuNSA1Ljg5LjM5IDEuOTYuNjkgMy45OS44OSA2LjA4bC4zIDYuMjQtLjMxIDYuMjRjLS4yIDIuMDktLjUgNC4xMi0uODkgNi4wOGE1OS4xIDU5LjEgMCAwIDEtMS40OCA1LjgyIDYwLjIzIDYwLjIzIDAgMCAxLTIuMDUgNS42MmwtLjA0LjA5aDBsLS4wNS4xMS0yLjYxIDUuMzgtMy4wNyA1LjA3LTMuNTIgNC43M2E1OC4wOCA1OC4wOCAwIDAgMS0zLjk1IDQuMzRsLTQuMzUgMy45NS00LjcyIDMuNTEtNS4wNiAzLjA2LTUuMzkgMi42Mi0uMTYuMDZhNTguNTggNTguNTggMCAwIDEtNS42IDIuMDUgNTcuNjIgNTcuNjIgMCAwIDEtNS44OCAxLjVjLTEuOTYuMzktMy45OS42OS02LjA4Ljg5bC02LjI0LjNhNjUuNDcgNjUuNDcgMCAwIDEtNi4yNC0uM2wtNi4wOC0uODlhNTkuMSA1OS4xIDAgMCAxLTUuODItMS40OGMtMS44OS0uNTgtMy43Ni0xLjI3LTUuNjEtMi4wNWwtLjItLjA4aDBsLTUuMzgtMi42MS01LjA3LTMuMDdhNjAuMTEgNjAuMTEgMCAwIDEtNC43Mi0zLjUxbC00LjM1LTMuOTUtMy45NS00LjM0LTMuNTItNC43My0zLjA2LTUuMDYtMi42Mi01LjQtLjA2LS4xNWE1OC41OCA1OC41OCAwIDAgMS0yLjA1LTUuNmMtLjYxLTEuOTYtMS4xMS0zLjkzLTEuNS01Ljg5cy0uNjktMy45OS0uODktNi4wOEMuMSA2NS41OSAwIDYzLjUxIDAgNjEuNDRhNjIuMzcgNjIuMzcgMCAwIDEgLjMxLTYuMjRjLjItMi4wOS41LTQuMTIuODktNi4wOGE1OS4xIDU5LjEgMCAwIDEgMS40OC01LjgyIDU5LjYgNTkuNiAwIDAgMSAyLjA0LTUuNTlsLjA5LS4yM2gwbDIuNjEtNS4zOCAzLjA3LTUuMDcgMy41Mi00LjczYTU4LjA4IDU4LjA4IDAgMCAxIDMuOTUtNC4zNGw0LjM0LTMuOTUgNC43My0zLjUyIDUuMDctMy4wNy4xMy0uMDYgNS4yNi0yLjU1LjE1LS4wNmE1OC41OCA1OC41OCAwIDAgMSA1LjYtMi4wNWMxLjk1LS42MSAzLjkxLTEuMTEgNS44OC0xLjVoMHoiIGZpbGw9IiM2ZDZkNzAiLz48L3N2Zz4K',
					tooltip : _('Show version information'),
					handler : this.showDialog.createDelegate(this, ['filesplugin.featureversioninfo'], 2)
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.SHARING], true),
					icon    : 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPScxMicgaGVpZ2h0PScxMicgdmlld0JveD0iMCAwIDEyMi44OCAxMjAuOTQiPjxwYXRoIGQ9Ik05OC4xMSAwQTI0Ljc3IDI0Ljc3IDAgMSAxIDgwLjYgNDIuMjhsLS42NC0uNjctMzEuMTQgMTNhMjUgMjUgMCAwIDEgLjUzIDguOTVMODEgNzguMzFBMjQuNjYgMjQuNjYgMCAxIDEgNzQuNzIgODhMNDUuMzQgNzQuMjZBMjQuNzcgMjQuNzcgMCAxIDEgNDIuMjggNDNsMS4yNyAxLjM3IDMwLjc0LTEyLjgyQTI0Ljc3IDI0Ljc3IDAgMCAxIDk4LjExIDB6IiBmaWxsPSIjNmQ2ZDcwIi8+PC9zdmc+Cg==',
					tooltip : _('Share files')
				}, {
					getClass: Zarafa.plugins.files.settings.data.AccountRenderUtil.featureRenderer.createDelegate(this, [Zarafa.plugins.files.data.AccountRecordFeature.STREAMING], true),
					icon    : 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPScxNicgaGVpZ2h0PScxNicgdmlld0JveD0iMCAwIDEyMi44OCA4MC40NSI+PHBhdGggZD0iTTYxLjM4IDUyYTE4LjI1IDE4LjI1IDAgMCAxLTMuMTItLjI2IDEyLjM2IDEyLjM2IDAgMCAxLTQuNDQtMi4yOSAxMi40NSAxMi40NSAwIDAgMS0uODYtLjc3IDExLjkxIDExLjkxIDAgMCAxLTMuNS04LjUgMTIuMTIgMTIuMTIgMCAwIDEgMy41LTguNSAxMS45MSAxMS45MSAwIDAgMSA4LjUtMy41IDEyLjEyIDEyLjEyIDAgMCAxIDguNSAzLjUgMTIuMDUgMTIuMDUgMCAwIDEgMi4zNSAxMy42M0ExMS4zNyAxMS4zNyAwIDAgMSA2MS4zOCA1MnpNMjMuNjcgNzIuNDFhNC44NiA0Ljg2IDAgMCAxIDEuMTYgMy40MyA0Ljk0IDQuOTQgMCAwIDEtMS41NiAzLjMxbC0uMTUuMTNhNC44MiA0LjgyIDAgMCAxLTMuNDQgMS4xNiA0LjkgNC45IDAgMCAxLTMuMjktMS41NWwtLjE2LS4yQTY5Ljg3IDY5Ljg3IDAgMCAxIDQgNTkuNzQgNDguMjkgNDguMjkgMCAwIDEgMCA0MGE0OC43NSA0OC43NSAwIDAgMSA0LjY0LTE5LjcxQTcwLjA3IDcwLjA3IDAgMCAxIDE3LjU4IDEuNjJsLjEzLS4xM0E1IDUgMCAwIDEgMjEgLjA5YTQuNzkgNC43OSAwIDAgMSAzLjQ4IDEuMzNsLjExLjEyQTQuOTMgNC45MyAwIDAgMSAyNiA0LjgyYTQuNzggNC43OCAwIDAgMS0xLjMzIDMuNDdsLS4wNy4wOGE2MS41OSA2MS41OSAwIDAgMC0xMSAxNS43OCAzOC45MSAzOC45MSAwIDAgMC0zLjg1IDE2QTM5LjQ4IDM5LjQ4IDAgMCAwIDEzIDU2LjI1IDYwLjM2IDYwLjM2IDAgMCAwIDIzLjI1IDcyYTEuNTQgMS41NCAwIDAgMSAuMjkuMjNsLjEzLjE0em0xOC4yNi0xNC42NWE0Ljg3IDQuODcgMCAwIDEgLjkzIDMuMzkgNC43NSA0Ljc1IDAgMCAxLTEuNzUgMy4yNGwtLjE5LjE1YTQuOSA0LjkgMCAwIDEtMy4zOS45MyA0Ljc1IDQuNzUgMCAwIDEtMy4yNS0xLjc1bC0uMDktLjExYTQyLjg0IDQyLjg0IDAgMCAxLTYuNzEtMTEuMzUgMzMuNzQgMzMuNzQgMCAwIDEtMi4zNS0xMi4xMyAzMi42MSAzMi42MSAwIDAgMSAyLjI1LTEyIDQyLjEgNDIuMSAwIDAgMSA2Ljc0LTExLjM2IDQuODUgNC44NSAwIDAgMSA3LjU2IDYuMDh2LjA1YTMyLjIzIDMyLjIzIDAgMCAwLTUuMSA4LjUxIDIyLjc3IDIyLjc3IDAgMCAwIDAgMTcuNDEgMzMuODQgMzMuODQgMCAwIDAgNS4yIDguNzJsLjE3LjI1em02NC41OSAyMS4xYTQuOTMgNC45MyAwIDAgMS0zLjMgMS41MyA0LjcxIDQuNzEgMCAwIDEtMy41LTEuMjMgNC44NSA0Ljg1IDAgMCAxLTEuNjItMy4zNWgwYTQuNzcgNC43NyAwIDAgMSAxLjIzLTMuNDdsLjA2LS4wN2E2MCA2MCAwIDAgMCAxMC40Ni0xNkEzOS41IDM5LjUgMCAwIDAgMTEzLjE4IDQwYTM4LjkxIDM4LjkxIDAgMCAwLTMuODYtMTZBNjAuOTIgNjAuOTIgMCAwIDAgOTguMjUgOC4yNGwtLjEyLS4xM2E0Ljc5IDQuNzkgMCAwIDEtMS4yNC0zLjM4IDUgNSAwIDAgMSAxLjQ5LTMuMzdsLjEzLS4xMkE0Ljc5IDQuNzkgMCAwIDEgMTAxLjg5IDBhNSA1IDAgMCAxIDMuMzcgMS40OWwuMTIuMTJhNzAuNTggNzAuNTggMCAwIDEgMTIuODcgMTguNTkgNDguODMgNDguODMgMCAwIDEgNC42MiAxOS42OCA0OC42NyA0OC42NyAwIDAgMS00IDE5LjgxIDY5LjQyIDY5LjQyIDAgMCAxLTEyIDE4LjY4IDEuOTQgMS45NCAwIDAgMS0uMzQuNDdoMHpNODguMzUgNjQuMDVhNC44NSA0Ljg1IDAgMCAxLTguMjgtMi44OSA0Ljg0IDQuODQgMCAwIDEgMS4wNS0zLjU2di0uMDVhMzMgMzMgMCAwIDAgNS4xNy04LjYzIDIzLjEzIDIzLjEzIDAgMCAwIDAtMTcuNDh2LS4wOGEzMS45IDMxLjkgMCAwIDAtNS4xLTguNDRsLS4xNS0uMThhNC44OSA0Ljg5IDAgMCAxLS45Mi0zLjM4IDQuNzEgNC43MSAwIDAgMSAxLjc1LTMuMjRMODIgMTZhNC45MSA0LjkxIDAgMCAxIDMuNDgtMSA0LjggNC44IDAgMCAxIDMuMjUgMS43NmwuMDcuMDlhNDEuNjQgNDEuNjQgMCAwIDEgNi42OSAxMS4zIDMyLjQ3IDMyLjQ3IDAgMCAxIDIuMjYgMTIgMzMuNTkgMzMuNTkgMCAwIDEtMi4zNSAxMi4xIDQzLjM3IDQzLjM3IDAgMCAxLTYuNzcgMTEuNDUgMi40NyAyLjQ3IDAgMCAxLS4yOC4yOHoiIGZpbGw9IiM2ZDZkNzAiLz48L3N2Zz4K',
					tooltip : _('Fast Down/Upload')
				}]
			}];
	},

	/**
	 * This method gets called if the user clicks on the quota icon.
	 * It will then display the {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel quota panel}.
	 *
	 * @param {Object} grid the grid
	 * @param {Number} rowIndex used to retrieve the selected record
	 * @param {String} componentType the component to show
	 */
	showDialog: function(grid, rowIndex, componentType) {
		var record = grid.getStore().getAt(rowIndex);
		if (record.get('status') !== 'ok') {
			return;
		}

		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType[componentType], undefined, {
			store  : grid.getStore(),
			item   : record,
			manager: Ext.WindowMgr
		});
	}
});
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.AccountPanel
 * @extends Ext.grid.GridPanel
 * @xtype filesplugin.accountpanel
 * The main gridpanel for our data
 */
Zarafa.plugins.files.settings.ui.AccountPanel = Ext.extend(Ext.Panel, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config)
	{
		config = Ext.applyIf(config || {}, {
			border: false,
			layout: 'fit',
			items : [{
				xtype: "filesplugin.accountgrid",
				backendStore : config.model.backendStore,
				store : config.store,
				flex : 1
			}]
		});

		Zarafa.plugins.files.settings.ui.AccountPanel.superclass.constructor.call(this, config);
	}
});
Ext.reg('filesplugin.accountpanel', Zarafa.plugins.files.settings.ui.AccountPanel);Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.featurequotainfocontentpanel
 */
Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param {Object} config configuration object with the account.
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.featurequotainfocontentpanel',
			title     : _('Quota Information'),
			statefull : false,
			width     : 200,
			autoHeight: true,
			items     : [{
				xtype: 'filesplugin.featurequotainfopanel',
				item : config.item
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.featurequotainfocontentpanel', Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel
 * @extends Ext.Panel
 * @xtype filesplugin.featurequotainfopanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoContentPanel FeatureQuotaInfoContentPanel}.
 */
Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Object} The current loaded account record.
	 */
	account: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.account = config.item;
		}

		Ext.applyIf(config, {
			xtype      : 'filesplugin.featurequotainfopanel',
			items      : this.createPanelItems(),
			buttons    : [{
				text   : _('Reload'),
				handler: this.doReload.createDelegate(this),
				scope  : this
			}, {
				text   : _('Close'),
				handler: this.doClose,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel.superclass.constructor.call(this, config);

		this.doReload();
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * Reload the quota store.
	 */
	doReload: function () {
		var responseHandler = new Zarafa.core.data.AbstractResponseHandler({
			doGetquota: this.gotQuotaValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getquota',
			{
				accountId: this.account.get("id"),
				folder   : "/"
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It sets the quota information in the form panel.
	 *
	 * @param {Object} response object from the server
	 */
	gotQuotaValues: function (response) {
		if (!this.formPanel) {
			return;
		}

		var used = parseInt(response["quota"][0].amount);
		var available = parseInt(response["quota"][1].amount);
		// Backend sometimes returns a negative value for available data, set it to zero.
		if (used < 0 ) {
			used = 0;
		}

		if (available < 0) {
			available = 0;
		}

		this.formPanel.getForm().setValues([
			{ id: 'usedField', value: Ext.util.Format.fileSize(used) },
			{ id: 'availableField', value: Ext.util.Format.fileSize(available) },
			{ id: 'totalField', value: Ext.util.Format.fileSize(available + used) }
		]);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel FeatureQuotaInfoPanel}.
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function () {
		return [{
			xtype: 'form',
			border: false,
			ref: 'formPanel',
			labelAlign: 'left',
			items: [{
				xtype     : 'displayfield',
				name      : 'usedField',
				fieldLabel: _('Used'),
				value     : _('Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				name      : 'availableField',
				fieldLabel: _('Free'),
				value     : _('Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				name      : 'totalField',
				fieldLabel: _('Total'),
				value     : _('Loading') + '&hellip;'
			}]
		}];
	}
});

Ext.reg('filesplugin.featurequotainfopanel', Zarafa.plugins.files.settings.ui.FeatureQuotaInfoPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.featureversioninfocontentpanel
 */
Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype: 'filesplugin.featureversioninfocontentpanel',
			stateful : false,
			title     : _('Version Information'),
			width      : 300,
			height     : 100,
			items     : [{
				xtype: 'filesplugin.featureversioninfopanel',
				item : config.item
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.featureversioninfocontentpanel', Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel);
Ext.namespace('Zarafa.plugins.files.settings.ui');

/**
 * @class Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel
 * @extends Ext.Panel
 * @xtype filesplugin.featureversioninfopanel
 *
 * Will generate UI for {@link Zarafa.plugins.files.settings.ui.FeatureVersionInfoContentPanel FeatureVersionInfoContentPanel}.
 */
Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Object} The current loaded account record.
	 */
	account: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure.
	 */
	constructor: function (config) {
		config = config || {};

		if (config.item) {
			this.account = config.item;
		}

		Ext.applyIf(config, {

			xtype      : 'filesplugin.featureversioninfopanel',
			items      : this.createPanelItems(config),
			buttons    : [{
				text   : _('Close'),
				handler: this.doClose,
				scope  : this
			}]
		});

		Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel.superclass.constructor.call(this, config);

		this.doReload();
	},

	/**
	 * Close the dialog.
	 */
	doClose: function () {
		this.dialog.close();
	},

	/**
	 * Reload the version store.
	 */
	doReload: function () {
		var responseHandler = new Zarafa.core.data.AbstractResponseHandler({
			doGetversion: this.gotVersionValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getversion',
			{
				accountId: this.account.get("id")
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It will update the textfield values.
	 *
	 * @param {Object} response version information object
	 */
	gotVersionValues: function (response) {
		this.backendVersionField.setValue(response.version.backend);
		this.serverVersionField.setValue(response.version.server);
	},

	/**
	 * Function will create panel items for {@link Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel FeatureVersionInfoPanel}.
	 *
	 * @return {Array} array of items that should be added to panel.
	 * @private
	 */
	createPanelItems: function () {
		return [{
			xtype     : 'form',
			border    : false,
			labelAlign: 'left',
			items     : [{
				xtype     : 'displayfield',
				ref       : '../backendVersionField',
				fieldLabel: _('Backend'),
				value     : _('Loading') + '&hellip;'
			}, {
				xtype     : 'displayfield',
				ref       : '../serverVersionField',
				fieldLabel: this.account.get('backend'),
				value     : _('Loading') + '&hellip;'
			}]
		}];
	}
});

Ext.reg('filesplugin.featureversioninfopanel', Zarafa.plugins.files.settings.ui.FeatureVersionInfoPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesHierarchyRootNode
 * @extends Zarafa.hierarchy.ui.HierarchyRootNode
 *
 * Utility TreeNode which is the root node for the entire hierarchy,
 * which by default is invisible. The direct childnodes for this nodes
 * are the opened stores.
 */
Zarafa.plugins.files.ui.FilesHierarchyRootNode = Ext.extend(Zarafa.hierarchy.ui.HierarchyRootNode, {

	/**
	 * Finds a TreeNode which represents the given EntryId
	 * @param {String} entryid The Entryid to find
	 * @return {Zarafa.hierarchy.ui.RootFolderNode} The found node
	 */
	findChildByEntryId : function(id)
	{
		return this.findChildBy(function(node) {
			return Zarafa.core.EntryId.compareEntryIds(node.attributes.folder.get('id'), id);
		});
	},

	/**
	 * Find a store treenode by the given Entryid
	 * @param {String} entryid The Store Entryid to find
	 * @return {Zarafa.hierarchy.ui.RootFolderNode} The found store node
	 */
	findChildStoreByEntryId : function(id)
	{
		return this.findChildBy(function(node) {
			return Zarafa.core.EntryId.compareStoreEntryIds(node.attributes.folder.get('id'), id);
		});
	}
});
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesListToolbar
 * @extends Ext.Toolbar
 * @xtype filesplugin.fileslisttoolbar
 *
 * The top toolbar for the files explorer.
 */
Zarafa.plugins.files.ui.FilesListToolbar = Ext.extend(
	Zarafa.core.ui.ContentPanelToolbar,
	{
		/**
		 * @cfg {Zarafa.plugins.files.FilesContext} context The context to which this toolbar belongs
		 */
		context: undefined,

		/**
		 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
		 * @property
		 * @type Zarafa.plugins.files.FilesContextModel
		 */
		model: undefined,

		/**
		 * @constructor
		 * @param config
		 */
		constructor: function (config) {
			config = config || {};

			if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
				this.model = config.context.getModel();
			}

			Ext.applyIf(config, {
				enableOverflow: true,
				items: this.createToolbarItems(),
			});
			Zarafa.plugins.files.ui.FilesListToolbar.superclass.constructor.call(
				this,
				config,
			);

			this.initEvent();
			this.onFolderChangeLoad(this.model, [this.model.getDefaultFolder()]);
		},

		/**
		 * Called after constructing files list toolbar.
		 */
		initEvent: function () {
			this.mon(this.model, {
				recordselectionchange: this.onRecordSelectionChange,
				folderchange: this.onFolderChangeLoad,
				scope: this,
			});

			const fileTypes = [
				{
					text: _('Document'),
					iconCls: 'plus',
					signatureId: false,
					handler: this.onCreateFile(this.model, '.docx'),
				},
				{
					text: _('Presentation'),
					iconCls: 'plus',
					signatureId: false,
					handler: this.onCreateFile(this.model, '.pptx'),
				},
				{
					text: _('Spreadsheet'),
					iconCls: 'plus',
					signatureId: false,
					handler: this.onCreateFile(this.model, '.xlsx'),
				},
			];

			// Create menu for the first time.
			// instance creation of menu will be handled by MenuMgr
			this.createFileButton.menu = Ext.menu.MenuMgr.get({
				xtype: 'menu',
				items: fileTypes,
			});
		},

		/**
		 * Create configuration object array which used to
		 * create toolbar items.
		 *
		 * @return {Array} configuration object array for toolbar.
		 */
		createToolbarItems: function () {
			return [
				{
					cls: 'files_icon_actionbutton',
					text: _('Upload'),
					ref: 'uploadButton',
					overflowText: _('Upload files'),
					iconCls: 'files_icon_action files_icon_action_upload',
					handler: this.onFileUpload,
					model: this.model,
					disabled: true,
					scope: this,
				},
				{
					xtype: 'button',
					text: _('Create document'),
					overflowText: _('Create document'),
					tooltip: _('Create office document'),
					iconCls: 'icon_new_note', // TODO: Change to proper icon
					ref: 'createFileButton',
					scope: this,
					disabled: false,
				},
				{
					cls: 'files_icon_actionbutton',
					text: _('New Folder'),
					ref: 'createFolderButton',
					disabled: true,
					overflowText: _('New Folder'),
					iconCls: 'files_icon_action files_icon_action_new_folder',
					handler: this.onCreateFolder,
					scope: this,
				},
				{
					ref: 'downloadBtn',
					cls: 'files_icon_actionbutton',
					text: _('Download'),
					overflowText: _('Download files'),
					iconCls: 'files_icon_action files_icon_action_download',
					handler: this.onFileDownload,
					disabled: true,
					scope: this,
				},
				{
					cls: 'files_icon_actionbutton',
					ref: 'shareBtn',
					text: _('Share'),
					overflowText: _('Share files'),
					iconCls: 'files_icon_action files_icon_action_share',
					handler: this.onFileShare,
					disabled: true,
					scope: this,
				},
				{
					cls: 'files_icon_actionbutton',
					ref: 'attachToMailBtn',
					text: _('Attach to mail'),
					overflowText: _('Attach to mail'),
					iconCls: 'files_icon_action files_icon_action_attach_to_mail',
					handler: this.onFileAddToMail,
					disabled: true,
					scope: this,
				},
				{
					cls: 'files_icon_actionbutton',
					ref: 'attachLinkToMailBtn',
					text: _('Attach to mail as link'),
					overflowText: _('Attach to mail as link'),
					iconCls: 'files_icon_action files_icon_action_attach_to_mail_link',
					handler: this.onFileLinkAddToMail,
					disabled: true,
					scope: this,
				},
				{
					xtype: 'tbfill',
				},
				{
					tooltip: _('Rename'),
					overflowText: _('Rename'),
					ref: 'renameBtn',
					iconCls: 'files_icon_action files_icon_action_edit',
					handler: this.onRename,
					nonEmptySelectOnly: true,
					disabled: true,
					scope: this,
				},
				{
					xtype: 'zarafa.toolbarbutton',
					tooltip: _('Delete'),
					ref: 'deleteBtn',
					overflowText: _('Delete'),
					iconCls: 'files_icon_action files_icon_action_delete',
					handler: this.onDelete,
					disabled: true,
					scope: this,
				},
			];
		},

		/**
		 * Event handler triggered when selection was changed in
		 * grid. it will disable/enable download, attach to mail,
		 * share and rename buttons in toolbar.
		 *
		 * @param {Zarafa.core.ContextModel} model this model.
		 * @param {Zarafa.plugins.files.data.FilesRecord[]} records The selected records
		 */
		onRecordSelectionChange: function (model, records) {
			var linkShareVisible = true;
			if (Ext.isArray(records)) {
				for (let i = 0; i < records.length; i++) {
					const sharedIds = records[i].get('sharedid');
					if (sharedIds.length === 0) {
						linkShareVisible = false;
					}
				}
			}

			var validator = Zarafa.plugins.files.data.Utils.Validator;
			var isVisible = validator.actionSelectionVisibilityFilter(
				records,
				false,
				true,
				false,
				true,
			);
			linkShareVisible =
				linkShareVisible &&
				validator.actionSelectionVisibilityFilter(
					records,
					false,
					false,
					false,
					true,
				);
			this.downloadBtn.setDisabled(!isVisible);
			this.attachToMailBtn.setDisabled(!isVisible);
			this.attachLinkToMailBtn.setDisabled(!linkShareVisible);

			isVisible = validator.actionSelectionVisibilityFilter(
				records,
				true,
				false,
				false,
				true,
			);
			if (isVisible) {
				this.shareBtn.setDisabled(!isVisible);
				var account = records[0].getAccount();
				this.shareBtn.setVisible(
					account.supportsFeature(
						Zarafa.plugins.files.data.AccountRecordFeature.SHARING,
					),
				);
			} else {
				this.shareBtn.setVisible(false);
			}
			this.renameBtn.setDisabled(!isVisible);
			this.deleteBtn.setDisabled(
				!validator.actionSelectionVisibilityFilter(
					records,
					false,
					false,
					false,
					true,
				),
			);
		},

		/**
		 * Event handler which is triggered when folder was changed.
		 * It will disable or enable the toolbar items.
		 *
		 * @param {Zarafa.plugins.files.FilesContextModel} model The {@link Zarafa.plugins.files.FilesContextModel FilesContextModel}
		 * @param {Zarafa.plugins.files.data.FilesRecord} records
		 * @param {Object} options
		 */
		onFolderChangeLoad: function (model, folders) {
			var folder = folders[0] || undefined;
			if (Ext.isEmpty(folder) || folder.get('folder_id') === '#R#') {
				this.setDisabled(true);
				this.shareBtn.setVisible(false);
			} else {
				this.createFolderButton.setDisabled(false);
				this.createFileButton.setDisabled(false);
				this.uploadButton.setDisabled(false);
			}
		},

		/**
		 * Event handler for creating a new file
		 */
		onCreateFile: function (model, filetype) {
			return function (button) {
				var hierarchyStore = model.getHierarchyStore();
				var folder = hierarchyStore.getFolder(model.getStore().getPath());
				Zarafa.plugins.files.data.Actions.createFile(
					model,
					undefined,
					folder,
					button,
					filetype,
				);
			};
		},

		/**
		 * Event handler for opening the "create new folder" dialog.
		 */
		onCreateFolder: function () {
			var model = this.model;
			var hierarchyStore = model.getHierarchyStore();
			var folder = hierarchyStore.getFolder(model.getStore().getPath());
			Zarafa.plugins.files.data.Actions.createFolder(model, undefined, folder);
		},

		/**
		 * Event handler for opening the Browser's file selection dialog.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onFileUpload: function (button, event) {
			var uploadComponent = new Zarafa.plugins.files.ui.UploadComponent({
				callback: this.uploadCallback,
				multiple: true,
				scope: this,
			});

			uploadComponent.openUploadDialog();
		},

		/**
		 * Event handler for downloading the selected files.
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @private
		 */
		onFileDownload: function () {
			var records = this.model.getSelectedRecords();
			Zarafa.plugins.files.data.Actions.downloadItem(records);
		},

		/**
		 * Event handler for sharing the selected files.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onFileShare: function (button, event) {
			var records = this.model.getSelectedRecords();
			Zarafa.plugins.files.data.Actions.createShareDialog(records);
		},

		/**
		 * Event handler for attaching the selected files to a new mail record.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onFileAddToMail: function (button, event) {
			var records = this.model.getSelectedRecords();

			var emailRecord = container
				.getContextByName('mail')
				.getModel()
				.createRecord();
			var idsList = [];
			var attachmentStore = emailRecord.getAttachmentStore();

			Ext.each(
				records,
				function (record) {
					idsList.push(record.get('folder_id'));
				},
				this,
			);

			container
				.getNotifier()
				.notify(
					'info.files',
					_('Attaching'),
					_('Creating email... Please wait!'),
				);

			try {
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'downloadtotmp',
					{
						ids: idsList,
						maxAttachmentSize: container
							.getServerConfig()
							.getMaxAttachmentSize(),
						dialog_attachments: attachmentStore.getId(),
					},
					new Zarafa.core.data.AbstractResponseHandler({
						doDownloadtotmp: this.attachToMail.createDelegate(
							this,
							[emailRecord],
							true,
						),
					}),
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		},

		/**
		 * Event handler for attaching the selected file as link to a new mail record.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onFileLinkAddToMail: function (button, event) {
			var records = this.model.getSelectedRecords();
			var idsList = [];
			var emailRecord = container
				.getContextByName('mail')
				.getModel()
				.createRecord();

			Ext.each(
				records,
				function (record) {
					idsList.push(record.get('folder_id'));
				},
				this,
			);

			container
				.getNotifier()
				.notify(
					'info.files',
					_('Attaching'),
					_('Creating email... Please wait!'),
				);

			try {
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'loadsharingdetails',
					{
						records: idsList,
					},
					new Zarafa.plugins.files.backend.Default.data.ResponseHandler({
						successCallback: this.attachLinkToMail.createDelegate(
							this,
							[emailRecord],
							true,
						),
					}),
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		},

		/**
		 * Callback for the loadsharingdetails response. This function will initialize the UI with the given
		 * share records.
		 *
		 * @param {Object} response the response object from the share record request
		 * @private
		 */
		attachLinkToMail: function (response, emailRecord) {
			const signature = emailRecord.get('html_body');
			var shares = response.shares;
			// For each record
			const urls = Object.values(shares).map((share) => {
				// Get last share-link of record
				const arr = Object.values(share);
				const len = arr.length;
				if (len > 0) {
					return arr[len - 1].url;
				}
				return '';
			});
			var html = '<br>';
			urls.forEach((url) => {
				html += '<a href="' + url + '">' + url + '</a><br>';
			});
			html += signature;
			emailRecord.set('html_body', html);
			Zarafa.core.data.UIFactory.openCreateRecord(emailRecord);
		},

		/**
		 * The callback function of {@link Zarafa.plugins.files.ui.UploadComponent}
		 * which used to upload the attachment file on server.
		 *
		 * @param {Object/Array} files The files contains file information.
		 * @param {Object} form the form is contains {@link Ext.form.BasicForm bacisform} info.
		 */
		uploadCallback: function (files, form) {
			Zarafa.plugins.files.data.Actions.uploadAsyncItems(
				files,
				this.model.getStore(),
			);
		},

		/**
		 * This method will add the downloaded files to a new mail record.
		 *
		 * @param responseItems
		 * @param response
		 * @param emailRecord
		 */
		attachToMail: function (response, emailRecord) {
			Zarafa.plugins.files.data.Actions.openCreateMailContent(
				emailRecord,
				response.items,
			);
		},

		/**
		 * Event handler for renaming a selected file.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onRename: function (button, event) {
			var records = this.model.getSelectedRecords();
			Zarafa.plugins.files.data.Actions.openRenameDialog(records[0]);
		},

		/**
		 * Event handler for deleting files and folders.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onDelete: function (button, event) {
			var model = this.model;
			var records = model.getSelectedRecords();
			var folders = [];
			Ext.each(
				records,
				function (item) {
					folders.push(model.getHierarchyStore().getFolder(item.id));
				},
				this,
			);

			Zarafa.plugins.files.data.Actions.deleteRecords(records);
		},
	},
);

Ext.reg(
	'filesplugin.fileslisttoolbar',
	Zarafa.plugins.files.ui.FilesListToolbar,
);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesMainContextMenu = Ext.extend(
	Zarafa.core.ui.menu.ConditionalMenu,
	{
		/**
		 * @cfg {Zarafa.plugins.files.FilesContext} context The context to which this context menu belongs.
		 */
		context: undefined,
		model: null,

		/**
		 * @constructor
		 * @param {Object} config configuration object.
		 */
		constructor: function (config) {
			config = config || {};

			Ext.applyIf(config, {
				items: [
					this.createContextActionItems(config.context.model),
					{ xtype: 'menuseparator' },
					container.populateInsertionPoint(
						'plugin.files.contextmenu.actions',
						this,
					),
					{ xtype: 'menuseparator' },
					container.populateInsertionPoint(
						'plugin.files.contextmenu.options',
						this,
					),
				],
			});

			this.model = config.context.model;

			Zarafa.plugins.files.ui.FilesMainContextMenu.superclass.constructor.call(
				this,
				config,
			);
		},

		/**
		 * Create a context menu items.
		 *
		 * @return {Array} return an array which contains the configuration objects for
		 * context menu.
		 *
		 * @param model
		 */
		createContextActionItems: function (model) {
			return [
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Download'),
					iconCls: 'files_icon_action files_icon_action_download',
					handler: this.onContextItemDownload,
					beforeShow: function (item, records) {
						var visible =
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								false,
								true,
								false,
								false,
							);

						item.setVisible(visible);
					},
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Share'),
					iconCls: 'files_icon_action files_icon_action_share',
					handler: this.onContextItemShare,
					beforeShow: function (item, records) {
						var visible = false;
						var isShared = false;
						if (records.length > 0) {
							var account = records[0].getAccount();
							isShared = records[0].get('isshared');
							visible = account.supportsFeature(
								Zarafa.plugins.files.data.AccountRecordFeature.SHARING,
							);
						}

						visible =
							visible &&
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								true,
								false,
								false,
								true,
							);

						item.setVisible(visible);

						if (isShared == true) {
							item.setText(_('Edit share'));
						}
					},
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('New Folder'),
					iconCls: 'files_icon_action files_icon_action_new_folder',
					handler: this.onContextItemNewFolder,
					beforeShow: function (item, records) {
						item.setVisible(
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								true,
								false,
								true,
								true,
							),
						);
					},
					model: model,
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Attach to mail'),
					iconCls: 'files_icon_action files_icon_action_attach_to_mail',
					handler: this.onContextItemAttach,
					beforeShow: function (item, records) {
						var visible =
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								false,
								true,
								false,
								true,
							);
						var max_attachment_size = container
							.getServerConfig()
							.getMaxAttachmentSize();

						for (var i = 0; i < records.length; i++) {
							var record = records[i];
							if (record.get('message_size') > max_attachment_size) {
								visible = false;
								break;
							}
						}

						item.setVisible(visible);
					},
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					cls: 'files_icon_actionbutton',
					text: _('Attach to mail as link'),
					iconCls: 'files_icon_action files_icon_action_attach_to_mail_link',
					handler: this.onFileLinkAddToMail,
					scope: this,
					beforeShow: function (item, records) {
						var visible =
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								false,
								false,
								false,
								true,
							);

						if (Ext.isArray(records)) {
							for (let i = 0; i < records.length; i++) {
								const sharedIds = records[i].get('sharedid');
								if (sharedIds.length === 0) {
									visible = false;
								}
							}
						}

						item.setVisible(visible);
					},
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Rename'),
					iconCls: 'files_icon_action files_icon_action_edit',
					handler: this.onContextItemRename,
					beforeShow: function (item, records) {
						item.setVisible(
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								true,
								false,
								false,
								true,
							),
						);
					},
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Delete'),
					iconCls: 'files_icon_action files_icon_action_delete',
					handler: this.onContextItemDelete,
					beforeShow: function (item, records) {
						item.setVisible(
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								false,
								false,
								false,
							),
						);
					},
					scope: this,
				},
				{
					xtype: 'zarafa.conditionalitem',
					text: _('Info'),
					iconCls: 'icon_info',
					handler: this.onContextItemInfo,
					beforeShow: function (item, records) {
						var visibilityFilter =
							Zarafa.plugins.files.data.Utils.Validator.actionSelectionVisibilityFilter(
								records,
								true,
								false,
								false,
								true,
							);
						var noPreviewPanel =
							this.context.getCurrentViewMode() ===
							Zarafa.plugins.files.data.ViewModes.NO_PREVIEW;
						item.setDisabled(!visibilityFilter || !noPreviewPanel);
					},
					scope: this,
				},
			];
		},

		/**
		 * Handler called when 'Download' context menu item is pressed.
		 */
		onContextItemDownload: function () {
			Zarafa.plugins.files.data.Actions.downloadItem(this.records);
		},

		/**
		 * Handler called when 'Delete' context menu item is pressed.
		 */
		onContextItemDelete: function () {
			Zarafa.plugins.files.data.Actions.deleteRecords(this.records);
		},

		/**
		 * Handler called when 'share' context menu item is pressed.
		 */
		onContextItemShare: function () {
			Zarafa.plugins.files.data.Actions.createShareDialog(this.records);
		},

		/**
		 * Event handler for opening the "create new folder" dialog.
		 *
		 * @param button
		 */
		onContextItemNewFolder: function (button) {
			var model = button.model;
			var hierarchyStore = model.getHierarchyStore();
			var folder = hierarchyStore.getFolder(this.records[0].get('entryid'));
			Zarafa.plugins.files.data.Actions.createFolder(model, undefined, folder);
		},

		/**
		 * Handler called when 'Info' context menu item is pressed.
		 * It will open the {@link Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel FilesRecordContentPanel}.
		 */
		onContextItemInfo: function () {
			var count = this.records.length;
			var record = undefined;

			if (count == 1) {
				record = this.records[0];
			}

			var config = Ext.applyIf(
				{},
				{
					modal: true,
					record: record,
				},
			);

			var componentType =
				Zarafa.core.data.SharedComponentType[
					'zarafa.plugins.files.fileinfopanel'
				];
			Zarafa.core.data.UIFactory.openLayerComponent(
				componentType,
				undefined,
				config,
			);
		},

		/**
		 * Handler called when 'Rename' context menu item is pressed.
		 */
		onContextItemRename: function () {
			Zarafa.plugins.files.data.Actions.openRenameDialog(this.records[0]);
		},

		/**
		 * Handler called when 'Attach to mail' context menu item is pressed.
		 * It will create new mail with selected file(s) as an attachment.
		 */
		onContextItemAttach: function () {
			var emailRecord = container
				.getContextByName('mail')
				.getModel()
				.createRecord();
			var idsList = [];
			var attachmentStore = emailRecord.getAttachmentStore();

			Ext.each(
				this.records,
				function (record) {
					idsList.push(record.get('folder_id'));
				},
				this,
			);

			container
				.getNotifier()
				.notify(
					'info.files',
					_('Attaching'),
					_('Creating email... Please wait!'),
				);

			try {
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'downloadtotmp',
					{
						ids: idsList,
						maxAttachmentSize: container
							.getServerConfig()
							.getMaxAttachmentSize(),
						dialog_attachments: attachmentStore.getId(),
					},
					new Zarafa.core.data.AbstractResponseHandler({
						doDownloadtotmp: this.attachToMail.createDelegate(
							this,
							[emailRecord],
							true,
						),
					}),
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		},

		/**
		 * Open newly created mail record into tab panel.
		 *
		 * @param {Array} responseItems The File records that will be added as attachments.
		 * @param {Object} response The response object belonging to the given command.
		 * @param {Zarafa.core.data.IPMRecord} emailRecord The mail record which contains files records
		 * as an attachments.
		 */
		attachToMail: function (response, emailRecord) {
			Zarafa.plugins.files.data.Actions.openCreateMailContent(
				emailRecord,
				response.items,
			);
		},

		/**
		 * Event handler for attaching the selected file as link to a new mail record.
		 *
		 * See {@link #onFileInputChange} for the handling of the selected files.
		 * @param {Ext.Button} button the button on which click event is performed.
		 * @param {Ext.EventObject} event The event object
		 * @private
		 */
		onFileLinkAddToMail: function (button, event) {
			var records = this.model.getSelectedRecords();
			var idsList = [];
			var emailRecord = container
				.getContextByName('mail')
				.getModel()
				.createRecord();

			Ext.each(
				records,
				function (record) {
					idsList.push(record.get('folder_id'));
				},
				this,
			);

			container
				.getNotifier()
				.notify(
					'info.files',
					_('Attaching'),
					_('Creating email... Please wait!'),
				);

			try {
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'loadsharingdetails',
					{
						records: idsList,
					},
					new Zarafa.plugins.files.backend.Default.data.ResponseHandler({
						successCallback: this.attachLinkToMail.createDelegate(
							this,
							[emailRecord],
							true,
						),
					}),
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		},

		/**
		 * Callback for the loadsharingdetails response. This function will initialize the UI with the given
		 * share records.
		 *
		 * @param {Object} response the response object from the share record request
		 * @private
		 */
		attachLinkToMail: function (response, emailRecord) {
			const signature = emailRecord.get('html_body');
			var shares = response.shares;
			// For each record
			const urls = Object.values(shares).map((share) => {
				// Get last share-link of record
				const arr = Object.values(share);
				const len = arr.length;
				if (len > 0) {
					return arr[len - 1].url;
				}
				return '';
			});
			var html = '<br>';
			urls.forEach((url) => {
				html += '<a href="' + url + '">' + url + '</a><br>';
			});
			html += signature;
			emailRecord.set('html_body', html);
			Zarafa.core.data.UIFactory.openCreateRecord(emailRecord);
		},
	},
);

Ext.reg(
	'filesplugin.filesmaincontextmenu',
	Zarafa.plugins.files.ui.FilesMainContextMenu,
);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesMainPanel
 * @extends Zarafa.common.ui.ContextMainPanel
 * @xtype filesplugin.filesmainpanel
 */
Zarafa.plugins.files.ui.FilesMainPanel = Ext.extend(Zarafa.common.ui.ContextMainPanel, {

	/**
	 * @constructor
	 * @param {Object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.filesmainpanel',
			layout: 'zarafa.switchborder',
			header : false,
			iconCls : 'icon_files',
			items: [
				this.initMainItems(config),
				this.initPreviewPanel()
			],
			tbar       : {
				xtype: 'filesplugin.filestoptoolbar',
				height      : 28,
				context     : config.context
			}
		});

		Zarafa.plugins.files.ui.FilesMainPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Initializes the different views for the files plugin.
	 *
	 * @param {Object} config Configuration object
	 * @return {Zarafa.mail.ui.MailGrid}
	 * @private
	 */
	initMainItems: function (config)
	{
		return {
			xtype : 'panel',
			layout : 'zarafa.collapsible',
			cls : 'zarafa-files-context-mainpanel',
			minWidth : 200,
			minHeight : 200,
			region : 'center',
			border: false,
			split : true,
			items : [{
				xtype : 'zarafa.switchviewcontentcontainer',
				ref : '../viewPanel',
				layout   : 'card',
				lazyItems: this.initViews(config.context)
			}],
			tbar : {
				xtype       : 'filesplugin.fileslisttoolbar',
				defaultTitle: _('Files'),
				height      : 33,
				context     : config.context
			}
		};
	},

	/**
	 * Function will initialize all views associated with files context
	 * it will also get views added through 3rd party plugins and add it here
	 * @param {Zarafa.plugins.files.FilesContext} context The Files Context
	 * @return {Array} array of config objects of different views
	 * @private
	 */
	initViews: function (context)
	{
		var allViews = [{
			xtype  : 'filesplugin.filesrecordaccountview',
			flex   : 1,
			id     : 'files-accountview',
			anchor : '100%',
			context: context
		}, {
			xtype  : 'filesplugin.filesrecordgridview',
			flex   : 1,
			id     : 'files-gridview',
			anchor : '100%',
			context: context
		}, {
			xtype  : 'filesplugin.filesrecordiconview',
			flex   : 1,
			id     : 'files-iconview',
			anchor : '100%',
			context: context
		}];

		var additionalViewItems = container.populateInsertionPoint('plugin.files.views', this, context);
		allViews = allViews.concat(additionalViewItems);

		return allViews;
	},

	/**
	 * Initializes the {@link Zarafa.plugins.files.ui.FilesPreviewPanel PreviewPanel}
	 *
	 * @return {Zarafa.plugins.files.ui.FilesPreviewPanel}
	 * @private
	 */
	initPreviewPanel: function() {
		return {
			xtype  : 'filesplugin.filespreviewpanel',
			ref    : 'filesPreview',
			border : false,
			region : 'south',
			split  : true
		};
	},

	/**
	 * Called during rendering of the panel, this will initialize all events.
	 * @private
	 */
	initEvents: function () {
		if (Ext.isDefined(this.context)) {
			this.mon(this.context, 'viewchange', this.onViewChange, this);
			this.mon(this.context, 'viewmodechange', this.onViewModeChange, this);

			this.onViewChange(this.context, this.context.getCurrentView());
			this.onViewModeChange(this.context, this.context.getCurrentViewMode());
		}

		Zarafa.plugins.files.ui.FilesMainPanel.superclass.initEvents.apply(this, arguments);
	},

	/**
	 * Event handler which is fired when the currently active view inside the {@link #context}
	 * has been updated. This will update the call
	 * {@link #viewPanel}#{@link Zarafa.core.ui.SwitchViewContentContainer#switchView}
	 * to make the requested view active.
	 *
	 * @param {Zarafa.core.Context} context The context which fired the event.
	 * @param {Zarafa.common.data.Views} newView The ID of the selected view.
	 * @param {Zarafa.common.data.Views} oldView The ID of the previously selected view.
	 */
	onViewChange: function (context, newView, oldView)
	{
		var store = context.getModel().getStore();
		switch (newView) {
			case Zarafa.plugins.files.data.Views.LIST:
				this.viewPanel.switchView(store.getPath() === "#R#" ? 'files-accountview' : 'files-gridview');
				break;
			case Zarafa.plugins.files.data.Views.ICON:
				this.viewPanel.switchView('files-iconview');
				break;
		}
	},

	/**
	 * Event handler which is fired when the {@link Zarafa.core.Context} fires the
	 * {@link Zarafa.core.Context#viewmodechange viewmodechange} event. This will
	 * convert the configured {@link Zarafa.common.data.ViewModes mode} to a
	 * {@link Zarafa.common.ui.layout.SwitchBorderLayout.Orientation orientation}
	 * to be {@link Zarafa.common.ui.layout.SwitchBorderLayout.setOrientation applied}
	 * to the {@link #layout}.
	 *
	 * @param {Zarafa.core.Context} context The context which fired the event
	 * @param {Zarafa.common.data.ViewModes} newViewMode The new active mode
	 * @param {Zarafa.common.data.ViewModes} oldViewMode The previous mode
	 * @private
	 */
	onViewModeChange: function (context, newViewMode, oldViewMode)
	{
		var orientation;

		switch (newViewMode) {
			case Zarafa.plugins.files.data.ViewModes.NO_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.OFF;
				break;
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.HORIZONTAL;
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				orientation = Zarafa.common.ui.layout.SwitchBorderLayout.Orientation.VERTICAL;
				break;
			case Zarafa.plugins.files.data.ViewModes.SEARCH:
				return;
		}

		var layout = this.getLayout();
		if (!Ext.isFunction(layout.setOrientation)) {
			if (Ext.isString(layout)) {
				this.layoutConfig = Ext.apply(this.layoutConfig || {}, {orientation: orientation});
			} else {
				this.layout.orientation = orientation;
			}
		} else {
			layout.setOrientation(orientation);
		}
	}
});

Ext.reg('filesplugin.filesmainpanel', Zarafa.plugins.files.ui.FilesMainPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesPreviewPanel
 * @extends Ext.Panel
 * @xtype filesplugin.filespreviewpanel
 *
 * The preview panel container for the files preview.
 */
Zarafa.plugins.files.ui.FilesPreviewPanel = Ext.extend(Ext.Panel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		var toolbar = Ext.applyIf(config.tbar || {}, {
			xtype : 'zarafa.toolbar',
			height: 33,
			hidden: false,
			items : []
		});

		Ext.applyIf(config, {
			xtype   : 'filesplugin.filespreviewpanel',
			layout  : 'fit',
			stateful: true,
			cls     : 'zarafa-files-previewpanel',
			width   : 300,
			height  : 300,
			tbar    : toolbar
		});

		Zarafa.plugins.files.ui.FilesPreviewPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filespreviewpanel', Zarafa.plugins.files.ui.FilesPreviewPanel);

Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordAccountView = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.plugins.files.FilesContext} context The context to which this context menu belongs.
	 */
	context : undefined,

	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		Ext.applyIf(config, {
			xtype: 'filesplugin.filesrecordaccountview',
			layout:'fit',
			cls : 'zarafa-files-accountview',
			loadMask : true,
			header : false,
			border : false,
			maskDisabled : false,
			height : 400,
			loadingText   : _('Loading accounts') + '...',
			items :[{
				xtype: 'dataview',
				ref:'accountView',
				store : config.store,
				emptyText : '<div class="emptytext">' + _('There are no accounts added. Go to settings, Files tab and add an account') + '</div>',
				autoScroll : true,
				singleSelect : true,
				deferEmptyText: false,
				overClass : 'zarafa-files-accountview-over',
				tpl : this.initTemplate(config.model),
				selectedClass : 'zarafa-files-accountview-selected',
				itemSelector : 'div.zarafa-files-accountview-container'
			}]
		});

		Zarafa.plugins.files.ui.FilesRecordAccountView.superclass.constructor.call(this, config);
	},

	initTemplate: function (model) {
		// Load the account store
		return new Ext.XTemplate(
			'<div style="height: 100%; width: 100%; overflow: auto;">',
				'<tpl for=".">',
					'<div class="zarafa-files-accountview-container">',
						'<div class="zarafa-files-account-background {.:this.getAccountType}"> </div>',
						'<div class="zarafa-files-account-info">',
							'<span class="zarafa-files-accountview-subject">{filename:htmlEncode}</span>',
							'<span class="zarafa-files-accountview-account">{.:this.getAccountIdentifier}</span>',
						'</div>',
					'</div>',
				'</tpl>',
			'</div>',
			{
				getAccountType: function (record) {
					// get an instance of the account store.
					var store = this.model.getHierarchyStore();

					// look up the account
					var account = store.getById(record.id);

					var backend = "Webdav"; // Default is webdav...
					if (Ext.isDefined(account)) {
						backend = account.get("backend");
					}

					return "icon_256_" + backend;
				},

				getAccountIdentifier: function (record) {
					// get an instance of the account store.
					var store = this.model.getHierarchyStore();

					// look up the account
					var account = store.getById(record.id);

					var identifier = ""; // Default is empty...
					// TODO: this is not dynamic because the backend_config variable names might change in other backends
					if (Ext.isDefined(account)) {
						var bconfig = account.get("backend_config");
						if(bconfig && Ext.isDefined(bconfig.user) && Ext.isDefined(bconfig.server_address)) {
							identifier = bconfig.user + "@" + bconfig.server_address;
						}
					}

					return Zarafa.plugins.files.data.Utils.Format.truncate(identifier, 27); // 27 = length of the account field
				},
				model : model
			}
		);
	},

	/**
	 * Called during rendering of the panel, this will initialize all events.
	 * @private
	 */
	initEvents: function ()
	{
		this.accountView.on({
			'dblclick'       : this.onIconDblClick,
			scope            : this
		});
	},

	/**
	 * Initialize the component.
	 * It will register afterrender event with given handler which create loading mask.
	 * @private
	 */
	initComponent: function ()
	{
		Zarafa.plugins.files.ui.FilesRecordAccountView.superclass.initComponent.apply(this, arguments);

		// create load mask
		if (this.loadMask) {
			this.on('afterrender', this.createLoadMask, this);
		}
	},

	/**
	 * Function will create {@link Zarafa.common.ui.LoadMask} which will be shown
	 * when loading the {@link Zarafa.plugins.files.ui.FilesRecordAccountView FilesRecordAccountView}.
	 * @private
	 */
	createLoadMask: function ()
	{
		var hierarchyStore = this.model.getHierarchyStore();
		if (hierarchyStore.isLoading()) {
			this.loadMask = new Zarafa.common.ui.LoadMask(this.getEl(), { msg: this.loadingText, store: this.store});
			this.loadMask.show();
		}
	},

	/**
	 * Destroy all elements which were created by this panel.
	 * @override
	 */
	destroy: function ()
	{
		Ext.destroy(this.loadMask);
		Zarafa.plugins.files.ui.FilesRecordAccountView.superclass.destroy.apply(this, arguments);
	},

	/**
	 * Event handler which triggered when double click on data view item.
	 *
	 * @param {Ext.DataView} dataView The dataView item which is double clicked.
	 * @param {Number} index The index of an item which double clicked.
	 */
	onIconDblClick: function (dataView, index)
	{
		 var store = this.accountView.getStore();
		 var record = store.getAt(index);
		 var folder = this.model.getHierarchyStore().getFolder(record.get('entryid'));
		container.selectFolder(folder);
	}
});

Ext.reg('filesplugin.filesrecordaccountview', Zarafa.plugins.files.ui.FilesRecordAccountView);

Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordDetailsPanel = Ext.extend(Ext.form.FormPanel, {

	defaultPreviewImage: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iIzg4OCIgZD0iTTIuMjIgMi4yMmEuNzUuNzUgMCAwIDAtLjA3My45NzZsLjA3My4wODQgNC4wMzQgNC4wMzVhOS45ODYgOS45ODYgMCAwIDAtMy45NTUgNS43NS43NS43NSAwIDAgMCAxLjQ1NS4zNjQgOC40OSA4LjQ5IDAgMCAxIDMuNTgtNS4wMzRsMS44MSAxLjgxQTQgNCAwIDAgMCAxNC44IDE1Ljg2bDUuOTE5IDUuOTJhLjc1Ljc1IDAgMCAwIDEuMTMzLS45NzdsLS4wNzMtLjA4NC02LjExMy02LjExNC4wMDEtLjAwMi0xLjItMS4xOTgtMi44Ny0yLjg3aC4wMDJsLTIuODgtMi44NzcuMDAxLS4wMDItMS4xMzMtMS4xM0wzLjI4IDIuMjJhLjc1Ljc1IDAgMCAwLTEuMDYgMFptNy45ODQgOS4wNDUgMy41MzUgMy41MzZhMi41IDIuNSAwIDAgMS0zLjUzNS0zLjUzNVpNMTIgNS41Yy0xIDAtMS45Ny4xNDgtMi44ODkuNDI1bDEuMjM3IDEuMjM2YTguNTAzIDguNTAzIDAgMCAxIDkuODk5IDYuMjcyLjc1Ljc1IDAgMCAwIDEuNDU1LS4zNjNBMTAuMDAzIDEwLjAwMyAwIDAgMCAxMiA1LjVabS4xOTUgMy41MSAzLjgwMSAzLjhhNC4wMDMgNC4wMDMgMCAwIDAtMy44MDEtMy44WiIvPjwvc3ZnPg==',

	record : undefined,

	constructor: function (config) {
		config = config || {};
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();

		var layout = {
			type : 'vbox',
			align: 'stretch',
			pack : 'start'
		};
		if(viewMode === Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW) {
			Ext.apply(layout, {
				type : 'hbox'
			});
		}

		config = Ext.applyIf(config, {
			xtype      : 'filesplugin.filesrecorddetailspanel',
			ref        : '../fileinfo',
			autoDestroy: true,
			layout     : layout,
			border     : false,
			items      : [
				this.fieldSetFileInfo(),
				this.fieldSetFilePreview()
			]
		});
		// FixME : Listener is used when user use info button
		// in context menu. we can avoid this code by either using
		// initEvents function or by 'previewrecordchange' which was fired
		// from setPreviewRecord function of Zarafa.core.ContextModel
		if (Ext.isDefined(config.record)) {
			config = Ext.applyIf(config, {
				listeners: {
					afterlayout: function (cmp) {
						this.update(this.record);
					}
				}
			});
		}

		Zarafa.plugins.files.ui.FilesRecordDetailsPanel.superclass.constructor.call(this, config);
	},

	refresh: function () {
		this.removeAll();
		this.add(this.fieldSetFileInfo());
		this.add(this.fieldSetFilePreview());
	},

	fieldSetFileInfo: function () {
    return {
        xtype: 'fieldset',
        title: _('File information'),
        border: false,
        defaults: {
            anchor: '100%',
            readOnly: true
        },
        items: [{
            xtype: 'container',
            layout: 'hbox',
            items: [{
                xtype: 'label',
                text: _('Filename'),
                width: 125
            }, {
                xtype: 'textfield',
                ref: '../../filename',
                value: "unknown",
                flex: 1
            }]
        }, {
            xtype: 'container',
            ref: '../filesizeContainer',
            layout: 'hbox',
            items: [{
                xtype: 'label',
                text: _('Filesize'),
                width: 125
            }, {
                xtype: 'textfield',
                ref: '../../filesize',
                value: "unknown",
                flex: 1
            }]
        }, {
            xtype: 'container',
            layout: 'hbox',
            items: [{
                xtype: 'label',
                text: _('Last modified'),
                width: 125
            }, {
                xtype: 'textfield',
                ref: '../../lastmodified',
                value: "unknown",
                flex: 1
            }]
        }, {
            xtype: 'container',
            layout: 'hbox',
            items: [{
                xtype: 'label',
                text: _('Type'),
                width: 125
            }, {
                xtype: 'textfield',
                ref: '../../type',
                value: "unknown",
                flex: 1
            }]
        }, {
	    xtype: 'container',
	    ref: '../sharedContainer',
	    layout: 'hbox',
	    items: [{
		xtype: 'label',
		text: _('Is shared'),
		width: 125
	    }, {
		xtype: 'checkbox',
		ref: '../../shared',
		flex: 1,
		inputValue: _('Yes'),
		uncheckedValue: _('No'),
		readOnly: true,
		checked: ("unknown" === _("Yes")) ? true : false
	    }]
        }]
    };
},


	fieldSetFilePreview: function () {
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();

		var css = "width: 100%;";
		switch (viewMode) {
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				css = "width: 100%;";
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				css = "height: 100%;";
				break;
			default:
				break;
		}

		return {
			xtype: 'fieldset',
			title: _('File preview'),
			ref  : 'filepreview',
			flex : 1,
			autoScroll: true,

			defaultType: 'textfield',
			items      : [{
				xtype : 'component',
				id    : 'previewimage',
				autoEl: {tag: 'img', src: this.defaultPreviewImage, style: css}
			}]
		};
	},

	setPreviewPanel: function (record, extension) {
		var context = Zarafa.plugins.files.data.ComponentBox.getContext();
		var viewMode = context.getCurrentViewMode();
		var fileviewerEnabled = Ext.isDefined(container.getPluginByName('filepreviewer')) ? true: false;
		var pdfEnabled = Ext.isDefined(container.getPluginByName('pdfbox')) ? true: false;
		var odfEnabled = Ext.isDefined(container.getPluginByName('webodf')) ? true: false;

		var css = "width: 100%;";
		var cssNone = "width: 20%; position: relative; top: 50%; transform: translate(-50%) translateY(-50%); left: 50%;";
		switch (viewMode) {
			case Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW:
				css = "width: 100%;";
				break;
			case Zarafa.plugins.files.data.ViewModes.BOTTOM_PREVIEW:
				css = "height: 100%;";
				break;
			default:
				break;
		}

		var component = {};

		if (!fileviewerEnabled && !Ext.isEmpty(extension) && (/\.(gif|jpg|jpeg|tiff|png|bmp)$/i).test(extension)) {
			component = {
				xtype : 'component',
				autoEl: {tag: 'img', src: Zarafa.plugins.files.data.Actions.getDownloadLink(record), style: css}
			}
		} else if (fileviewerEnabled && !Ext.isEmpty(extension) && (new RegExp(container.getSettingsModel().get("zarafa/v1/plugins/filepreviewer/supported_filetypes"), "i")).test(extension)) {
			component = {
				xtype   : 'filepreviewer.viewerpanel',
				record: record,
				defaultScale: 1,
				autoResize: this.filepreview, // autoresize on this element
				height: this.filepreview.getInnerHeight()
			}
		} else if (!fileviewerEnabled && pdfEnabled && !Ext.isEmpty(extension) && (/\.(pdf)$/i).test(extension)) {
			component = {
				xtype   : 'filesplugin.pdfjspanel',
				src     : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
				title: record.get('filename')
			}
		} else if (!fileviewerEnabled && !pdfEnabled && !Ext.isEmpty(extension) && (/\.(pdf)$/i).test(extension)) { // if the pdfjs plugin is not available
			// show the pdf file in an iframe
			component = {
				xtype  : 'component',
				autoEl : {
					tag: 'iframe',
					width: '98%',
					height: '98%',
					frameborder: 'none',
					seamless: '',
					src: Zarafa.plugins.files.data.Actions.getDownloadLink(record)
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(txt|html|php|js|c|cpp|h|java|sh|bat|log|cfg|conf|tex|py|pl)$/i).test(extension)) {
			component = {
				xtype    : 'textarea',
				hideLabel: true,
				readOnly : true,
				anchor   : '0, 0',
				listeners: {
					'afterrender': function () {
						Ext.Ajax.request({
							method : 'GET',
							url    : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							success: function (result, request) {
								var responsetext = result.responseText;

								this.setRawValue(responsetext);
							},
							scope  : this
						});
					}
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(mp3|wav)$/i).test(extension)) {
			var audioType = '';
			switch(extension.toLowerCase()) {
				case '.wav':
					audioType = 'audio/wav';
					break;
				default:
					audioType = 'audio/mpeg';
			}

			component = {
				xtype : 'component',
				autoEl: {
					tag: 'audio',
					style: css,
					controls: 'controls',
					cn    : [
						{
							tag: 'source',
							src: Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							type: audioType
						},
						_('Your browser does not support previewing of audio files!')
					]
				}
			}
		} else if (!Ext.isEmpty(extension) && (/\.(mp4|ogg|webm)$/i).test(extension)) {
			var videoType = '';
			switch(extension.toLowerCase()) {
				case '.ogg':
					videoType = 'video/ogg';
					break;
				case '.webm':
					videoType = 'video/webm';
					break;
				default:
					videoType = 'audio/mp4';
			}

			component = {
				xtype : 'component',
				autoEl: {
					tag: 'video',
					style: css + 'height: auto;',
					poster: 'plugins/files/resources/images/preview/video_loader.gif',
					preload: 'metadata',
					controls: 'controls',
					cn    : [
						{
							tag: 'source',
							src: Zarafa.plugins.files.data.Actions.getDownloadLink(record),
							type: videoType
						},
						_('Your browser does not support previewing of video files!')
					]
				}
			}
		} else if (odfEnabled && !Ext.isEmpty(extension) && (/\.(odp|odt|ods)$/i).test(extension)) {
			component = {
				xtype : 'filesplugin.webodfpanel',
				src   : Zarafa.plugins.files.data.Actions.getDownloadLink(record),
				title : record.get('filename')
			}
		} else {
			component = {
				xtype : 'component',
				autoEl: {tag: 'img', src: this.defaultPreviewImage, style: cssNone}
			}
		}

		this.filepreview.removeAll(true);
		this.filepreview.add(component);
		this.filepreview.doLayout();
	},

	update: function (record)
	{
		this.filename.setValue(record.get('filename'));

		var recordType = record.get('type') == Zarafa.plugins.files.data.FileTypes.FILE;
		this.filesizeContainer.setVisible(recordType);
		if (recordType) {
			this.filesize.setValue(Zarafa.plugins.files.data.Utils.Format.fileSize(record.get('message_size')));
		}

		var lastModifiedDate = Ext.util.Format.date(new Date(record.get('lastmodified')), _('d.m.Y G:i'));
		this.lastmodified.setValue(lastModifiedDate);

		var type = _('Folder');
		if (recordType) {
			var extension = this.getExtension(record.get('filename'));
			type = String.format(_('File ({0})'), extension);
		}
		this.type.setValue(type);

		var supportSharing = record.getAccount().supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.SHARING);
		this.sharedContainer.setVisible(supportSharing);
		if (supportSharing) {
			this.shared.setValue(record.get("isshared") ? _('Yes') : _('No'));
		}
		this.setPreviewPanel(record, extension);
	},

	onRender: function (ct, position) {
		Zarafa.plugins.files.ui.FilesRecordDetailsPanel.superclass.onRender.call(this, ct, position);
		this.wrap = this.el.wrap({cls: 'preview-body'});
		this.resizeEl = this.positionEl = this.wrap;
	},

	getExtension: function (filename) {
		var i = filename.lastIndexOf('.');
		return (i < 0) ? '' : filename.substr(i);
	}
});

Ext.reg('filesplugin.filesrecorddetailspanel', Zarafa.plugins.files.ui.FilesRecordDetailsPanel);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordGridColumnModel = Ext.extend(Zarafa.common.ui.grid.ColumnModel, {

	useCompactView: false,

	constructor: function (config) {
		config = config || {};

		this.defaultColumns = this.createDefaultColumns();
		this.compactColumns = this.createCompactColumns();

		Ext.applyIf(config, {
			columns : this.defaultColumns,
			defaults: {
				sortable: true
			}
		});

		if (config.useCompactView === true) {
			config.columns = this.compactColumns;
		}

		Ext.apply(this, config);

		Zarafa.plugins.files.ui.FilesRecordGridColumnModel.superclass.constructor.call(this, config);
	},

	createDefaultColumns: function () {
		return [{
			id       : 'type',
			dataIndex: 'type',
			header   : '<p class="icon_index">&nbsp;</p>',
			headerCls: 'zarafa-icon-column icon',
			renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
			width    : 24,
			fixed    : true,
			tooltip  : _('Sort by: Type')
		},
			{
				header   : 'ID',
				dataIndex: 'id',
				width    : 50,
				hidden   : true,
				tooltip  : _('Sort by: ID')
			},
			{
				header   : 'Path',
				dataIndex: 'path',
				width    : 100,
				hidden   : true,
				tooltip  : _('Sort by: Path')
			},
			{
				header   : _('Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : _('Sort by: Filename')
			},
			{
				header   : _('Last modified'),
				dataIndex: 'lastmodified',
				width    : 160,
				renderer : Zarafa.plugins.files.data.Utils.Renderer.datetimeRenderer,
				tooltip  : _('Sort by: Last modified')
			},
			{
				header   : _('Size'),
				dataIndex: 'message_size',
				width    : 80,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : _('Sort by: Size')
			},
			{
				id       : 'isshared',
				dataIndex: 'isshared',
				header   : '<p class="files_icon_12_share">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.sharedRenderer,
				listeners: {
					click: this.doOnShareButtonClick
				},
				width    : 32,
				fixed    : true,
				tooltip  : _('Sort by: Shared')
			}
		];
	},

	createCompactColumns: function () {
		return [{
			id       : 'column_type',
			dataIndex: 'type',
			header   : '<p class="icon_index">&nbsp;</p>',
			headerCls: 'zarafa-icon-column icon',
			renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
			width    : 24,
			fixed    : true,
			tooltip  : _('Sort by: Type')
		},
			{
				header   : _('Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : _('Sort by: Filename')
			},
			{
				header   : _('Last modified'),
				dataIndex: 'lastmodified',
				width    : 100,
				renderer : Zarafa.plugins.files.data.Utils.Renderer.datetimeRenderer,
				tooltip  : _('Sort by: Last modified')
			},
			{
				header   : _('Size'),
				dataIndex: 'message_size',
				width    : 80,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : _('Sort by: Size')
			},
			{
				id       : 'isshared',
				dataIndex: 'isshared',
				header   : '<p class="files_icon_12_share">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.sharedRenderer,
				listeners: {
					click: this.doOnShareButtonClick
				},
				width    : 32,
				fixed    : true,
				tooltip  : _('Sort by: Shared')
			}
		];
	},

	setCompactView: function (compact) {
		if (this.useCompactView !== compact) {
			this.useCompactView = compact;

			if (compact) {
				this.name = 'compact';

				this.defaultColumns = this.config;
				this.columns = this.compactColumns;
			} else {
				this.name = 'default';
				this.compactColumns = this.config;
				this.columns = this.defaultColumns;
			}

			this.setConfig(this.columns, false);
		}
	},

	doOnShareButtonClick: function (col, grid, row, event) {
		var record = grid.store.getAt(row);

		if (record.get("isshared") === true) {
			Zarafa.plugins.files.data.Actions.createShareDialog([record]);
		}
	}
});
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordGridView = Ext.extend(Zarafa.common.ui.grid.GridPanel, {
	/**
	 * @cfg {Zarafa.plugins.files.FilesContext} context The context to which this context menu belongs.
	 */
	context : undefined,

	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	dropTarget: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		config.store = Ext.StoreMgr.lookup(config.store);

		Ext.applyIf(config, {
			xtype                     : 'filesplugin.filesrecordgridview',
			ddGroup                   : 'dd.filesrecord',
			id                        : 'files-gridview',
			enableDragDrop            : true,
			border                    : false,
			stateful                  : true,
			statefulRelativeDimensions: false,
			loadMask                  : this.initLoadMask(),
			viewConfig                : this.initViewConfig(),
			sm                        : this.initSelectionModel(),
			cm                        : this.initColumnModel(),
			keys                      : {
				key    : Ext.EventObject.DELETE,
				handler: this.onKeyDelete,
				scope  : this
			}
		});
		Zarafa.plugins.files.ui.FilesRecordGridView.superclass.constructor.call(this, config);

	},

	initEvents: function () {
		Zarafa.plugins.files.ui.FilesRecordGridView.superclass.initEvents.call(this);

		this.mon(this, 'cellcontextmenu', this.onCellContextMenu, this);
		this.mon(this, 'rowbodycontextmenu', this.onRowBodyContextMenu, this);
		this.mon(this, 'rowdblclick', this.onRowDblClick, this);
		this.mon(this, 'afterrender', this.initDropTarget, this);

		this.mon(this.getSelectionModel(), 'rowselect', this.onRowSelect, this, {buffer: 1});
		this.mon(this.getSelectionModel(), 'selectionchange', this.onSelectionChange, this, {buffer: 1});

		this.mon(this.context, 'viewmodechange', this.onContextViewModeChange, this);
		this.onContextViewModeChange(this.context, this.context.getCurrentViewMode());
		this.mon(this.bwrap, 'drop', this.onDropItemToUpload, this);
	},

	initLoadMask: function () {
		return {
			msg: _('Loading files') + '...'
		};
	},

	initViewConfig: function () {
		return {

			enableRowBody: false,

			rowSelectorDepth: 15
		};
	},

	initSelectionModel: function () {
		return new Ext.grid.RowSelectionModel();
	},

	initColumnModel: function () {
		return new Zarafa.plugins.files.ui.FilesRecordGridColumnModel();
	},

	initDropTarget: function () {
		this.dropTarget = new Ext.dd.DropTarget(this.getEl(), {
			ddGroup   : 'dd.filesrecord',
			copy      : false,
			gridStore : this.getStore(),
			gridSM    : this.getSelectionModel(),
			notifyDrop: function (ddSource, e, data) {

				if (this.notifyOver(ddSource, e, data) !== this.dropAllowed) {
					return false;
				}

				var cellindex = ddSource.getDragData(e).rowIndex;
				var dropTarget = this.gridStore.getAt(cellindex);
				if (Ext.isDefined(cellindex) && dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {

					Ext.each(data.selections, function (record) {
						record.setDisabled(true);
					});

					return Zarafa.plugins.files.data.Actions.moveRecords(data.selections, dropTarget, {hierarchyStore: this.gridStore.hierarchyStore});
				} else {
					return false;
				}
			},
			notifyOver: function (ddSource, e, data) {
				var cellindex = ddSource.getDragData(e).rowIndex;
				var ret = this.dropNotAllowed;

				if (Ext.isDefined(cellindex)) {
					var dropTarget = this.gridStore.getAt(cellindex);

					if (dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
						ret = this.dropAllowed;
					}

					Ext.each(data.selections, function (record) {
						var srcId = record.get("folder_id");
						var trgId = dropTarget.get("folder_id");
						if (srcId === trgId || record.get("filename") === ".." || trgId.slice(0, srcId.length) === srcId) {
							ret = this.dropNotAllowed;
							return false;
						}
					}, this);
				}

				return ret;
			},

			notifyEnter: function (ddSource, e, data) {
				return this.notifyOver(ddSource, e, data);
			}
		});

		this.getView().dragZone.onBeforeDrag = function (data, e) {
			var ret = true;
			var selectedRowInSelection = false;
			var selectedItem = data.grid.getStore().getAt(data.rowIndex);

			Ext.each(data.selections, function (record) {
				if (selectedItem.get("id") === record.get("id")) {
					selectedRowInSelection = true;
				}
				if (record.get("filename") === ".." || record.getDisabled()) {
					ret = false;
					return false;
				}
			});

			if (selectedRowInSelection) {
				return ret;
			} else {

				if (selectedItem.get("filename") === ".." || selectedItem.getDisabled()) {
					return false;
				} else {
					return true;
				}
			}
		}
	},

	/**
	 * Event handler for the 'drop' event which happens if the user drops a file
	 * from the desktop to the {@link #wrap} element.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onDropItemToUpload : function (event)
	{
		event.stopPropagation();
		event.preventDefault();

		var files = event.browserEvent.target.files || event.browserEvent.dataTransfer.files;
		Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, this.getStore());
	},

	onContextViewModeChange: function (context, newViewMode, oldViewMode) {
		var compact = newViewMode === Zarafa.plugins.files.data.ViewModes.RIGHT_PREVIEW;
		this.getColumnModel().setCompactView(compact);
	},

	onCellContextMenu: function (grid, rowIndex, cellIndex, event) {
		var sm = this.getSelectionModel();
		if (sm.hasSelection()) {
			if (!sm.isSelected(rowIndex)) {
				sm.clearSelections();
				sm.selectRow(rowIndex);
			}
		} else {
			sm.selectRow(rowIndex);
		}

		var records = sm.getSelections();
		var show = !records.some(function(record) {
			return record.getDisabled() === true
		}, this);

		if (show) {
			Zarafa.core.data.UIFactory.openDefaultContextMenu(records, {
				position: event.getXY(),
				context : this.context
			});
		}
	},

	onRowBodyContextMenu: function (grid, rowIndex, event)
	{
		this.onCellContextMenu(grid, rowIndex, -1, event);
	},

	/**
	 *
	 * @param grid
	 * @param rowIndex
	 */
	onRowDblClick: function (grid, rowIndex)
	{
		var store = this.getStore();
		var record = store.getAt(rowIndex);
		var officeFiletypes = container.getSettingsModel().get('zarafa/v1/plugins/files/onlyoffice_filetypes');
		var officeEnabled = container.getSettingsModel().get('zarafa/v1/plugins/files/onlyoffice_enabled');
		officeFiletypes = (officeFiletypes || '').split(',');

		if (record.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
			Zarafa.plugins.files.data.Actions.openFolder(this.model, record.get('entryid'));
		} else if(officeEnabled && officeFiletypes.some((type) => record.get('folder_id').endsWith(type))) { // If onlyoffice document
			Zarafa.plugins.files.data.Actions.openTab(record);
		} else {
			Zarafa.plugins.files.data.Actions.downloadItem(record);
		}
	},

	onKeyDelete: function (key, event)
	{
		var selections = this.getSelectionModel().getSelections();
		Zarafa.plugins.files.data.Actions.deleteRecords(selections);
	},

	/**
	 * Event handler triggered when row was selected in grid.
	 * set single selected record in preview panel if view mode is
	 * other then no preview.
	 *
	 * @param {Ext.grid.RowSelectionModel} selectionModel The selection model used by the grid.
	 * @param {Number} rowNumber The index of selected row.
	 * @param {Zarafa.plugins.files.data.FilesRecord} record The record which is selected.
	 */
	onRowSelect: function (selectionModel, rowNumber, record)
	{
		var viewMode = this.context.getCurrentViewMode();
		var count = selectionModel.getCount();
		if (viewMode === Zarafa.plugins.files.data.ViewModes.NO_PREVIEW || count > 1 || record.getDisabled()) {
			return;
		}
		if (count === 1) {
			var id = container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/";
			if (record.get('folder_id') !== id) {
				this.model.setPreviewRecord(record);
			}
			return;
		}
		this.model.setPreviewRecord(undefined);
	},

	/**
	 * Event handler triggered when selection gets changed in grid.
	 * It will set the selected record in preview panel. If selection
	 * is empty then clear the preview panel.
	 *
	 * @param {Ext.grid.RowSelectionModel} selectionModel The selection model used by the grid.
	 * @private
	 */
	onSelectionChange: function (selectionModel)
	{
		var selections = selectionModel.getSelections();
		this.model.setSelectedRecords(!Ext.isEmpty(selections) ? selections : undefined);
	}
});

Ext.reg('filesplugin.filesrecordgridview', Zarafa.plugins.files.ui.FilesRecordGridView);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordIconView = Ext.extend(Zarafa.common.ui.DraggableDataView, {
	/**
	 * @cfg {Zarafa.plugins.files.FilesContext} context The context to which this context menu belongs.
	 */
	context : undefined,

	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	dropTarget: undefined,

	keyMap: undefined,

	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}
		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		config.store = Ext.StoreMgr.lookup(config.store);

		config.plugins = Ext.value(config.plugins, []);
		config.plugins.push('zarafa.icondragselectorplugin');

		Ext.applyIf(config, {
			xtype: 'filesplugin.filesrecordiconview',
			cls           : 'zarafa-files-iconview',
			loadingText   : _('Loading files') + '...',
			deferEmptyText: false,
			autoScroll    : true,
			emptyText     : '<div class="emptytext">' + _('There are no items to show in this view') + '</div>',
			overClass     : 'zarafa-files-iconview-over',
			tpl           : this.initTemplate(),
			multiSelect   : true,
			selectedClass : 'zarafa-files-iconview-selected',
			itemSelector  : 'div.zarafa-files-iconview-thumb',
			enableDragDrop: true,
			ddGroup       : 'dd.filesrecord'
		});

		Zarafa.plugins.files.ui.FilesRecordIconView.superclass.constructor.call(this, config);

		this.initEvents();
	},

	initTemplate: function () {
		return new Ext.XTemplate(
			'<div style="height: 100%; width: 100%; overflow: auto;">',
				'<tpl for=".">',
					'<div class="zarafa-files-iconview-thumb">',
						'<div class="zarafa-files-iconview-icon {.:this.getTheme} {.:this.getHidden}"></div>',
						'<div class="zarafa-files-iconview-subject">{filename:htmlEncode}</div>',
					'</div>',
				'</tpl>',
			'</div>',
			{
				getHidden: function (record) {
					if (record.filename === "..") {
						return "files_type_hidden";
					}
					return "";
				},
				getTheme: function (record) {
					switch (record.type) {
						case Zarafa.plugins.files.data.FileTypes.FOLDER:
							return Zarafa.plugins.files.data.Utils.File.getIconClass("folder");
							break;
						case Zarafa.plugins.files.data.FileTypes.FILE:
							return Zarafa.plugins.files.data.Utils.File.getIconClass(record.filename);
							break;
						default:
							return 'files48icon_blank';
							break;
					}
				}
			}
		);
	},

	initEvents: function ()
	{
		this.on({
			'contextmenu'    : this.onFilesIconContextMenu,
			'dblclick'       : this.onIconDblClick,
			'selectionchange': this.onSelectionChange,
			'afterrender'    : this.onAfterRender,
			scope            : this
		});

		this.mon(this.store, 'createfolder', this.onCreateFolder, this);
	},

	/**
	 * Event handler triggers when folder is record is created.
	 *
	 * @param {Zarafa.plugins.files.data.FilesRecordStore} store The store which fires this event.
	 * @param {String} parentFolderId The parentFolderId under which folder was created.
	 * @param {Object} data The data contains the information about newly created folder.
	 */
	onCreateFolder : function (store, parentFolderId, data)
	{
		if (store.getPath() === parentFolderId) {
			var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, data);
			store.add(record);
			store.on("update", Zarafa.plugins.files.data.Actions.doRefreshIconView, Zarafa.plugins.files.data.Actions, {single: true});
			record.commit(true);
		}
	},

	onAfterRender: function ()
	{
		// Add key maps only while keyboard shortcut is enable
		if (Zarafa.core.KeyMapMgr.isGloballyEnabled()) {
			this.keyMap = new Ext.KeyMap(this.getEl(), {
				key: Ext.EventObject.DELETE,
				fn: this.onKeyDelete.createDelegate(this)
			});

			// Disable all other key maps for this element
			Zarafa.core.KeyMapMgr.disableAllKeymaps(this.getEl());
		}

		this.initDropTarget();
	},

	onKeyDelete: function (key, event) {
		var selections = this.getSelectedRecords();
		Zarafa.plugins.files.data.Actions.deleteRecords(selections);
	},

	initDropTarget: function () {
		var iconViewDropTargetEl = this.getEl();

		var wrap = iconViewDropTargetEl.wrap({cls: 'x-form-field-wrap'});
		this.mon(wrap, 'drop', this.onDropItemToUpload, this);

		this.dropTarget = new Ext.dd.DropTarget(iconViewDropTargetEl, {
			ddGroup    : 'dd.filesrecord',
			copy       : false,
			fileStore  : this.getStore(),
			model: this.model,
			notifyDrop : function (ddSource, e, data) {

				if (this.notifyOver(ddSource, e, data) !== this.dropAllowed) {
					return false;
				}

				var dragData = ddSource.getDragData(e);

				if (Ext.isDefined(dragData)) {
					var cellindex = dragData.index;
					var dropTarget = this.fileStore.getAt(cellindex);
					if (Ext.isDefined(cellindex) && dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {

						Ext.each(data.selections, function (record) {
							record.setDisabled(true);
						});
						return Zarafa.plugins.files.data.Actions.moveRecords(data.selections, dropTarget, {hierarchyStore: this.model.getHierarchyStore()});
					}
				}

				return false;
			},
			notifyOver : function (ddSource, e, data) {
				var dragData = ddSource.getDragData(e);
				var ret = this.dropNotAllowed;

				if (Ext.isDefined(dragData)) {
					var cellindex = dragData.index;

					if (Ext.isDefined(cellindex)) {
						var dropTarget = this.fileStore.getAt(cellindex);

						if (dropTarget.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
							ret = this.dropAllowed;
						}

						Ext.each(data.selections, function (record) {
							var srcId = record.get("id");
							var trgId = dropTarget.get("id");
							if (srcId === trgId || record.get("filename") === ".." || trgId.slice(0, srcId.length) === srcId) {
								ret = this.dropNotAllowed;
								return false;
							}
						}, this);
					}
				}
				return ret;
			},
			notifyEnter: function (ddSource, e, data) {
				return this.notifyOver(ddSource, e, data);
			}
		});

		this.dragZone.onBeforeDrag = function (data, e) {
			var ret = true;
			var selectedRowInSelection = false;
			var selectedItem = this.view.getStore().getAt(data.index);

			Ext.each(data.selections, function (record) {
				if (selectedItem.get("id") === record.get("id")) {
					selectedRowInSelection = true;
				}
				if (record.getDisabled()) {
					ret = false;
					return false;
				}
			});

			if (selectedRowInSelection) {
				return ret;
			} else {

				if (selectedItem.getDisabled()) {
					return false;
				} else {
					return true;
				}
			}
		}
	},

	/**
	 * Event handler for the 'drop' event which happens if the user drops a file
	 * from the desktop to the {@link #wrap} element.
	 * @param {Ext.EventObject} event The event object
	 * @private
	 */
	onDropItemToUpload : function (event)
	{
		event.stopPropagation();
		event.preventDefault();

		var files = event.browserEvent.target.files || event.browserEvent.dataTransfer.files;
		Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, this.getStore());
	},

	onFilesIconContextMenu: function (dataview, index, node, eventObj) {

		if (!dataview.isSelected(node)) {
			dataview.select(node);
		}

		var records = dataview.getSelectedRecords();

		var show = true;
		Ext.each(records, function (record) {
			if (record.getDisabled() === true) {
				show = false;
				return;
			}
		});

		if (show) {
			Zarafa.core.data.UIFactory.openDefaultContextMenu(records, {
				position: eventObj.getXY(),
				context : this.context
			});
		}
	},

	onIconDblClick: function (dataview, index, node, event)
	{
		var store = this.getStore();
		var record = store.getAt(index);
		var officeFiletypes = container.getSettingsModel().get('zarafa/v1/plugins/files/onlyoffice_filetypes');
		var officeEnabled = container.getSettingsModel().get('zarafa/v1/plugins/files/onlyoffice_enabled');
		officeFiletypes = (officeFiletypes || '').split(',');

		if (record.get('type') === Zarafa.plugins.files.data.FileTypes.FOLDER) {
			Zarafa.plugins.files.data.Actions.openFolder(this.model, record.get('entryid'));
		} else if(officeEnabled && officeFiletypes.some((type) => record.get('folder_id').endsWith(type))) { // If onlyoffice document
			Zarafa.plugins.files.data.Actions.openTab(record);
		} else {
			Zarafa.plugins.files.data.Actions.downloadItem(record);
		}
	},

	onSelectionChange: function (dataView, selections) {
		var records = dataView.getSelectedRecords();

		// FIXME: In grommunio Web context who contains the dataViews are not statefulRecordSelection but in files context
		//  we have preview panel and because of that we need to make file context model statefulRecordSelection to true.
		//  here we face an issue when we delete more than one record from icon views because onSelectionChange function
		//  also called to deselect the record. when record was deleted at that time dataView.getSelectedRecords returns
		//  an array whose first element value is undefined because getSelectedRecords gets the record by using viewIndex
		//  from store and at this time length of records was changed due to first deletion and because of that it will give
		//  undefined as first element of an array.
		records = records.filter(function(record) {
			return Ext.isDefined(record);
		}, this);

		this.model.setSelectedRecords(records, false);

		var viewMode = this.context.getCurrentViewMode();
		var count = records.length;

		if (viewMode !== Zarafa.plugins.files.data.ViewModes.NO_PREVIEW) {
			if (count !== 1) {
				this.model.setPreviewRecord(undefined);
			} else if (count == 1) {
				if (records[0].get('folder_id') !== (container.getSettingsModel().get('zarafa/v1/contexts/files/files_path') + "/") && records[0].get('filename') !== "..") {
					this.model.setPreviewRecord(records[0]);
				} else {
					this.model.setPreviewRecord(undefined);
				}
			}
		}
	}
});

Ext.reg('filesplugin.filesrecordiconview', Zarafa.plugins.files.ui.FilesRecordIconView);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesRecordViewPanel = Ext.extend(Ext.Panel, {

	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.filesrecordviewpanel',
			border: false,
			cls   : 'zarafa-filesviewpanel',
			layout: 'zarafa.collapsible',
			items : [{
				xtype: 'filesplugin.filesrecorddetailspanel'
			}]
		});

		Zarafa.plugins.files.ui.FilesRecordViewPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filesrecordviewpanel', Zarafa.plugins.files.ui.FilesRecordViewPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesTopToolbar
 * @extends Ext.Toolbar
 * @xtype filesplugin.filestoptoolbar
 *
 * The top toolbar for the files explorer.
 */
Zarafa.plugins.files.ui.FilesTopToolbar = Ext.extend(Ext.Toolbar, {
	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.model) && Ext.isDefined(config.context)) {
			config.model = config.context.getModel();
		}

		Ext.applyIf(config, {
			cls: 'files_top_toolbar',
			items: [{
				xtype: 'filesplugin.navigationbar',
				model: config.model,
				accountsStore : config.context.getAccountsStore()
			}, {
				xtype: 'tbfill'
			}, {
				xtype: 'filesplugin.quotabar',
				model: config.model,
				accountsStore : config.context.getAccountsStore()
			}]
		});
		Zarafa.plugins.files.ui.FilesTopToolbar.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.filestoptoolbar', Zarafa.plugins.files.ui.FilesTopToolbar);
Ext.namespace('Zarafa.plugins.files.ui');

Zarafa.plugins.files.ui.FilesTreeContextMenu = Ext.extend(Zarafa.core.ui.menu.ConditionalMenu, {
	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			items: [
				this.createContextActionItems(),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.treecontextmenu.actions', this),
				{xtype: 'menuseparator'},
				container.populateInsertionPoint('plugin.files.treecontextmenu.options', this)
			]
		});

		Zarafa.plugins.files.ui.FilesTreeContextMenu.superclass.constructor.call(this, config);
	},

	/**
	 * Function create an array which used to create files tree context menu.
	 * @return {Array} Array which contains the context menu items.
	 */
	createContextActionItems: function () {
		return [{
			xtype     : 'zarafa.conditionalitem',
			text      : _('New Folder'),
			iconCls   : 'files_icon_action files_icon_action_new_folder',
			handler   : this.onContextItemNewFolder,
			scope     : this
		},{
			xtype     : 'zarafa.conditionalitem',
			text      : _('Refresh'),
			iconCls   : 'files_icon_action icon_cache',
			handler   : this.onContextItemRefresh,
			beforeShow: this.onBeforeShowItem,
			name      : "refresh",
			scope     : this
		},{
			xtype     : 'zarafa.conditionalitem',
			text      : _('Rename'),
			iconCls   : 'files_icon_action files_icon_action_edit',
			handler   : this.onContextItemRename,
			name      : "rename",
			beforeShow: this.onBeforeShowItem,
			scope     : this
		}, {
			xtype     : 'zarafa.conditionalitem',
			text      : _('Delete'),
			iconCls   : 'files_icon_action files_icon_action_delete',
			name      : "delete",
			handler   : this.onContextItemDelete,
			beforeShow: this.onBeforeShowItem,
			scope     : this
		}];
	},

	/**
	 * Event handler triggered before the "Refresh", "Rename" and "Delete"
	 * context menu item show.
	 *
	 * @param {Ext.Button} item The item which is going to show.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} record The folder record on which
	 * this context menu item shows.
	 */
	onBeforeShowItem : function (item, record)
	{
		var path = Zarafa.plugins.files.data.Utils.File.stripAccountId(record.get('folder_id'));

		if (item.name === "refresh") {
			item.setVisible(path === "/");
			return;
		}
		item.setVisible(path !== "/");
	},

	/**
	 * Handler called when "Refresh" button is pressed.
	 * it will reload the {@link Zarafa.plugins.files.ui.NavigatorTreePanel NavigatorTreePanel}.
	 */
	onContextItemRefresh: function()
	{
		this.model.getHierarchyStore().reload();
	},

	/**
	 * Handler called when "Delete" button is pressed.
	 * it will delete the folder from {@link Zarafa.plugins.files.ui.NavigatorTreePanel NavigatorTreePanel}.
	 */
	onContextItemDelete: function ()
	{
		Zarafa.plugins.files.data.Actions.deleteRecords(this.records);
	},

	/**
	 * Handler called when "New Folder" button is pressed.
	 * It will open the {@link Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel CreateFolderContentPanel}
	 */
	onContextItemNewFolder: function ()
	{
		Zarafa.plugins.files.data.Actions.createFolder(this.model, undefined, this.records);
	},

	/**
	 * Handler called when "Rename" button is pressed.
	 * It is used to rename the folder of {@link Zarafa.plugins.files.ui.NavigatorTreePanel NavigatorTreePanel}.
	 */
	onContextItemRename: function ()
	{
		Zarafa.plugins.files.data.Actions.openRenameDialog(this.records);
	}
});

Ext.reg('filesplugin.filestreecontextmenu', Zarafa.plugins.files.ui.FilesTreeContextMenu);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FolderNodeUI
 * @extends Ext.tree.TreeNodeUI
 *
 * {@link Ext.tree.TreeNodeUI TreeNodeUI} has limitation that you can't add html tags to textNode as it should only
 * contain text, not anything else. but we need to add counter of folders to textnode and also make some changes according to
 * counters shown or not. So this class changes the template that is used to create {@link Zarafa.hierarchy.ui.FolderNode FolderNode}
 * so we can create a new element for showing counters so it will not interfere with default functionality of text nodes.
 *
 * The default layout of extjs for treenodes is something like this
 <pre><code>
 <div unselectable="on" class="x-tree-node-el x-tree-node-leaf x-unselectable" >	// element node
 <span class="x-tree-node-indent">		// for indentation
 <img class="x-tree-elbow-line">
 <img class="x-tree-elbow-line">
 </span>
 <img class="x-tree-ec-icon x-tree-elbow">	// expand icon
 <img unselectable="on" class="x-tree-node-icon icon_folder_note">	// folder icon
 <a tabindex="1" href="" class="x-tree-node-anchor" hidefocus="on">
 <span unselectable="on"> node text </span>						// text node
 </a>
 </div>
 </code></pre>
 *  but for our custom needs we need to change that layout to accommodate counters also
 <pre><code>
 <div unselectable="on" class="x-tree-node-el x-tree-node-leaf x-unselectable" >	// element node
 <span class="x-tree-node-indent">		// for indentation
 <img class="x-tree-elbow-line">
 <img class="x-tree-elbow-line">
 </span>
 <img class="x-tree-ec-icon x-tree-elbow">	// expand icon
 <img unselectable="on" class="x-tree-node-icon icon_folder_note">	// folder icon
 <a tabindex="1" href="" class="x-tree-node-anchor" hidefocus="on">
 <span unselectable="on" class="zarafa-hierarchy-node-text"> node text </span>	// text node
 <span unselectable="on" class="zarafa-hierarchy-node-unread-count">(2)</span>	// counter node
 </a>
 </div>
 </code></pre>
 */
Zarafa.plugins.files.ui.FolderNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
	/**
	 * Function will render {@link Zarafa.hierarchy.ui.FolderNode FolderNode} based on modified template for
	 * our custom needs.
	 * @param {Zarafa.hierarchy.ui.FolderNode} n tree node.
	 * @param {Object} a config object of {@link Zarafa.hierarchy.ui.FolderNode FolderNode}.
	 * @param {Ext.Element} targetNode element in which {@link Zarafa.hierarchy.ui.FolderNode FolderNode} will be rendered.
	 * @param {Boolean} bulkRender
	 */
	renderElements: function (n, a, targetNode, bulkRender) {
		// add some indent caching, this helps performance when rendering a large tree
		this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
		var cb = Ext.isBoolean(a.checked);
		var checkboxMarkup = '';
		if (cb) {
			var checkboxId = Ext.id(null, 'zarafa-files-node-cb-');
			checkboxMarkup =
				'<input class="x-tree-node-cb zarafa-hierarchy-node-cb" type="checkbox" id="' +
				checkboxId +
				'" name="' +
				checkboxId +
				'"' +
				(a.checked ? ' checked="checked"' : '') +
				' />';
		}
		var icon =
				'<img src="' +
				(a.icon || this.emptyIcon) +
				'" class="x-tree-node-icon" unselectable="on" />',
			nel,
			href = a.href ? a.href : Ext.isGecko ? '' : '#',
			buf =
				'<li class="x-tree-node">' +
				'<div ext:tree-node-id="' +
				n.id +
				'" class="x-tree-node-el x-tree-node-leaf x-unselectable zarafa-hierarchy-node" unselectable="on">' +
				// indent space
				'<span class="x-tree-node-indent">' +
				this.indentMarkup +
				'</span>' +
				// expand icon
				'<img src="' +
				this.emptyIcon +
				'" class="x-tree-ec-icon x-tree-elbow" />' +
				// checkbox
				checkboxMarkup +
				// node icon
				icon +
				// node element (this.elNode)
				'<a hidefocus="on" class="x-tree-node-anchor zarafa-hierarchy-node-anchor" ' +
				'href="' +
				href +
				'" tabIndex="1" ' +
				(a.hrefTarget ? ' target="' + a.hrefTarget + '"' : '') +
				'>' +
				// hierarchy node text (this.textNode)
				'<span unselectable="on">' +
				(n.tpl ? n.tpl.apply(a) : n.text) +
				'</span>' +
				'<span class="zarafa-hierarchy-node-backend" unselectable="on"></span>' +
				'</a>' +
				'</div>' +
				'<ul class="x-tree-node-ct" style="display:none;"></ul>' +
				'</li>';

		if (
			bulkRender !== true &&
			n.nextSibling &&
			(nel = n.nextSibling.ui.getEl())
		) {
			this.wrap = Ext.DomHelper.insertHtml('beforeBegin', nel, buf);
		} else {
			this.wrap = Ext.DomHelper.insertHtml('beforeEnd', targetNode, buf);
		}

		this.elNode = this.wrap.childNodes[0];
		this.ctNode = this.wrap.childNodes[1];
		var cs = this.elNode.childNodes;
		this.indentNode = cs[0];
		this.ecNode = cs[1];
		this.iconNode = cs[2];
		var index = 3;
		if (cb) {
			this.checkbox = cs[2];
			this.iconNode = cs[3];
			index++;
		}
		this.anchor = cs[index];
		this.textNode = cs[index].firstChild;

		this.folderBackendNode = cs[index].firstChild.nextSibling;
		// Apply some optional CSS classes
		var elNode = Ext.get(this.elNode);
		var iconNode = Ext.get(this.iconNode);
		var containerNode = Ext.get(this.wrap);

		if (!Ext.isEmpty(a.cls)) {
			elNode.addClass(a.cls);
		}

		if (a.icon) {
			iconNode.addClass('x-tree-node-inline-icon');
		}

		if (a.iconCls) {
			iconNode.addClass(a.iconCls);
		}

		if (!Ext.isEmpty(a.containerCls)) {
			containerNode.addClass(a.containerCls);
		}
		this.showFolderBackend(n);
	},

	/**
	 * Called when the node is going to change the class.
	 * @param {Ext.tree.Node} node The node which must be updated
	 * @param {String} cls The class which must be applied to the {@link #iconNode}
	 * @private
	 */
	onContainerClsChange: function (node, cls, oldCls) {
		if (this.rendered) {
			var containerNode = Ext.get(this.wrap);
			if (!Ext.isEmpty(oldCls)) {
				containerNode.replaceClass(oldCls, cls);
			} else {
				containerNode.addClass(cls);
			}
		}
	},

	/**
	 * Called when the node has changed the text, this will re-apply the text to the node
	 * @param {Ext.tree.Node} node The node which must be updated
	 * @param {String} text The text which must be applied to the node
	 * @param {String} oldText The previous text which was set on the node
	 * @private
	 */
	onTextChange: function (node, text, oldText) {
		if (this.rendered) {
			this.textNode.innerHTML = node.tpl
				? node.tpl.apply(node.attributes)
				: text;
		}
	},

	/**
	 * Called when the node is going to change the icon.
	 * @param {Ext.tree.Node} node The node which must be updated
	 * @param {String} iconCls The iconCls which must be applied to the {@link #iconNode}
	 * @param {String} oldIconCls The old iconCls which must be removed from the {@link #iconNode}.
	 * @private
	 */
	onIconChange: function (node, iconCls, oldIconCls) {
		if (this.rendered) {
			var iconNode = Ext.get(this.iconNode);
			if (!Ext.isEmpty(oldIconCls)) {
				iconNode.replaceClass(oldIconCls, iconCls);
			} else {
				iconNode.addClass(iconCls);
			}
		}
	},

	/**
	 * Function is used to show backend name along with {@link Zarafa.plugins.files.data.FilesFolderRecord folder} folder name.
	 * @param {Zarafa.plugins.files.ui.FilesFolderNode} node which has to show backend name.
	 */
	showFolderBackend: function (node) {
		var folder = node.getFolder();

		if (!Ext.isDefined(folder) || !folder.isSubTreeFolder()) {
			return;
		}

		var ownerNode = Ext.get(this.folderBackendNode);
		var store = folder.getFilesStore();
		var backendName = store.getBackend();
		if (backendName === 'Owncloud' || backendName === 'Default') {
			backendName = _('Default');
		}
		var ownerName = ' - ' + backendName;

		ownerNode.update(ownerName);
		ownerNode.repaint();
	},
});
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.MultipleFileUploadField
 * @extends Ext.ux.form.FileUploadField
 * @xtype filesplugin.multiplefileuploadfield
 *
 * Creates a file upload field.
 */
Zarafa.plugins.files.ui.MultipleFileUploadField = Ext.extend(Ext.ux.form.FileUploadField, {
	// override
	createFileInput: function () {
		var opt = {
			id  : this.getFileInputId(),
			name: this.name || this.getId(),
			cls : 'x-form-file',
			tag : 'input',
			type: 'file',
			size: 1
		};

		opt.multiple = 'multiple';
		this.fileInput = this.wrap.createChild(opt);
	},

	// override
	bindListeners  : function () {
		this.fileInput.on({
			scope     : this,
			mouseenter: function () {
				this.button.addClass(['x-btn-over', 'x-btn-focus'])
			},
			mouseleave: function () {
				this.button.removeClass(['x-btn-over', 'x-btn-focus', 'x-btn-click'])
			},
			mousedown : function () {
				this.button.addClass('x-btn-click')
			},
			mouseup   : function () {
				this.button.removeClass(['x-btn-over', 'x-btn-focus', 'x-btn-click'])
			},
			change    : function () {
				var v = this.fileInput.dom.value;
				if (this.fileInput.dom.files.length > 1) {
					v = this.fileInput.dom.files.length + ' ' + _('files selected');
				}
				this.setValue(v);
				this.fireEvent('fileselected', this, v);
			}
		});
	}
});

Ext.reg('filesplugin.multiplefileuploadfield', Zarafa.plugins.files.ui.MultipleFileUploadField);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.Tree
 * @extends Ext.tree.TreePanel
 * @xtype filesplugin.tree
 *
 * The hierarchy tree panel implementation for files.
 */
Zarafa.plugins.files.ui.Tree = Ext.extend(Ext.tree.TreePanel, {

	/**
	 * @cfg {String} account id that should be loaded.
	 */
	accountFilter: undefined,

	/**
	 * @cfg {Object} config option for {@link Zarafa.hierarchy.ui.FolderNode foldernode}
	 */
	nodeConfig : undefined,

	/**
	 * @cfg {String} IPMFilter The IPM String on which the hierarchy must be filtered
	 */
	FilesFilter : undefined,

	/**
	 * @cfg {Object} treeSorter a {@link Ext.Ext.tree.TreeSorter} config or {@link Boolean}
	 * to sort the {@linkZarafa.plugins.files.ui.Tree Tree}
	 * Defaults to <code>true</code>.
	 */
	treeSorter : true,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.accountFilter)) {
			this.accountFilter = config.accountFilter;
		}

		if (!config.store) {
			config.store = config.model.getHierarchyStore();
		}

		Ext.applyIf(config, {
			xtype : 'filesplugin.tree',
			enableDD : true,
			border : false,
			ddGroup : 'dd.filesrecord',
			ddAppendOnly : true,
			pathSeparator: '&',
			root : new Zarafa.plugins.files.ui.FilesHierarchyRootNode({
				text : 'Files',
				id : '#R#',
				// TODO: Find why we need this
				cc : false,
				leaf : false,
				expanded : true,
				uiProvider : Ext.tree.RootTreeNodeUI
			}),
			expanded : true,
			rootVisible : false,
			autoScroll : true,
			maskDisabled : true
		});
		Zarafa.plugins.files.ui.Tree.superclass.constructor.call(this, config);

		if(this.treeSorter && !(this.treeSorter instanceof Ext.tree.TreeSorter)) {
			this.treeSorter = new Ext.tree.TreeSorter(this);
		}
	},

	/**
	 * Init the events.
	 */
	initEvents : function()
	{
		Zarafa.plugins.files.ui.Tree.superclass.initEvents.apply(this, arguments);
		this.on('afterrender', this.onAfterRenderTree, this);
	},

	/**
	 * Function will initialize {@link Zarafa.plugins.files.ui.Tree Tree}.
	 * @protected
	 */
	initComponent : function()
	{
		// Initialize the loader
		if (!this.loader) {
			this.loader = new Zarafa.plugins.files.data.NavigatorTreeLoader({
				tree : this,
				store : this.store,
				nodeConfig : this.nodeConfig,
				deferredLoading : this.deferredLoading
			});
		}

		Zarafa.plugins.files.ui.Tree.superclass.initComponent.apply(this, arguments);

		// create load mask
		if(this.loadMask) {
			this.on('render', this.createLoadMask, this);
		}
	},

	/**
	 * Function will create {@link Zarafa.common.ui.LoadMask} which will be shown
	 * when loading the {@link Zarafa.plugins.files.ui.Tree Tree}.
	 * @private
	 */
	createLoadMask : function()
	{
		this.loadMask = new Zarafa.common.ui.LoadMask(this.ownerCt.getEl(), Ext.apply({ store: this.store }, this.loadMask));
		if (this.store.isLoading()) {
			this.loadMask.show();
		}
	},

	/**
	 * Event handler triggered when tree panel rendered.
	 * It will also listen for the {@link Ext.tree.DefaultSelectionModel#selectionchange selection change} event.
	 * @private
	 */
	onAfterRenderTree : function()
	{
		this.mon(this.selModel, 'selectionchange', this.onSelectionChange, this);
	},

	/**
	 * Event handler triggered when folder is changed in hierarchy.
	 * It will check that selected node(folder) is Expandable and currently not Expanded then
	 * expand the selected node.
	 *
	 * @param {Ext.tree.DefaultSelectionModel} selModel The {@link Ext.tree.DefaultSelectionModel DefaultSelectionModel}.
	 * @param {TreeNode} node The node to select
	 */
	onSelectionChange : function(selModel, selectedNode)
	{
		if (Ext.isEmpty(selectedNode)) {
			return;
		}

		if(selectedNode.isExpandable() && !selectedNode.isExpanded()){
			selectedNode.expand(false, true);
		}
	},

	/**
	 * Manual selection of the treeNode to which the folder is attached in the tree.
	 * This will first ensure the given folder {@link #ensureFolderVisible is visible}
	 * and will then {@link Ext.tree.DefaultSelectionModel#select select the given node} in the tree.
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder to select
	 * @param {Boolean} ensureVisibility True to make given folder visible in screen moving scroll bar.
	 * @return {Boolean} True when the TreeNode for the given folder existed, and could be selected.
	 */
	selectFolderInTree : function(folder, ensureVisibility)
	{
		var treeNode;

		if (ensureVisibility !== false) {
			treeNode = this.ensureFolderVisible(folder);
		} else {
			treeNode = this.getNodeById(folder.id);
		}

		if (treeNode) {
			this.getSelectionModel().select(treeNode, undefined, ensureVisibility);
			return true;
		}
		return false;
	},
	/**
	 * This will call {@link Ext.tree.TreeNode#ensureVisible} in the node
	 * to which the given folder is attached. This will first make sure
	 * that the actual folder has been loaded by the parent folder.
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder which must
	 * be made visible.
	 * @return {Zarafa.hierarchy.ui.FolderNode} The node which was made visible,
	 * false if the folder was not found in the hierarchy.
	 */
	ensureFolderVisible : function(folder)
	{
		var treeNode = this.getNodeById(folder.id);

		// If the tree has not been found, take the parent folder.
		if (!treeNode) {
			var parentfolder = folder.getParentFolder();
			if (!parentfolder) {
				return false;
			}

			// With the parent folder we can ensure that folder is visible,
			// and expand it to ensure the subfolders will be rendered.
			var parentNode = this.ensureFolderVisible(parentfolder);
			if (!parentNode) {
				return false;
			}

			parentNode.expand();

			// With luck, the node has now been created.
			treeNode = this.getNodeById(folder.id);
		}

		if (treeNode) {
			// Ensure that the given node is visible.
			// WARNING: After this call, treeNode is destroyed
			// as the TreeLoader will have reloaded the parent!
			treeNode.ensureVisible();

			// Obtain the new treeNode reference, update the UI and return it.
			treeNode = this.getNodeById(folder.id);
			treeNode.update(true);
			return treeNode;
		}

		return false;
	},

	/**
	 * The filter which is applied for filtering nodes from the
	 * {@link Zarafa.plugins.files.ui.Tree Tree}.
	 *
	 * @param {Object} folder the folder to filter
	 * @return {Boolean} true to accept the folder
	 */
	nodeFilter : function (folder)
	{
		var hide = false;

		if (Ext.isDefined(this.accountFilter)) {
			hide = folder.getFilesStore().get('entryid') !== this.accountFilter;
		}

		if (!hide && Ext.isDefined(this.FilesFilter)) {
			hide = folder.get('object_type') !== Zarafa.plugins.files.data.FileTypes.FOLDER;
		}

		if( !hide && folder.isHomeFolder()) {
			hide = true;
		}

		return !hide;
	}
});

Ext.reg('filesplugin.tree', Zarafa.plugins.files.ui.Tree);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.TreeSorter
 * @extends Ext.tree.TreeSorter
 *
 * Special sorting class for the {@link Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel AttachFromFilesTreePanel},
 * this enables special sorting of the folders and files in alphabetical order.
 */
Zarafa.plugins.files.ui.TreeSorter = Ext.extend(Ext.tree.TreeSorter, {
	/**
	 * @constructor
	 * @param {Ext.tree.Tree} tree The tree which this sorter is being applied on
	 * @param {Object} config Configuration object
	 */
	constructor : function(tree, config)
	{
		Zarafa.plugins.files.ui.TreeSorter.superclass.constructor.apply(this, arguments);

		this.sortFn = this.hierarchySort.createDelegate(this);
	},

	/**
	 * Special sorting function which applies special sorting when the folder and field
	 * shows in tree.
	 *
	 * @param {Ext.tree.Node} nodeOne The first node to be compared
	 * @param {Ext.tree.Node} nodeTwo The second node to be compared
	 * @private
	 */
	hierarchySort : function(nodeOne, nodeTwo)
	{
		var nodeOneAttributes = nodeOne.attributes;
		var nodeTwoAttributes = nodeTwo.attributes;

		var isNodeOneFolder = nodeOneAttributes.isFolder;
		var isNodeTwoFolder = nodeTwoAttributes.isFolder;

		// Both the nodes are folder.
		var bothFolders = isNodeOneFolder && isNodeTwoFolder;
		// Both the nodes are files.
		var bothNotFolders = !isNodeOneFolder && !isNodeTwoFolder;

		if(bothFolders || bothNotFolders) {
			var nodeOneText = nodeOneAttributes.text,
				nodeTwoText = nodeTwoAttributes.text;

			return nodeOneText.toUpperCase() < nodeTwoText.toUpperCase() ? -1 : 1;
		} else {
			// second node is folder but node one is not folder.
			var nodeTwoIsFolder = !isNodeOneFolder && isNodeTwoFolder;
			return nodeTwoIsFolder ? 1 : -1;
		}
	}
});
Ext.namespace('Zarafa.plugins.files.ui');
/**
 * @class Zarafa.plugins.files.ui.UploadComponent
 * @extends Ext.Component
 * @xtype filesplugin.uploadcomponent
 */
Zarafa.plugins.files.ui.UploadComponent = Ext.extend(Ext.Component, {
	/**
	 * @cfg {Function} callback The callback function which must be called when the
	 * file has be selected from Browser's file selection dialog.
	 */
	callback: Ext.emptyFn,

	/**
	 * @cfg {Object} scope The scope for the {@link #callback} function
	 */
	scope: undefined,

	/**
	 * @cfg {Boolean} multiple The multiple true to allow upload multiple files
	 * else allow single file only. by default it is false.
	 */
	multiple: false,

	/**
	 * @cfg {String} accept the accept define which type of files allow to
	 * show in Browser's file selection dialog. i.e image/* to allow all type of images.
	 */
	accept: undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype: 'filesplugin.uploadcomponent'
		});

		Zarafa.plugins.files.ui.UploadComponent.superclass.constructor.call(this, config);
	},

	/**
	 * Event handler for opening the Browser's file selection dialog.
	 * See {@link #onFileInputChange} for the handling of the selected files.
	 * @private
	 */
	openUploadDialog: function () {
		var uploadEl = this.getUploadEl();

		// Register the change event handler
		// so we detect when the user selects a file.
		uploadEl.on('change', this.onFileInputChange, this);

		// Mimic clicking on the <input> field
		// to open the File Selection dialog.
		uploadEl.dom.click();
	},

	/**
	 * Obtain or instantiate the {@link Ext.Element attachment} &lt;input&gt; element used
	 * for opening the File selection dialog.
	 * @return {Ext.Element} The file input element
	 * @private
	 */
	getUploadEl: function () {
		var uploadInputId = Ext.id(null, 'zarafa-files-upload-input-');
		var uploadEl = Ext.DomHelper.append(Ext.getBody(), {
			cls : 'x-hidden',
			tag : 'input',
			type: 'file',
			id: uploadInputId,
			name: uploadInputId
		});

		if (Ext.isDefined(this.multiple) && this.multiple) {
			uploadEl.multiple = this.multiple;
		}

		if (Ext.isDefined(this.accept)) {
			uploadEl.accept = this.accept;
		}

		uploadEl = Ext.get(uploadEl);
		return uploadEl;
	},

	/**
	 * Event handler which is fired when the {@link #attachEl} has been changed.
	 * @param {Ext.EventObject} event The event
	 * @private
	 */
	onFileInputChange: function (event) {
		var browserEvent = event.browserEvent;
		var uploadEl = Ext.get(browserEvent.target);
		var transfer = browserEvent.dataTransfer;
		var transferFile = transfer ? transfer.files : undefined;
		var files = uploadEl.dom.files || transferFile;

		this.callback.call(this.scope, files);

		// remove attachment element.
		uploadEl.remove();
	}
});

Ext.reg('filesplugin.uploadcomponent', Zarafa.plugins.files.ui.UploadComponent);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.ContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 */
Zarafa.plugins.files.ui.ContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {
	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			// Overridden from Ext.Component
			xtype: 'zarafa.plugins.files.ui.contentpanel',
			layout : 'fit',
			header: false,
			iconCls: 'icon_files',
			border: false,
			items : [{
				xtype: 'zarafa.plugins.files.ui.onlyofficepanel',
				url: config.url,
				record: config.record,
				tabOrder: config.tabOrder
			}]
		});

		Zarafa.plugins.files.ui.ContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('zarafa.plugins.files.ui.contentpanel', Zarafa.plugins.files.ui.ContentPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.OnlyofficePanel
 * @extends Ext.OnlyofficePanel
 */
Zarafa.plugins.files.ui.OnlyofficePanel = Ext.extend(Ext.Panel, {
	/**
	 * The id of the iframe element inside this onlyofficepanel
	 * @property
	 * @type String
	 */
	iframeId : undefined,

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};
		const url = config.url;
		const fileid = config.record.getFileid();

		this.iframeId = 'files-iframe-'+config.tabOrder;
		const baseUrl = window.location.protocol + '//' + window.location.hostname + "/files/index.php/apps/onlyoffice/";
		const filePath = url.split('/').slice(1).join('%2F');

		Ext.applyIf(config, {
			// Overridden from Ext.Component
			xtype: 'zarafa.plugins.files.ui.onlyofficepanel',
			layout : 'fit',
			header: false,
			border: false,
			iconCls: 'icon_files',
			html : {
				tag: 'iframe',
				width: '100%',
				frameborder: '0',
				id: this.iframeId,
				cls: 'files-iframe',
				src: baseUrl + fileid + "?filePath=%2F" + filePath,
				style: 'position:absolute;display:block;top:-50px;height:calc(100% + 50px);',
			},
			listeners: {
				afterrender: this.onAfterRender,
				scope: this
			}
		});

		window.addEventListener("message", (e) => {
			if(e.data === "ocLoginRequired") {
					Ext.MessageBox.show({
					title: _('grommunio Web'),
					msg: _('Authentication required, when using grommunio-files for the first time'),
					buttons: Ext.MessageBox.OK,
					icon: Ext.MessageBox.INFO,
					scope : this
				});
			}
		});

		Zarafa.plugins.files.ui.OnlyofficePanel.superclass.constructor.call(this, config);
	},

	/**
	 * Handler for the afterrender event of this panel. Will set a load mask when opening
	 * a url.
	 */
	onAfterRender: function()
	{
		const iframe = document.getElementById(this.iframeId);
		const iframeWin = iframe.contentWindow || iframe;
		const iframeDoc = iframe.contentDocument || iframeWin.document;

		var script = iframeDoc.createElement("script");
		script.append(`
			window.onload = function() {
				const el = document.getElementById('password');
				if(el) {
					var parentWindow = window.parent;
					parentWindow.postMessage('ocLoginRequired', '*');
				}
			}
	`);
	iframeDoc.documentElement.appendChild(script);
	}
});

Ext.reg('zarafa.plugins.files.ui.onlyofficepanel', Zarafa.plugins.files.ui.OnlyofficePanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.attachfromfilescontentpanel
 *
 * This content panel contains the download tree panel for attaching item to emails.
 */
Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout : 'fit',
			title : _('Add attachment from Files'),
			closeOnSave: true,
			width : 400,
			height : 300,
			items: [{
				xtype :'filesplugin.attachfromfilespanel',
				model : config.model,
				emailrecord: config.record,
			}]
		});

		Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.attachfromfilescontentpanel', Zarafa.plugins.files.ui.dialogs.AttachFromFilesContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.AttachFromFilesGridPanel
 * @extends Ext.grid.GridPanel
 * @xtype filesplugin.attachfromfilesgridpanel
 *
 * This dialog panel will provide facility to user to select the
 * files by checking checkbox from {@link Ext.grid.GridPanel GridPanel}.
 */
Zarafa.plugins.files.ui.dialogs.AttachFromFilesGridPanel = Ext.extend(Ext.grid.GridPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		var model = new Ext.grid.CheckboxSelectionModel({
			checkOnly : true,
			headerCls: 'zarafa-icon-column',
			header : '<p class="icon_all_day">&nbsp;<span class="title">' + _('All Day') + '</span></p>',
			width    : 24
		});

		Ext.applyIf(config, {
			xtype : 'filesplugin.attachfromfilesgridpanel',
			style: {
				paddingLeft: '9px'
			},
			columns: [model,{
				id       : 'type',
				dataIndex: 'type',
				header   : '<p class="icon_index">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
				width    : 24,
				fixed    : true,
				tooltip  : _('Sort by: Type')
			},{
				header   : _('Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : _('Sort by: Filename')
			},{
				header   : _('Size'),
				dataIndex: 'message_size',
				width    : 80,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : _('Sort by: Size')
			}],
			selModel: model,
			loadMask : {
				msg : _('Loading files') + '...'
			},
			store:{
				xtype: 'filesplugin.filesrecordstore'
			},
			viewConfig : {
				forceFit : true
			}
		});

		Zarafa.plugins.files.ui.dialogs.AttachFromFilesGridPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.attachfromfilesgridpanel', Zarafa.plugins.files.ui.dialogs.AttachFromFilesGridPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.AttachFromFilesTreePanel
 * @extends Ext.Panel
 * @xtype filesplugin.attachfromfilespanel
 *
 * This dialog panel will provide the filechooser tree.
 */
Zarafa.plugins.files.ui.dialogs.AttachFromFilesPanel = Ext.extend(Ext.Panel, {

	/**
	 * @var {Zarafa.core.data.IPMRecord} emailRecord
	 */
	emailRecord: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.emailrecord)) {
			this.emailRecord = config.emailrecord;
		}

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.attachfromfilespanel',
			layout: {
				type: 'hbox',
				align: 'stretch'
			},
			border: false,
			header: false,
			items : [{
				xtype : 'filesplugin.tree',
				model : config.model,
				border : true,
				flex : 1,
				ref : 'hierarchyTree'
			}, {
				xtype : 'filesplugin.attachfromfilesgridpanel',
				ref : 'attachGrid',
				flex : 2
			}],
			buttonAlign: 'right',
			buttons: this.createActionButtons()
		});

		Zarafa.plugins.files.ui.dialogs.AttachFromFilesPanel.superclass.constructor.call(this, config);
		if (Ext.isDefined(this.model)) {
			this.mon(this.hierarchyTree, 'click', this.onTreeNodeClick, this);
		}
	},

	/**
	 * Called when a treeNode is click in tree. The corresponding folder is added to,
	 * or removed from the active folder list depending on the state of the check box.
	 * @param {Ext.tree.TreeNode} treeNode tree node.
	 * @private
	 */
	onTreeNodeClick : function(treeNode)
	{
		var folder = treeNode.getFolder();
		this.attachGrid.store.load({
			params: {
				only_files : true
			},
			folder : folder });
	},

	/**
	 * Generate the toolbar buttons.
	 *
	 * @returns {Object}
	 */
	createActionButtons: function () {
		return [{
			xtype  : 'button',
			text   : _('Add attachment'),
			iconCls: 'icon_files_category_white',
			handler: this.downloadSelectedFilesFromFilesToTmp,
			scope  : this
		},{
			text   : _('Cancel'),
			handler: this.onClickCancel,
			scope: this
		}];
	},

	/**
	 * Event handler which is triggered when the user presses the cancel
	 * {@link Ext.Button button}. This will close this dialog.
	 * @private
	 */
	onClickCancel : function()
	{
		this.dialog.close();
	},

	/**
	 * Start to download the files to a temporary folder on the backend.
	 */
	downloadSelectedFilesFromFilesToTmp: function () {
		var selectedNodes = this.attachGrid.getSelectionModel().getSelections();
		var idsList = [];
		var emailRecord = this.dialog.record;

		if (Ext.isDefined(this.emailRecord)) {
			emailRecord = this.emailRecord;
		}

		var attachmentStore = emailRecord.getAttachmentStore();
		var server = container.getServerConfig();
		var max_attachment_size = server.getMaxAttachmentSize();
		var size_exceeded = false;

		Ext.each(selectedNodes, function (node, index) {
			if (node.get('message_size') > max_attachment_size) {
				Zarafa.common.dialogs.MessageBox.show({
					title  : _('Warning'),
					msg    : String.format(_('The file {0} is too large!'), node.get('filename')) + ' (' + _('max') + ': ' + Ext.util.Format.fileSize(max_attachment_size) + ')',
					icon   : Zarafa.common.dialogs.MessageBox.WARNING,
					buttons: Zarafa.common.dialogs.MessageBox.OK
				});
				size_exceeded = true;
				return false;
			}
			idsList.push(node.get('folder_id'));
		});

		if (!size_exceeded) {
			if (idsList.length < 1) {
				Ext.MessageBox.show({
					title  : _('Warning'),
					msg    : _('You have to choose at least one file!'),
					icon   : Zarafa.common.dialogs.MessageBox.WARNING,
					buttons: Zarafa.common.dialogs.MessageBox.OK
				});
			} else {
				try {
					this.disable();
					// TODO: try to remove this single request call.
					container.getRequest().singleRequest(
						'filesbrowsermodule',
						'downloadtotmp',
						{
							ids               : idsList,
							dialog_attachments: attachmentStore.getId()
						},
						new Zarafa.core.data.AbstractResponseHandler({
							doDownloadtotmp: this.addDownloadedFilesAsAttachmentToEmail.createDelegate(this)
						})
					);
				} catch (e) {
					Zarafa.common.dialogs.MessageBox.show({
						title  : _('Warning'),
						msg    : e.getMessage(),
						icon   : Zarafa.common.dialogs.MessageBox.WARNING,
						buttons: Zarafa.common.dialogs.MessageBox.OK
					});
				}
			}
		}
	},

	/**
	 * Convert the serverresponse to {@link Ext.data.Record}.
	 *
	 * @param {Object} downloadedFileInfo
	 * @returns {Ext.data.Record}
	 */
	convertDownloadedFileInfoToAttachmentRecord: function (downloadedFileInfo) {
		var attachmentRecord = Zarafa.core.data.RecordFactory.createRecordObjectByObjectType(Zarafa.core.mapi.ObjectType.MAPI_ATTACH);

		attachmentRecord.set('tmpname', downloadedFileInfo.tmpname);
		attachmentRecord.set('name', downloadedFileInfo.name);
		attachmentRecord.set('size', downloadedFileInfo.size);
		attachmentRecord.set('attach_id', downloadedFileInfo.attach_id);
		return attachmentRecord;
	},

	/**
	 * Add the attachment records to the email.
	 *
	 * @param downloadedFilesInfoArray
	 */
	addDownloadedFilesAsAttachmentToEmail: function (downloadedFilesInfo) {
		var downloadedFilesInfoArray = downloadedFilesInfo.items;
		var emailRecord = this.dialog.record;
		if (Ext.isDefined(this.emailRecord)) {
			emailRecord = this.emailRecord;
		}
		var attachmentStore = emailRecord.getAttachmentStore();

		Ext.each(downloadedFilesInfoArray, function (downloadedFileInfo) {
			var attachmentRecord = this.convertDownloadedFileInfoToAttachmentRecord(downloadedFileInfo);
			attachmentStore.add(attachmentRecord);
		}, this);
		this.dialog.close();
	}
});

Ext.reg('filesplugin.attachfromfilespanel', Zarafa.plugins.files.ui.dialogs.AttachFromFilesPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel
 * @extends Zarafa.core.ui.RecordContentPanel
 * @xtype filesplugin.createfoldercontentpanel
 *
 * Create folder content panel provide the {@link Zarafa.plugins.files.ui.Tree Tree} to create
 * the new folder in that.
 */
Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel = Ext.extend(Zarafa.core.ui.RecordContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'zarafa.createfoldercontentpanel',
			layout: 'fit',
			title : _('Create New Folder'),
			recordComponentPluginConfig : Ext.applyIf(config.recordComponentPluginConfig || {}, {
				allowWrite : true,
				ignoreUpdates : false,
				useShadowStore : true,
				shadowStore : new Zarafa.plugins.files.data.FilesShadowStore()
			}),
			showLoadMask: false,
			showInfoMask: false,
			width: 300,
			height: 350,
			items: [{
				xtype: 'filesplugin.createfolderpanel',
				accountFilter : config.accountFilter,
				parentFolder : config.parentFolder,
				model : config.model
			}]
		});

		Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel.superclass.constructor.call(this, config);
	},
	/**
	 * Fired when the {@link #updaterecord} event has been fired. This will close the panel if the record
	 * is being {@link Ext.data.Record#COMMIT committed}.
	 *
	 * @param {Zarafa.core.ui.RecordContentPanel} contentpanel The record which fired the event
	 * @param {String} action write Action that occurred. Can be one of
	 * {@link Ext.data.Record.EDIT EDIT}, {@link Ext.data.Record.REJECT REJECT} or
	 * {@link Ext.data.Record.COMMIT COMMIT}
	 * @param {Zarafa.core.data.IPMRecord} record The record which was updated
	 * @private
	 * @overridden
	 */
	onUpdateRecord : function(contentpanel, action, record)
	{
		Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel.superclass.onUpdateRecord.apply(this, arguments);

		if (action == Ext.data.Record.COMMIT) {
			this.close();
			return false;
		}
	}
});

Ext.reg('filesplugin.createfoldercontentpanel', Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFolderPanel
 * @extends Ext.Panel
 * @xtype filesplugin.createfolderpanel
 *
 * Panel for users to create folder record in different supported backends.
 */
Zarafa.plugins.files.ui.dialogs.CreateFolderPanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.plugins.files.data.FilesFolderRecord} parentFolder (optional) The parent folder
	 * underneath the new folder will be created.
	 */
	parentFolder : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		config.plugins = Ext.value(config.plugins, []);
		config.plugins.push('zarafa.recordcomponentupdaterplugin');

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.createfolderpanel',
			layout: {
				type: 'fit',
				align: 'stretch'
			},
			border: false,
			header: false,
			items: this.createPanel(config),
			buttonAlign: 'right',
			buttons: [{
				text: _('Ok'),
				ref: '../okButton',
				cls: 'zarafa-action',
				handler : this.onOk,
				scope: this
			},{
				text: _('Cancel'),
				ref: '../cancelButton',
				handler : this.onCancel,
				scope: this
			}]
		});

		Zarafa.plugins.files.ui.dialogs.CreateFolderPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Creates body for {@link Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel CreateFolderContentPanel}
	 * @param {Object} config The config options contains the {@link Zarafa.plugins.files.FilesContextModel FilesContextModel} and
	 * {@link Zarafa.plugins.files.ui.Tree#accountFilter}. which used by {@link Zarafa.plugins.files.ui.Tree Tree}.
	 *
	 * @return {Array} Array which contains configuration object to create the {@link Zarafa.plugins.files.ui.Tree TreePanel}.
	 * @private
	 */
	createPanel : function(config)
	{
		return [{
			xtype : 'panel',
			layout : 'form',
			border : false,
			defaults : {
				anchor :'100%'
			},
			labelAlign : 'top',
			items : [{
				xtype : 'textfield',
				fieldLabel : _('Name'),
				cls: 'form-field-name',
				ref : '../newNameField',
				enableKeyEvents: true,
				listeners: {
					keyup: {
						fn: this.handleEnter,
						buffer: 200
					},
					scope: this
				}
			},{
				xtype : 'filesplugin.tree',
				model : config.model,
				FilesFilter: Zarafa.plugins.files.data.FileTypes.FOLDER,
				bodyCssClass : 'files-create-folder-tree-panel',
				fieldLabel : _('Select where to place the folder'),
				anchor : '100% 80%',
				forceLayout : true,
				ref : '../hierarchyTree',
				accountFilter : config.accountFilter
			}]
		}];
	},

	/**
	 * Function called by Extjs when the panel has been {@link #render rendered}.
	 * At this time all events can be registered.
	 * @private
	 */
	initEvents : function ()
	{
		Zarafa.plugins.files.ui.dialogs.CreateFolderPanel.superclass.initEvents.apply(this, arguments);
		// If there is a folder we should select, the enable the 'load' event handler
		// as we will have to wait until the correct node has been loaded.
		if (this.parentFolder) {
			this.mon(this.hierarchyTree, 'load', this.onTreeNodeLoad, this);
		}
		this.mon(this.hierarchyTree.getSelectionModel(), 'selectionchange', this.onHierarchyNodeSelect, this);
	},

	/**
	 * Event handler which is triggered when the user presses the cancel
	 * {@link Ext.Button button}. This will close this dialog.
	 * @private
	 */
	onCancel : function()
	{
		this.dialog.close();
	},

	/**
	 * Event handler which is triggered when the user presses the ok
	 * {@link Ext.Button button}. function is responsible to create folder
	 * under the respective folder as well as check for duplicate folder.
	 *
	 * @param {Ext.Button} button which triggers this event.
	 * @param {Ext.EventObject} event The event object
	 */
	onOk : function (button, event)
	{
		var folderName = this.newNameField.getValue();

		if (Ext.isEmpty(folderName.trim())) {
			Ext.MessageBox.show({
				title: _('grommunio Web'),
				msg: _('You must specify a name.'),
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.INFO,
				scope : this
			});
			return;
		}

		var childFolders = this.parentFolder.getChildren();
		var folderAlreadyExist = childFolders.some(function(item){
			return item.get('filename') === folderName;
		});

		if (folderAlreadyExist) {
			Zarafa.plugins.files.data.Actions.msgWarning(_('Folder already exists'));
			return;
		} else if (!Zarafa.plugins.files.data.Utils.File.isValidFilename(folderName)) {
			Zarafa.plugins.files.data.Actions.msgWarning(_('Incorrect foldername'));
			return;
		}

		var record = this.record;
		record.beginEdit();
		record.set("display_name", folderName);
		record.set("filename", folderName);
		record.set("text", folderName);
		record.set("icon_index", Zarafa.core.mapi.IconIndex["folder_note"]);
		record.set("folder_id", this.parentFolder.get('folder_id') + folderName + "/" );
		record.set("path", this.parentFolder.get('folder_id'));
		record.set("parent_entryid", this.parentFolder.get('entryid'));
		record.set("store_entryid", this.parentFolder.get('store_entryid'));
		record.endEdit();

		this.dialog.saveRecord();
	},

	/**
	 * Event handler which triggered when selection get change in hiererachy tree.
	 *
	 * @param {model} model The model for the treepanel
	 * @param {TreeNode} node The selected tree node
	 * @private
	 */
	onHierarchyNodeSelect : function(model, node)
	{
		if (node) {
			this.parentFolder = node.getFolder();
		}
	},

	/**
	 * Fired when the {@link Zarafa.hierarchy.ui.Tree Tree} fires the {@link Zarafa.hierarchy.ui.Tree#load load}
	 * event. This function will try to select the {@link Ext.tree.TreeNode TreeNode} in
	 * {@link Zarafa.hierarchy.ui.Tree Tree} initially. When the given node is not loaded yet, it will try again
	 * later when the event is fired again.
	 *
	 * @private
	 */
	onTreeNodeLoad : function()
	{
		// If the folder could be selected, then unregister the event handler.
		if (this.hierarchyTree.selectFolderInTree(this.parentFolder)) {
			this.mun(this.hierarchyTree, 'load', this.onTreeNodeLoad, this);
			// Force focus on input field
			this.newNameField.focus();
		}
	},

	/**
	 * Function is used to update values of fields when ever an updated
	 * {@link Zarafa.plugins.files.data.FilesFolderRecord record} is received.
	 * @param {Zarafa.plugins.files.data.FilesFolderRecord} record The record update the panel with.
	 * @param {Boolean} contentReset force the component to perform a full update of the data.
	 */
	update : function(record, contentReset)
	{
		this.record = record;
	},

	/**
	 * Event handler which is triggered when
	 * a key is pressed in the searchTextField
	 *
	 * @param {Ext.form.TextField} field
	 * @param {Ext.EventObject} e
	 * @private
	 */
	handleEnter: function(field, e)
	{
		if (e.getKey() === e.ENTER) {
			this.onOk();
		}
	}
});

Ext.reg('filesplugin.createfolderpanel', Zarafa.plugins.files.ui.dialogs.CreateFolderPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFileContentPanel
 * @extends Zarafa.core.ui.RecordContentPanel
 * @xtype filesplugin.createfilecontentpanel
 *
 * Create file content panel provide the {@link Zarafa.plugins.files.ui.Tree Tree} to create
 * the new file in that.
 */
Zarafa.plugins.files.ui.dialogs.CreateFileContentPanel = Ext.extend(Zarafa.core.ui.RecordContentPanel, {

	/**
	 * @constructor
	 * @param config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};
		const filetypeTitle = config.button.text || 'Document';

		Ext.applyIf(config, {
			xtype : 'zarafa.createfilecontentpanel',
			layout: 'fit',
			title : _('Create New ' + filetypeTitle),
			showLoadMask: false,
			showInfoMask: false,
			width: 300,
			height: 110,
			items: [{
				xtype: 'filesplugin.createfilepanel',
				accountFilter : config.accountFilter,
				parentFolder : config.parentFolder,
				model : config.model,
				filetype: config.filetype
			}]
		});

		Zarafa.plugins.files.ui.dialogs.CreateFileContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.createfilecontentpanel', Zarafa.plugins.files.ui.dialogs.CreateFileContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.CreateFolderPanel
 * @extends Ext.Panel
 * @xtype filesplugin.createfolderpanel
 *
 * Panel for users to create folder record in different supported backends.
 */
Zarafa.plugins.files.ui.dialogs.CreateFilePanel = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.plugins.files.data.FilesFolderRecord} parentFolder (optional) The parent folder
	 * underneath the new folder will be created.
	 */
	parentFolder : undefined,

	fileType: '',
	model: null,

	/**
	 * @constructor
	 * @param {Object} config Configuration structure
	 */
	constructor : function(config)
	{
		config = config || {};

		config.plugins = Ext.value(config.plugins, []);
		config.plugins.push('zarafa.recordcomponentupdaterplugin');

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.createfilepanel',
			layout: {
				type: 'fit',
			},
			border: false,
			header: false,
			items: this.createPanel(config),
			buttonAlign: 'right',
			buttons: [{
				text: _('Ok'),
				ref: '../okButton',
				cls: 'zarafa-action',
				handler : this.onOk,
				scope: this
			},{
				text: _('Cancel'),
				ref: '../cancelButton',
				handler : this.onCancel,
				scope: this
			}]
		});

		this.filetype = config.filetype || '.xlsx';
		this.model = config.model;

		Zarafa.plugins.files.ui.dialogs.CreateFilePanel.superclass.constructor.call(this, config);
	},

	/**
	 * Creates body for {@link Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel CreateFolderContentPanel}
	 * @param {Object} config The config options contains the {@link Zarafa.plugins.files.FilesContextModel FilesContextModel} and
	 * {@link Zarafa.plugins.files.ui.Tree#accountFilter}. which used by {@link Zarafa.plugins.files.ui.Tree Tree}.
	 *
	 * @return {Array} Array which contains configuration object to create the {@link Zarafa.plugins.files.ui.Tree TreePanel}.
	 * @private
	 */
	createPanel : function(config)
	{
		return [{
			xtype : 'panel',
			layout : 'form',
			border : false,
			defaults : {
				anchor :'100%'
			},
			labelAlign : 'top',
			items : [{
				xtype : 'textfield',
				fieldLabel : _('Name'),
				cls: 'form-field-name',
				ref : '../newNameField',
				enableKeyEvents: true,
				listeners: {
					keyup: {
						fn: this.handleEnter,
						buffer: 200
					},
					scope: this
				},
			}]
		}];
	},

	/**
	 * Function called by Extjs when the panel has been {@link #render rendered}.
	 * At this time all events can be registered.
	 * @private
	 */
	initEvents : function ()
	{
		Zarafa.plugins.files.ui.dialogs.CreateFolderPanel.superclass.initEvents.apply(this, arguments);
	},

	/**
	 * Event handler which is triggered when the user presses the cancel
	 * {@link Ext.Button button}. This will close this dialog.
	 * @private
	 */
	onCancel : function()
	{
		this.dialog.close();
	},

	/**
	 * Event handler which is triggered when the user presses the ok
	 * {@link Ext.Button button}. function is responsible to create folder
	 * under the respective folder as well as check for duplicate folder.
	 *
	 * @param {Ext.Button} button which triggers this event.
	 * @param {Ext.EventObject} event The event object
	 */
	onOk : function (button, event)
	{
		var fileName = this.newNameField.getValue();
		var dir = this.parentFolder.get('folder_id');
		var accId = dir.substr(0, dir.indexOf('/') + 1);
		dir = dir.substr(dir.indexOf('/'));

		if (Ext.isEmpty(fileName.trim())) {
			Ext.MessageBox.show({
				title: _('grommunio Web'),
				msg: _('You must specify a name.'),
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.INFO,
				scope : this
			});
			return;
		}

		if (!Zarafa.plugins.files.data.Utils.File.isValidFilename(fileName)) {
			Zarafa.plugins.files.data.Actions.msgWarning(_('Incorrect filename'));
			return;
		}

		const url = Zarafa.plugins.files.data.Utils.File.getNewFileUrl(accId);

		if(!url) {
			Ext.MessageBox.show({
				title: _('grommunio Web'),
				msg: _('Error reaching office backend'),
				buttons: Ext.MessageBox.OK,
				icon: Ext.MessageBox.INFO,
				scope : this
			});
			return;
		}

		fetch(url, {
			method: "POST",
			headers: {'Content-Type': 'application/json'}, 
			body: JSON.stringify({ "name": fileName + this.filetype, "dir": dir }),
		}).then(async res => {
			const result = await res.json();
			const { id, name, size, type, path } = result;
			var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FILES, {
				"object_type": Zarafa.plugins.files.data.FileTypes.FILE,
				'folder_id': dir,
				'fileid': id,
				'display_name': name,
				'message_size': size,
				'type': type,
				'path': dir + name,
			});
			if(record) {
				Zarafa.plugins.files.data.Actions.openTab(record);
				Zarafa.plugins.files.data.Actions.updateCache(accId.slice(0, -1) + dir);
				if (this.model) {
					this.model.reload();
				}
				this.dialog.close();
			}
		});
	},

	/**
	 * Event handler which is triggered when
	 * a key is pressed in the searchTextField
	 *
	 * @param {Ext.form.TextField} field
	 * @param {Ext.EventObject} e
	 * @private
	 */
	 handleEnter: function(field, e)
	 {
		 if (e.getKey() === e.ENTER) {
			 this.onOk();
		 }
	 }

});

Ext.reg('filesplugin.createfilepanel', Zarafa.plugins.files.ui.dialogs.CreateFilePanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.filesrecordcontentpanel
 *
 * This content panel contains the record information panel.
 */
Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param {object} config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {

			xtype: 'filesplugin.filesrecordcontentpanel',

			layout: 'fit',
			title : _('File information'),
			items : [this.createPanel(config)]
		});

		Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel.superclass.constructor.call(this, config);
	},

	/**
	 * This creates the {@link Zarafa.plugins.files.ui.FilesRecordDetailsPanel}
	 * @param config
	 * @returns {object}
	 */
	createPanel: function (config) {

		return {
			xtype : 'filesplugin.filesrecorddetailspanel',
			record: config.record
		};
	}
});

Ext.reg('filesplugin.filesrecordcontentpanel', Zarafa.plugins.files.ui.dialogs.FilesRecordContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.filesuploadcontentpanel
 *
 * This class displays the main upload dialog if a users click on the + sign in the tabbar. It will
 * show a simple folder selector tree and a file upload field.
 */
Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @var string The selected destination path
	 */
	targetFolder: undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			xtype: 'filesplugin.filesuploadcontentpanel',
			layout: 'fit',
			title : _('Upload file'),
			items : [{
				xtype     : 'form',
				ref       : 'mainuploadform',
				layout    : {
					type : 'vbox',
					align: 'stretch',
					pack : 'start'
				},
				fileUpload: true,
				padding   : 5,
				items     : [
					this.createFolderSelector(),
					this.createUploadField()
				],
				buttons   : this.createActionButtons()
			}]
		});

		Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel.superclass.constructor.call(this, config);
	},

	/**
	 * Generates and returns the upload field UI.
	 *
	 * @returns {Object}
	 * @private
	 */
	createUploadField: function () {
		return {
			xtype : 'panel',
			title : _('Select a file') + ' (' + _('Maximum upload size') + ': ' + Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) + '):',
			layout: 'fit',
			padding: 10,
			items : [{
				xtype     : 'filesplugin.multiplefileuploadfield',
				buttonText: _('Browse') + '...',
				name      : 'attachments[]',
				disabled  : true,
				listeners : {
					'fileselected': this.onUploadFieldChanged,
					'scope'       : this
				},
				ref       : '../../mainuploadfield'
			}]
		};
	},

	/**
	 * Generates and returns the folder selector treepanel UI.
	 *
	 * @returns {Object}
	 * @private
	 */
	createFolderSelector: function () {
		var filesContext = container.getContextByName("filescontext");
		var model = filesContext.getModel();
		return {
			xtype : 'filesplugin.tree',
			title : _('Select upload folder') + ':',
			FilesFilter: Zarafa.plugins.files.data.FileTypes.FOLDER,
			flex:1,
			store : model.getHierarchyStore(),
			listeners : {
				click : this.onFolderSelected,
				scope : this
			}
		};
	},

	/**
	 * Generates and returns the buttons for the dialog.
	 *
	 * @returns {*[]}
	 */
	createActionButtons: function () {
		return [{
			xtype   : 'button',
			ref     : '../../mainuploadbutton',
			disabled: true,
			text    : '&nbsp;&nbsp;' + _('Upload'),
			iconCls : 'icon_files',
			handler : this.doUpload,
			scope   : this
		}, {
			xtype  : 'button',
			text   : _('Cancel'),
			handler: this.onClose,
			scope  : this
		}];
	},

	/**
	 * Event handler triggered when {@link Zarafa.plugins.files.ui.FilesFolderNode FilesFolderNode}
	 * has been selected in {@link Zarafa.plugins.files.ui.Tree TreePanel}. The selected folder id
	 * will be stored to {@link #targetFolder}.
	 *
	 * @param {Zarafa.plugins.files.ui.FilesFolderNode} node The node which selected in {@link Zarafa.plugins.files.ui.Tree TreePanel}
	 */
	onFolderSelected: function (node) {
		var folder = node.getFolder();
		this.targetFolder = folder.get('folder_id');
		this.dialog.mainuploadfield.enable();
	},

	/**
	 * Eventhandler for the fileselected event of the filefield.
	 * This function will check the filesize if the browser supports the file API.
	 *
	 * @param field
	 * @param newValue
	 * @param oldValue
	 */
	onUploadFieldChanged: function (field, newValue, oldValue) {
		if (!Ext.isEmpty(newValue)) {
			var form = field.ownerCt.ownerCt.getForm();

			var files;
			files = this.mainuploadfield.fileInput.dom.files;

			var filesTooLarge = false;
			Ext.each(files, function (file) {
				if (file.size > Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize()) {

					this.mainuploadfield.reset();

					Zarafa.common.dialogs.MessageBox.show({
						title  : _('Error'),
						msg    : String.format(_('File "{0}" is too large! Maximum allowed filesize: {1}.'), file.name, Zarafa.plugins.files.data.Utils.Format.fileSize(Zarafa.plugins.files.data.Utils.Core.getMaxUploadFilesize())),
						icon   : Zarafa.common.dialogs.MessageBox.ERROR,
						buttons: Zarafa.common.dialogs.MessageBox.OK
					});

					this.mainuploadbutton.setDisabled(true);
					filesTooLarge = true;
					return false;
				} else {
					if (!filesTooLarge) {
						this.mainuploadbutton.setDisabled(false);
					}
				}
			}, this);

		} else {
			this.mainuploadbutton.setDisabled(true);
		}
	},

	/**
	 * Event handler that will start the upload process.
	 */
	doUpload: function () {
		var files = this.mainuploadfield.fileInput.dom.files;
		Zarafa.plugins.files.data.Actions.uploadAsyncItems(files, this.record.getStore(), this.targetFolder);
		this.onClose();
	},

	/**
	 * This function will close the dialog.
	 */
	onClose: function () {
		this.close();
	}
});

Ext.reg('filesplugin.filesuploadcontentpanel', Zarafa.plugins.files.ui.dialogs.FilesUploadContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.savetofilescontentpanel
 *
 * This content panel contains the upload panel for storing files to the backend.
 */
Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		Ext.applyIf(config, {
			layout     : 'fit',
			title      : _('Add item to Files'),
			closeOnSave: true,
			width      : 400,
			height     : 300,
			items: [{
				xtype   : 'filesplugin.savetofilespanel',
				response: config.response,
				model : config.model
			}]
		});

		Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.savetofilescontentpanel', Zarafa.plugins.files.ui.dialogs.SaveToFilesContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SaveToFilesPanel
 * @extends Ext.Panel
 * @xtype filesplugin.savetofilespanel
 *
 * This dialog panel will provide the file chooser tree for the destination folder selection.
 */
Zarafa.plugins.files.ui.dialogs.SaveToFilesPanel = Ext.extend(Ext.Panel, {

	/**
	 * @var {Object} response holds the response data from the attachment preparation event
	 */
	response: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		this.response = config.response;

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.savetofilespanel',
			layout: {
				type: 'hbox',
				align: 'stretch'
			},
			border: false,
			header: false,
			items : [
				this.createTreePanel(config),
				this.createGridPanel()
			],
			buttonAlign: 'right',
			buttons: this.createActionButtons()
		});

		Zarafa.plugins.files.ui.dialogs.SaveToFilesPanel.superclass.constructor.call(this, config);

		if (Ext.isDefined(this.model)) {
			this.mon(this.hierarchyTree, 'click', this.onTreeNodeClick, this);
		}
	},

	/**
	 * Create the {@link Zarafa.plugins.files.ui.Tree Tree}.
	 * @param {Object} config The configuration object.
	 * @return {Object} return an object which used to create the {@link Zarafa.plugins.files.ui.Tree Tree}
	 */
	createTreePanel : function(config)
	{
		return {
			xtype : 'filesplugin.tree',
			model : config.model,
			border : true,
			flex : 1,
			ref : 'hierarchyTree'
		};
	},

	/**
	 * Create the {@link Ext.grid.GridPanel GridPanel} which contains the files item
	 * of selected folder.
	 * @return {Object} return an object which used to create the {@link Ext.grid.GridPanel GridPanel}
	 */
	createGridPanel : function()
	{
		return {
			xtype: 'grid',
			style: {
				paddingLeft: '9px'
			},
			ref : 'filesGrid',
			flex : 2,
			columns: [{
				dataIndex: 'type',
				header   : '<p class="icon_index">&nbsp;</p>',
				headerCls: 'zarafa-icon-column icon',
				renderer : Zarafa.plugins.files.data.Utils.Renderer.typeRenderer,
				width    : 24,
				fixed    : true,
				tooltip  : _('Sort by: Type')
			},{
				header   : _('Filename'),
				dataIndex: 'filename',
				width    : 160,
				tooltip  : _('Sort by: Filename')
			},{
				header   : _('Size'),
				dataIndex: 'message_size',
				width    : 80,
				renderer : Zarafa.plugins.files.data.Utils.Format.fileSizeList,
				tooltip  : _('Sort by: Size')
			}],
			loadMask : {
				msg : _('Loading files') + '...'
			},
			store:{
				xtype: 'filesplugin.filesrecordstore',
			},
			viewConfig : {
				deferEmptyText : true,
				emptyText : '<div class="emptytext">' + _('There are no items to show in this list') + '</div>',
				forceFit : true
			}
		}
	},

	/**
	 * Generate the toolbar action buttons.
	 *
	 * @returns {Array} return an array of buttons.
	 */
	createActionButtons: function () {
		return [{
			xtype: 'button',
			text: _('New folder'),
			cls: 'zarafa-normal',
			handler: this.onClickNewFolder,
			scope: this
		}, {
			xtype: 'button',
			text: _('Save'),
			cls: 'zarafa-action',
			iconCls: 'icon_files_category_white',
			handler: this.onClickSave,
			scope: this
		}];
	},

	/**
	 * Called when a treeNode is click in tree. The corresponding folder is added to,
	 * or removed from the active folder list depending on the state of the check box.
	 * @param {Ext.tree.TreeNode} treeNode tree node.
	 * @private
	 */
	onTreeNodeClick : function(treeNode)
	{
		var folder = treeNode.getFolder();
		this.filesGrid.store.load({
			params: {
				only_files : true
			},
			folder : folder });
	},

	/**
	 * This will check if the file already exists in store.
	 */
	onClickSave: function ()
	{
		var folder = this.getSelectedFolder();

		if (!Ext.isDefined(folder)) {
			return false;
		}

		var store = this.filesGrid.getStore();

		var filesExists = [];

		for (var i = 0, len = this.response.count; i < len; i++) {
			var fileName = this.response.items[i].filename;
			var fileExists = store.find('display_name', fileName);

			if (fileExists !== -1) {
				filesExists.push(fileName);
			}
		}

		if (!Ext.isEmpty(filesExists)) {
			// TODO: Show which file name is duplicated.
			Ext.MessageBox.confirm(
				_('Confirm overwrite'),
				_('File already exists. Do you want to overwrite it?'),
				function(button) {
					if (button === 'no') {
						return;
					}
					this.doUpload(folder, button);
				},
				this
			);
		} else {
			this.doUpload(folder);
		}
	},

	/**
	 * This function will prompt {@link Zarafa.plugins.files.ui.dialogs.CreateFolderContentPanel CreateFolderContentPanel}
	 * the user for a new folder name.
	 */
	onClickNewFolder: function () {
		var folder = this.getSelectedFolder();
		if (!Ext.isDefined(folder)) {
			return;
		}
		Zarafa.plugins.files.data.Actions.createFolder(this.model, undefined, folder);
	},

	/**
	 * Function used to get the selected folder.
	 *
	 * @return {Zarafa.plugins.files.data.FilesFolderRecord|undefined} return selected folder else undefined.
	 */
	getSelectedFolder : function()
	{
		var selectionModel = this.hierarchyTree.getSelectionModel();
		var selectedNode = selectionModel.getSelectedNode();
		if (Ext.isEmpty(selectedNode)) {
			Zarafa.plugins.files.data.Actions.msgWarning(_('You have to choose a folder!'));
			return undefined;
		}
		return selectedNode.getFolder();
	},

	/**
	 * This function uploads the file to the server.
	 *
	 * @param button
	 */
	doUpload: function (folder, button)
	{
		if (!Ext.isDefined(button) || button === "yes") {
			try {
				this.disable();
				container.getRequest().singleRequest(
					'filesbrowsermodule',
					'uploadtobackend',
					{
						items  : this.response.items,
						count  : this.response.count,
						type   : this.response.type,
						destdir: folder.get('folder_id')
					},
					new Zarafa.core.data.AbstractResponseHandler({
						doUploadtobackend: this.uploadDone.createDelegate(this)
					})
				);
			} catch (e) {
				Zarafa.plugins.files.data.Actions.msgWarning(e.message);
			}
		}
	},

	/**
	 * Called after the upload has completed.
	 * It will notify the user and close the upload dialog.
	 *
	 * @param response
	 */
	uploadDone: function (response)
	{
		if (response.status === true) {
			container.getNotifier().notify('info.files', _('Uploaded'), _('Attachment successfully stored in Files'));
		} else {
			container.getNotifier().notify('error', _('Upload Failed'), _('Attachment could not be stored in Files! Error: ' + response.status));
		}

		this.dialog.close();
	}
});

Ext.reg('filesplugin.savetofilespanel', Zarafa.plugins.files.ui.dialogs.SaveToFilesPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.ShareContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.sharecontentpanel
 *
 * This content panel contains the sharing panel.
 */
Zarafa.plugins.files.ui.dialogs.ShareContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @var {Array} records
	 */
	records: null,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout     : 'fit',
			title      : _('Share Files'),
			width      : 800,
			height     : 500,
			items: [
				container.populateInsertionPoint('plugin.files.sharedialog', this, config.context)
			]
		});

		Zarafa.plugins.files.ui.dialogs.ShareContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.sharecontentpanel', Zarafa.plugins.files.ui.dialogs.ShareContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.SharePanel
 * @extends Ext.Panel
 * @xtype filesplugin.sharepanel
 *
 * This panel must be extended! It will then decide if it should be displayed or not.
 * Make sure that the plugin has a name set! The name should be in the following format:
 * filesbackend<Backendname>
 *
 * e.g.: filesbackendSMB, filesbackendOwncloud....
 */
Zarafa.plugins.files.ui.dialogs.SharePanel = Ext.extend(Ext.Panel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};

		var records = config.ownerCt.records;
		var hidden = true;


		// check if this panel should be enabled
		if (records.length > 0) {
			var account = records[0].getAccount();
			var backend = account.get("backend");
			var backendPlugin = config.plugin.info.name;

			// "filesbackend" has 12 chars
			if (backend === backendPlugin.substring(12)) {
				hidden = false;
			}
		}

		if (hidden) {
			Ext.applyIf(config, {
				disabled: true,
				hidden  : true
			});
		}


		Zarafa.plugins.files.ui.dialogs.SharePanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.sharepanel', Zarafa.plugins.files.ui.dialogs.SharePanel);Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * @xtype filesplugin.uploadstatuscontentpanel
 *
 * The content panel for the main upload status panel.
 */
Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			layout : 'fit',
			title : _('Uploading files to ') + Zarafa.plugins.files.data.Utils.File.stripAccountId(config.destination) + ' &hellip;',
			closeOnSave : true,
			width : 480,
			height : 445,
			items: [{
				xtype : 'filesplugin.uploadstatuspanel',
				files : config.files,
				destination : config.destination,
				keepBoth : config.keepBoth,
				callbackAllDone : config.callbackAllDone || Ext.emptyFn,
				callbackUploadFailed : config.callbackUploadFailed || Ext.emptyFn,
				callbackUploadAborted : config.callbackUploadAborted || Ext.emptyFn,
				store : config.store
			}]
		});

		Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel.superclass.constructor.call(this, config);
	}
});

Ext.reg('filesplugin.uploadstatuscontentpanel', Zarafa.plugins.files.ui.dialogs.UploadStatusContentPanel);
Ext.namespace('Zarafa.plugins.files.ui.dialogs');

/**
 * @class Zarafa.plugins.files.ui.dialogs.UploadStatusPanel
 * @extends Ext.form.FormPanel
 * @xtype filesplugin.uploadstatuspanel
 *
 * This panel will upload files via ajax and display some nice progressbars.
 */
Zarafa.plugins.files.ui.dialogs.UploadStatusPanel = Ext.extend(Ext.form.FormPanel, {

	/**
	 * @var FileList or string[]
	 */
	files: null,

	/**
	 * @var string
	 */
	destination: null,

	/**
	 * @var XMLHttpRequest[]
	 */
	xhr: [],

	/**
	 * @var object Timer object for calculating the upload speed
	 */
	statsTimer: null,

	/**
	 * @var int Lock for synchronisation between timer runs
	 */
	timerCounter: null,

	/**
	 * @var int Timerinterval in milliseconds
	 */
	timerIntervall: 200,

	/**
	 * @var function Callback gets called if all files were uploaded
	 */
	callbackAllDone: Ext.emptyFn,

	/**
	 * @var function Callback gets called if one upload fails
	 */
	callbackUploadFailed: Ext.emptyFn,

	/**
	 * @var function Callback gets called if one upload was aborted
	 */
	callbackUploadAborted: Ext.emptyFn,

	/**
	 * @cfg {@link Zarafa.plugins.files.data.FilesRecordStore store} which contains
	 * {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 */
	store : undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config) {
		config = config || {};
		Ext.applyIf(config, {
			autoScroll : true,
			defaults : {
				anchor: '100%'
			},
			items    : this.initUploadUI(config),
			listeners: {
				afterrender: this.startUpload,
				scope : this
			}
		});

		Zarafa.plugins.files.ui.dialogs.UploadStatusPanel.superclass.constructor.call(this, config);
	},

	/**
	 * This function generates a UI element that displays the file upload stats (ETA, speed, size...)
	 * @param file
	 * @param ref
	 * @returns {object}
	 */
	getBasicElement: function (file, ref) {
		var filename = file.name;
		var filesize = file.size;

		return {
			xtype        : 'panel',
			padding      : 10,
			custom_fileid: ref,
			ref          : 'fileuploadfield_' + ref,
			items        : [{
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .8,
					items      : {
						xtype     : 'displayfield',
						fieldClass: 'fp_upload_header',
						value     : filename
					}
				}, {
					columnWidth: .2,
					style      : 'text-align: right;',
					items      : {
						xtype     : 'displayfield',
						fieldClass: 'fp_upload_header',
						value     : Zarafa.plugins.files.data.Utils.Format.fileSizeList(filesize)
					}
				}]
			}, {
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .95,
					items      : {
						xtype: 'progress',
						text : _('Starting upload') + '&hellip;',
						ref  : '../../progress'
					}
				}, {
					columnWidth: .05,
					items      : {
						xtype        : 'button',
						ref          : '../../cancel',
						custom_fileid: ref,
						tooltip      : _('Cancel upload'),
						overflowText : _('Cancel upload'),
						iconCls      : 'icon_action_cancel',
						handler      : this.doCancelUpload.createDelegate(this)
					}
				}]
			}, {
				layout  : "column",
				border  : false,
				defaults: {
					border: false
				},
				anchor  : "0",
				items   : [{
					columnWidth: .33,
					items      : {
						xtype: 'displayfield',
						value: '- kB/s',
						ref  : '../../speed'
					}
				}, {
					columnWidth: .33,
					items      : [{
						xtype: 'displayfield',
						value: '0 MB',
						ref  : '../../uploaded'
					}]
				}, {
					columnWidth: .33,
					style      : 'text-align: right;',
					items      : {
						xtype: 'displayfield',
						value: _('- seconds left'),
						ref  : '../../eta'
					}
				}]
			}]
		};
	},

	/**
	 * Generates one UI element for each file
	 *
	 * @param config
	 * @returns {Array}
	 */
	initUploadUI: function (config) {
		var files = config.files;

		var items = [];

		Ext.each(files, function (file, index) {
			items.push(this.getBasicElement(file, index));
		}, this);

		return items;
	},

	/**
	 * This function generates the XMLHttpRequest's and starts the upload.
	 * The upload timer gets also started in this function.
	 */
	startUpload: function ()
	{
		Ext.each(this.files, function (file, index) {
			this.xhr[index] = new XMLHttpRequest();
			this.xhr[index].open("post", "index.php?load=custom&name=upload_file&keep_both="+this.keepBoth, true);

			// progress listener
			this.xhr[index].upload.addEventListener("progress", this.onUpdateProgress.createDelegate(this, [index], true), false);

			// finish listener
			this.xhr[index].addEventListener("load", this.onFinishProgress.createDelegate(this, [index], true), false);

			// error listener
			this.xhr[index].addEventListener("error", this.onUploadFailed.createDelegate(this, [index], true), false);

			// abort listener
			this.xhr[index].addEventListener("abort", this.onUploadAborted.createDelegate(this, [index], true), false);

			// Set headers - important for the php backend!
			this.xhr[index].setRequestHeader("X-FILE-NAME", encodeURIComponent(file.name));
			this.xhr[index].setRequestHeader("X-FILE-SIZE", file.size);
			this.xhr[index].setRequestHeader("X-FILE-TYPE", file.type);
			this.xhr[index].setRequestHeader("X-FILE-DESTINATION", this.destination);

			// Send the file
			this.xhr[index].send(file);

			this.xhr[index].cust_loaded = 0; // store loaded and total size to xhr element
			this.xhr[index].cust_total = 0;
		}, this);

		this.statsTimer = window.setInterval(this.getStats.createDelegate(this), this.timerIntervall);
		this.timerCounter = this.xhr.length; // set count for locking
	},

	/**
	 * This function gets called by the upload timer. It calculates upload speed
	 * and the remaining time.
	 */
	getStats: function () {
		Ext.each(this.xhr, function (request, index) {
			var oldloaded = request.cust_loaded_old;
			var loaded = request.cust_loaded;
			var total = request.cust_total;
			request.cust_loaded_old = loaded;

			// calculate speed and eta
			var speed = (loaded - oldloaded) / (this.timerIntervall / 1000); // bytes per second
			var speed_unit = ' B/s';

			// calc ETA
			var eta = (total - loaded) / speed; // seconds
			var eta_unit = _(' seconds left');

			if (eta > 60) {
				eta = eta / 60; // minutes
				eta_unit = _(' minutes left');
			}

			// transform speed units
			if (speed > 1000) {
				speed = speed / 1000; // kBps
				speed_unit = ' kB/s';
			}
			if (speed > 1000) {
				speed = speed / 1000; // mBps
				speed_unit = ' mB/s';
			}


			var filesUploaderPanel = this["fileuploadfield_" + index];
			if (Ext.isDefined(filesUploaderPanel)) {
				if (Ext.isDefined(oldloaded) && loaded != 0 && total != 0) {
					if (loaded != oldloaded) {
						filesUploaderPanel.speed.setValue(speed.toFixed(2) + speed_unit);
						filesUploaderPanel.eta.setValue(parseInt(eta) + eta_unit);
					}
				} else {
					filesUploaderPanel.speed.setValue('- kB/s');
					filesUploaderPanel.eta.setValue(_('- seconds left'));
				}
			}
		}, this);
	},

	/**
	 * Callback for the 'progress' event of the XHR request.
	 * It will update the progressbar and set the uploaded filesize.
	 *
	 * @param event
	 * @param index
	 */
	onUpdateProgress: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];

		if (Ext.isDefined(filesUploaderPanel)) {
			if (event.lengthComputable) {
				this.xhr[index].cust_loaded = event.loaded; // store loaded and total size to xhr element
				this.xhr[index].cust_total = event.total;

				var finished = ((event.loaded / event.total) * 100).toFixed(2);
				filesUploaderPanel.progress.updateProgress((event.loaded / event.total), _('Uploading: ') + finished + '%', true);
				filesUploaderPanel.uploaded.setValue(Zarafa.plugins.files.data.Utils.Format.fileSizeList(event.loaded));
			} else {
				filesUploaderPanel.progress.updateProgress(0.5, _('Upload status unavailable... please wait.'), true);
			}
		}
	},

	/**
	 * Callback for the 'load' event of the XHR request.
	 * This callback gets called after the file was uploaded to the server.
	 * It will update the progressbar and reset the uploaded filesize.
	 *
	 * @param event
	 * @param index
	 */
	onFinishProgress: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		// If files upload panel is not already closed then do go farther operations like
		// disable the cancel button and update progress bar etc.
		if (filesUploaderPanel) {
			filesUploaderPanel.progress.updateProgress(1, _('Upload finished!'), true);

			// reset stats - to tell the timer to stop
			this.xhr[index].cust_loaded = 0; // store loaded and total size to xhr element
			this.xhr[index].cust_total = 0;

			filesUploaderPanel.cancel.disable();
			this.checkTimerAlive();
		}
	},

	/**
	 * Callback for the 'abort' event of the XHR request.
	 * This callback gets called after a upload was aborted by the user.
	 * It will update the progressbar.
	 *
	 * @param event
	 * @param index
	 */
	onUploadAborted: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		if (Ext.isDefined(filesUploaderPanel)) {
			var progressbar = filesUploaderPanel.progress;
			progressbar.updateProgress(1, _('Upload aborted!'), true);
			progressbar.addClass("fp_upload_canceled");
			filesUploaderPanel.cancel.disable();

			// call callback
			this.callbackUploadAborted(this.files[index], this.destination, event);
			this.checkTimerAlive();
		}
	},

	/**
	 * Callback for the 'error' event of the XHR request.
	 * This callback gets called after a upload failed.
	 * It will update the progressbar.
	 *
	 * @param event
	 * @param index
	 */
	onUploadFailed: function (event, index) {
		var filesUploaderPanel = this["fileuploadfield_" + index];
		if (Ext.isDefined(filesUploaderPanel)) {
			var progressbar = filesUploaderPanel.progress;
			progressbar.updateProgress(1, _('Upload failed!'), true);
			progressbar.addClass("fp_upload_canceled");
			filesUploaderPanel.cancel.disable();

			// call callback
			this.callbackUploadFailed(this.files[index], this.destination, event);
			this.checkTimerAlive();
		}
	},

	/**
	 * This function will decrease the timer counter lock.
	 * If the lock is zero the dialog will be closed.
	 */
	checkTimerAlive: function () {
		this.timerCounter--;
		if (this.timerCounter <= 0) {
			window.clearTimeout(this.statsTimer);
			this.onUploadsFinished();
		}
	},

	/**
	 * This function can abort one XHR request.
	 *
	 * @param button
	 * @param event
	 */
	doCancelUpload: function (button, event) {
		this.xhr[button.custom_fileid].abort();
	},

	/**
	 * Close the dialog.
	 */
	onUploadsFinished: function () {
		this.dialog.close();
		this.callbackAllDone(this.files, this.destination, this.store);
	}
});

Ext.reg('filesplugin.uploadstatuspanel', Zarafa.plugins.files.ui.dialogs.UploadStatusPanel);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

/**
 * @class Zarafa.plugins.files.ui.snippets.FilesNavigationBar
 * @extends Ext.Panel
 * @xtype filesplugin.navigationbar
 *
 * This panel will display a windows explorer like navigation bar.
 */
Zarafa.plugins.files.ui.snippets.FilesNavigationBar = Ext.extend(Ext.Panel, {

	/**
	 * @cfg {Zarafa.core.Context} context The context to which this toolbar belongs
	 */
	context: undefined,

	/**
	 * The {@link Zarafa.core.ContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.mail.MailContextModel
	 */
	model: undefined,

	/**
	 * Maximum path parts to show. If the folder path is deeper,
	 * a back arrow will be shown.
	 * @property
	 * @type {Number}
	 */
	maxPathBeforeTruncate: 5,

	/**
	 * @cfg {Number} maxStringBeforeTruncate Maximum stringlength of a folder before it will get truncated.
	 */
	maxStringBeforeTruncate: 20,

	/**
	 * @cfg {String} pathTruncateText String that will be displayed in the back button.
	 */
	pathTruncateText: "&hellip;",

	/**
	 * Overflow flag. If this flag is true, the overflow button will be shown.
	 * @property
	 * @type boolean
	 */
	hasOverflow: false,

	/**
	 * The current path.
	 *
	 * @property
	 * @type {String}
	 */
	currentPath: '#R#',

	/**
	 * filesStore which contains the {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 *
	 * @property
	 * @type {Zarafa.plugins.files.data.FilesRecordStore}
	 */
	filesStore : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		if (Ext.isDefined(config.model) && !Ext.isDefined(config.filesStore)) {
			config.filesStore = config.model.getStore();
			this.currentPath = config.filesStore.folderId;
		}

		Ext.applyIf(config, {
			xtype : 'filesplugin.navigationbar',
			maskDisabled: false,
			hideBorders : true,
			border : false,
			cls: 'navbar_container',
			height: 25,
			defaults: {
				height: 25
			},
			layout : 'column',
		});

		Zarafa.plugins.files.ui.snippets.FilesNavigationBar.superclass.constructor.call(this, config);
	},

	/**
	 * initializes the events.
	 * @private
	 */
	initEvents: function () {
		this.on('afterrender', this.onAfterRender, this);
		this.mon(this.model, 'folderchange', this.onFolderChange, this);
		this.mon(this.model.getHierarchyStore(), 'updateFolder', this.onUpdateFolder, this);
		this.mon(this.model.getHierarchyStore(), 'load', this.onHierarchyStoreLoad, this);
	},

	/**
	 * Event handler triggered when {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore}.
	 * is load. It will update the navigation bar as per the default selected folder.
	 */
	onHierarchyStoreLoad : function()
	{
		var folder = this.model.getDefaultFolder();
		this.updateNavigationBar(folder);
	},

	/**
	 * Event handler triggered when folder was updated in hierarchy.
	 * It will call {@link #updateNavigationBar} function to update the navigation bar
	 * as per the updated folder.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord} storeRecord
	 * @param {Zarafa.hierarchy.data.IPFRecord} folder The folder which is updated in the hierarchy store.
	 */
	onUpdateFolder : function(store, parentFolder, folder)
	{
		if (folder.get('entryid') !== this.model.getDefaultFolder().get('entryid')) {
			return;
		}
		this.updateNavigationBar(folder);
	},

	/**
	 * Event handler triggered after the render navigation bar.
	 * update the navigation bar as per the currently selected folder.
	 */
	onAfterRender : function()
	{
		this.generateNavigationButtons(this.model.getDefaultFolder());
	},

	/**
	 * Event handler triggered when 'folderchange'. The function will
	 * update the navigation bar as per the currently selected folder.
	 */
	onFolderChange: function (model)
	{
		var folder = model.getDefaultFolder();
		this.updateNavigationBar(folder);
	},

	/**
	 * Update the navigation bar as per the selected folder if
	 * not selected it earlier.
	 *
	 * @param {Zarafa.hierarchy.data.IPFRecord} folder The folder which is updated in the hierarchy store.
	 */
	updateNavigationBar : function (folder)
	{
		var currentPath = folder.get('folder_id');

		if (this.currentPath !== currentPath) {
			this.currentPath = currentPath;
			this.generateNavigationButtons(folder);
		}
	},

	/**
	 * Calculate the maximum number of folders we can display.
	 * @private
	 */
	recalculateMaxPath: function() {
		var maxItemWidth = this.maxStringBeforeTruncate * 8; // one char is 8 pixels long
		var totalWidth = this.getWidth();
		var children = this.items ? this.items.items : [];
		var removeStatic = this.hasOverflow ? 3 : 2; // -2 because home and account folder does not count

		Ext.each(children, function (child) {
			totalWidth -= child.getWidth();
		});

		if(totalWidth < maxItemWidth) {
			// we need more space - remove first children
			// so we need at least maxItemWidth - totalWidth;

			var spaceNeeded = maxItemWidth - totalWidth;
			var childrenToRemove = 0;

			Ext.each(children, function (child) {
				spaceNeeded -= child.getWidth();
				childrenToRemove++;

				if(spaceNeeded <= 0) {
					return false;
				}
			});

			this.maxPathBeforeTruncate = children.length - childrenToRemove;
		} else {
			this.maxPathBeforeTruncate = Math.floor(children.length + (totalWidth / maxItemWidth));
		}
		this.maxPathBeforeTruncate = this.maxPathBeforeTruncate - removeStatic;
	},

	/**
	 * Create home button in navigation bar.
	 */
	createHomeButton : function()
	{
		// Added Home button in files navigation bar.
		this.add({
			xtype : 'button',
			cls: "files_navbar_button files_navbar_button_first",
			tooltip: _('Home'),
			path: "#R#", // root folder
			listeners: {
				click: this.doNavButtonClick,
				scope : this
			},
			iconCls: "files_navbar files_navbar_home"
		});
	},

	/**
	 * Create backend root button in navigation bar.
	 * @param {String} path The path of selected node/button in files navigation bar.
	 * @param {String} accountID The accountID of selected configured account.
	 * @param {Boolean} isLastButton true if selected node/button is last button in
	 * navigation bar
	 */
	createBackEndRootButton : function (path, accountID, isLastButton)
	{
		if (Ext.isEmpty(accountID)) {
			return;
		}

		var account = this.accountsStore.getById(accountID);
		if (Ext.isEmpty(account)) {
			return;
		}

		var lastCls = isLastButton ? " files_navbar_button_last" : "";
		var accountName = Zarafa.plugins.files.data.Utils.Format.truncate(account.get("name"), this.maxStringBeforeTruncate);
		var accButton = {
			xtype : 'button',
			cls : "files_navbar_button" + lastCls,
			path : "#R#" + accountID + "/",
			listeners : {
				click : this.doNavButtonClick,
				scope : this
			},
			text : accountName
		};

		// If account name is not set by user then show account backend icon
		if (Ext.isEmpty(accountName)) {
			accButton.iconCls = "files_navbar icon_16_" + account.get("backend");
		}
		this.add(accButton);
	},

	/**
	 * Create buttons for the given folder path.
	 *
	 * @param {Zarafa.hierarchy.data.IPFRecord} folder The folder which is updated in the hierarchy store.
	 */
	generateNavigationButtons: function (folder) {
		var currentPath = Ext.isDefined(folder) ? folder.get('folder_id') : "#R#";
		var accountID = Zarafa.plugins.files.data.Utils.File.getAccountId(currentPath);
		var path = Zarafa.plugins.files.data.Utils.File.stripAccountId(currentPath);

		// recalculate the width
		this.recalculateMaxPath();

		// first remove old buttons
		this.removeAll(true);

		// Create home button.
		this.createHomeButton();
		var isLastButton = path.indexOf("/") === -1 || path === "/";
		// Create Backend root button.
		this.createBackEndRootButton(path, accountID, isLastButton);

		if (!isLastButton) {
			var currPath = "/";
			var pathParts = path.replace(/^\/|\/$/g, '').split("/"); // trim leading and trailing slash and split afterwards

			if(pathParts.length > this.maxPathBeforeTruncate) {
				this.hasOverflow = true;

				var overflowParts = pathParts.splice(0, (pathParts.length - this.maxPathBeforeTruncate));

				var menu = [];
				Ext.each(overflowParts, function (pathPart) {
					currPath += pathPart + "/";
					menu.push({
						text: Zarafa.plugins.files.data.Utils.Format.truncate(pathPart, this.maxStringBeforeTruncate),
						handler: this.doNavButtonClick,
						iconCls: 'icon_folder_note',
						path: "#R#" + accountID + currPath,
						scope : this
					});
				}, this);

				var overflowButton = new Ext.Button({
					cls: "files_navbar_button",
					menu: menu,
					text : this.pathTruncateText
				});
				this.add(overflowButton);
			} else {
				this.hasOverflow = false;
			}

			Ext.each(pathParts, function (pathPart, index) {
				currPath += pathPart + "/";
				var lastCls = index == (pathParts.length-1) ? " files_navbar_button_last" : "";
				var navBtn = new Ext.Button({
					text: Zarafa.plugins.files.data.Utils.Format.truncate(pathPart, this.maxStringBeforeTruncate),
					cls: "files_navbar_button" + lastCls,
					path: "#R#" + accountID + currPath,
					listeners: {
						click: this.doNavButtonClick,
						scope : this
					}
				});
				this.add(navBtn);
			}, this);
		}

		this.doLayout();
	},

	/**
	 * Event handler that handles a navigation button click.
	 *
	 * @param button
	 */
	doNavButtonClick: function(button)
	{
		var model = this.model;
		var hierarchyStore = model.getHierarchyStore();
		var folder = hierarchyStore.getFolderByFolderId(button.path);

		if (!Ext.isDefined(folder)) {
			return;
		}

		if (folder.isHomeFolder()) {
			model.setPreviewRecord(undefined);
		}

		container.selectFolder(folder);
	}
});

Ext.reg('filesplugin.navigationbar', Zarafa.plugins.files.ui.snippets.FilesNavigationBar);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.FilesQuotaBar = Ext.extend(Ext.Panel, {
	/**
	 * The {@link Zarafa.plugins.files.FilesContextModel} which is obtained from the {@link #context}.
	 * @property
	 * @type Zarafa.plugins.files.FilesContextModel
	 */
	model: undefined,

	/**
	 * @cfg Boolean
	 */
	loadOnlyOnce: true,

	/**
	 * @cfg String
	 */
	defaultDirectory: "/",

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor: function (config) {
		config = config || {};

		if (!Ext.isDefined(config.store) && Ext.isDefined(config.model)) {
			config.store = config.model.getStore();
		}

		Ext.applyIf(config, {
			xtype : 'filesplugin.quotabar',
			cls : 'files_quota_bar_snippet',
			border : false,
			items : [{
				xtype : 'panel',
				ref : 'quotaPanel',
				layout : 'table',
				layoutConfig: {columns: 2},
				cls : 'files_quota_bar_container',
				border : false,
				items : [{
					xtype: 'label',
					ref : '../usageInfo',
					autoWidth: true
				}, {
					xtype: 'progress',
					width: '150',
					ref : '../progressBar',
					height: '8',
					cls : 'files_quota_bar',
					style: 'margin: 0 0 0 20px'
				}]
			}, {
				xtype: 'label',
				border : false,
				hidden : true,
				ref  : 'loadingIcon',
				html : '<div class="img"></div>'
			}]
		});

		Zarafa.plugins.files.ui.snippets.FilesQuotaBar.superclass.constructor.call(this, config);
	},

	/**
	 * initializes the events.
	 * @private
	 */
	initEvents: function ()
	{
		this.mon(this.store, {
			load : this.onStoreLoad,
			scope : this
		});
	},

	/**
	 * Event handler which will be called when the {@link #model} fires the
	 * {@link Zarafa.core.ContextModel#folderchange} event. This will determine
	 * if the selected folders support 'search folders' and update the UI accordingly.
	 * @param {Zarafa.core.ContextModel} model this context model.
	 * @param {Array} folders selected folders as an array of {Zarafa.hierarchy.data.MAPIFolderRecord Folder} objects.
	 * @private
	 */
	onStoreLoad: function (store, records, options)
	{
		var accID = Zarafa.plugins.files.data.Utils.File.getAccountId(store.folderId);

		// look up the account
		var account = this.accountsStore.getById(accID);

		if (Ext.isDefined(account) && account.supportsFeature(Zarafa.plugins.files.data.AccountRecordFeature.QUOTA)) {

			// load the information only once or if a new account was loaded.
			if (this.loaded != accID || !this.loadOnlyOnce) {
				this.quotaPanel.hide();
				this.loadingIcon.show();
				this.loadQuotaInformation(accID, this.defaultDirectory);
			}

			if (!this.isVisible()) {
				this.show();
			}
			// set the loaded flag to true
			this.loaded = accID;
		} else {
			this.hide();
		}
	},

	/**
	 * Request quota values from the server.
	 */
	loadQuotaInformation: function (accountID, directory) {
		var responseHandler = new Zarafa.core.data.AbstractResponseHandler({
			doGetquota: this.gotQuotaValues.createDelegate(this)
		});

		container.getRequest().singleRequest(
			'filesaccountmodule',
			'getquota',
			{
				accountId: accountID,
				folder   : directory
			},
			responseHandler
		);
	},

	/**
	 * Function is called after we received the response object from the server.
	 * It will update the textfield values.
	 *
	 * @param response
	 */
	gotQuotaValues: function (response) {
		if (!this.quotaPanel) {
			return;
		}

		var used = parseInt(response["quota"][0].amount);
		var free = parseInt(response["quota"][1].amount);
		var total = used + free;

		const quotaText = free < 0 ? '{0} in use' : '{0} of {1} in use';

		// show/hide components
		this.loadingIcon.hide();
		this.quotaPanel.show();

		// Update text values
		this.usageInfo.setText(
			free < 0 ? String.format(quotaText, Ext.util.Format.fileSize(used))
			: String.format(quotaText, Ext.util.Format.fileSize(used), Ext.util.Format.fileSize(total)));

		// Update progressbar
		this.progressBar.updateProgress(used / total);
	}
});

Ext.reg('filesplugin.quotabar', Zarafa.plugins.files.ui.snippets.FilesQuotaBar);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.PDFjsPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg{String} src
	 * URL to the PDF - Same Domain or Server with CORS Support
	 */
	src: '',

	/**
	 * @cfg {String} title The title of the pdf document.
	 */
	title: '',

	/**
	 * @cfg{Double} pageScale
	 * Initial scaling of the PDF. 1 = 100%
	 */
	pageScale: 1,

	/**
	 * @cfg{Boolean} disableWorker
	 * Disable workers to avoid yet another cross-origin issue(workers need the URL of
	 * the script to be loaded, and currently do not allow cross-origin scripts)
	 */
	disableWorker: true,

	/**
	 * @cfg{Boolean} disableTextLayer
	 * Enable to render selectable but hidden text layer on top of an PDF-Page.
	 * This feature is buggy by now and needs more investigation!
	 */
	disableTextLayer: true, // true by now, cause it´s buggy

	/**
	 * @cfg{String} loadingMessage
	 * The text displayed when loading the PDF.
	 */
	loadingMessage: 'Loading PDF, please wait...',

	/**
	 * @cfg{String} beforePageText
	 * The text displayed before the input item.
	 */
	beforePageText: 'Page',

	/**
	 * @cfg{String} afterPageText
	 * Customizable piece of the default paging text. Note that this string is formatted using
	 *{0} as a token that is replaced by the number of total pages. This token should be preserved when overriding this
	 * string if showing the total page count is desired.
	 */
	afterPageText: 'of {0}',

	/**
	 * @cfg{String} firstText
	 * The quicktip text displayed for the first page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	firstText: 'First Page',

	/**
	 * @cfg{String} prevText
	 * The quicktip text displayed for the previous page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	prevText: 'Previous Page',

	/**
	 * @cfg{String} nextText
	 * The quicktip text displayed for the next page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	nextText: 'Next Page',

	/**
	 * @cfg{String} lastText
	 * The quicktip text displayed for the last page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	lastText: 'Last Page',

	/**
	 * @cfg{String} fullscreenText
	 * The quicktip text displayed for the fullscreen button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	fullscreenText: 'Fullscreen',

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the input field used to display and change the current page number.
	 */
	inputItemWidth: 30,

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the combobox used to change display scale of the PDF.
	 */
	scaleWidth: 60,

	/**
	 * @constructor
	 */
	constructor: function (config) {
		config = config || {};

		config = Ext.applyIf(config, {
			xtype     : 'filesplugin.pdfjspanel',
			border    : false,
			baseCls   : 'x-panel pdf',
			bodyCssCls: 'pdf-body',
			pageScale : 1,
			header    : false, // hide title
			items     : [{
				xtype    : 'component',
				autoEl   : {
					cn: [
						{tag: 'canvas', 'class': 'pdf-page-container'},
						{tag: 'div', 'class': 'pdf-text-layer'}
					]
				},
				listeners: {
					afterrender: this.init.createDelegate(this)
				}
			}],
			bbar      : this.getPagingToolbar()
		});

		PDFJS.disableTextLayer = this.disableTextLayer;

		Zarafa.plugins.files.ui.snippets.PDFjsPanel.superclass.constructor.call(this, config);
	},

	getPagingToolbar: function () {
		var me = this;

		return {
			xtype : 'toolbar',
			height: 25,
			items : [{
				ref         : 'first',
				tooltip     : me.firstText,
				overflowText: me.firstText,
				iconCls     : 'x-tbar-page-first',
				disabled    : true,
				handler     : me.moveFirst,
				scope       : me
			}, {
				ref         : 'prev',
				tooltip     : me.prevText,
				overflowText: me.prevText,
				iconCls     : 'x-tbar-page-prev',
				disabled    : true,
				handler     : me.movePrevious,
				scope       : me
			}, '-', me.beforePageText, {
				xtype          : 'numberfield',
				ref            : 'inputItem',
				name           : 'inputItem',
				cls            : 'x-tbar-page-number',
				minValue       : 1,
				allowDecimals  : false,
				allowNegative  : false,
				enableKeyEvents: true,
				selectOnFocus  : true,
				submitValue    : false,
				width          : me.inputItemWidth,
				disabled       : true,
				margins        : '-1 2 3 2',
				listeners      : {
					scope  : me,
					keydown: me.onPagingKeyDown,
					blur   : me.onPagingBlur
				}
			}, {
				xtype  : 'tbtext',
				ref    : 'afterTextItem',
				text   : String.format(me.afterPageText, 1),
				margins: '0 5 0 0'
			}, '-', {
				ref         : 'next',
				tooltip     : me.nextText,
				overflowText: me.nextText,
				iconCls     : 'x-tbar-page-next',
				disabled    : true,
				handler     : me.moveNext,
				scope       : me
			}, {
				ref         : 'last',
				tooltip     : me.lastText,
				overflowText: me.lastText,
				iconCls     : 'x-tbar-page-last',
				disabled    : true,
				handler     : me.moveLast,
				scope       : me
			}, '->', {
				xtype         : 'combo',
				ref           : 'scaleCombo',
				triggerAction : 'all',
				lazyInit      : false,
				forceSelection: true,
				editable      : false,
				autoSelect    : true,
				disabled      : true,
				hidden        : true, // dont show this element . (for now... TODO)
				width         : me.scaleWidth,
				store         : {
					xtype      : 'jsonstore',
					autoDestroy: true,
					fields     : ['scale', 'text'],
					data       : [
						{
							scale: 0.5,
							text : '50%'
						}, {
							scale: 0.75,
							text : '75%'
						}, {
							scale: 1,
							text : '100%'
						}, {
							scale: 1.25,
							text : '125%'
						}, {
							scale: 1.5,
							text : '150%'
						}, {
							scale: 2,
							text : '200%'
						}, {
							scale: 4,
							text : '400%'
						}
					]
				},
				valueField    : 'scale',
				displayField  : 'text',
				mode          : 'local',
				listeners     : {
					scope : me,
					select: me.onScaleChange
				}
			}, {
				ref         : 'fullscreen',
				tooltip     : me.fullscreenText,
				overflowText: me.fullscreenText,
				iconCls     : 'files_icon_action_fullscreen',
				disabled    : false,
				handler     : me.displayFullscreen,
				scope       : me
			}]
		};
	},

	init: function () {
		// init gui elements
		this.pageContainer = this.el.query('.pdf-page-container')[0];

		if (!PDFJS.disableTextLayer) {
			this.textLayerDiv = this.el.query('.pdf-text-layer')[0];
		}

		if (this.disableWorker) {
			PDFJS.disableWorker = true;
		}

		// Asynchronously download PDF as an ArrayBuffer
		this.getDocument();
	}
	,

	onLoad: function () {
		try {
			var me = this, isEmpty;

			isEmpty = me.pdfDoc.numPages === 0;
			me.currentPage = me.currentPage || (isEmpty ? 0 : 1);

			me.renderPage(me.currentPage);
		}
		catch (error) {
			console.log("PDF: Can't render: " + error.message);
		}
	}
	,

	renderPage: function (num) {
		var me = this;
		var isEmpty, pageCount, currPage, afterText;
		var toolbar = me.getBottomToolbar();

		if (this.isRendering) {
			return;
		}

		me.isRendering = true;
		me.currentPage = num;

		currPage = me.currentPage;
		pageCount = me.pdfDoc.numPages;
		afterText = String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);
		isEmpty = pageCount === 0;

		toolbar.afterTextItem.setText(afterText);
		toolbar.inputItem.setDisabled(isEmpty);
		toolbar.inputItem.setValue(currPage);
		toolbar.first.setDisabled(currPage === 1 || isEmpty);
		toolbar.prev.setDisabled(currPage === 1 || isEmpty);
		toolbar.next.setDisabled(currPage === pageCount || isEmpty);
		toolbar.last.setDisabled(currPage === pageCount || isEmpty);
		toolbar.scaleCombo.setDisabled(isEmpty);
		toolbar.scaleCombo.setValue(me.pageScale);

		// Using promise to fetch the page
		me.pdfDoc.getPage(num).then(function (page) {

			if (!me.pageContainer) {
				// pageContainer not available. Widget destroyed already?
				me.isRendering = false;

				return;
			}

			var viewport = page.getViewport(me.pageScale);
			me.pageContainer.height = viewport.height;
			me.pageContainer.width = viewport.width;

			var ctx = me.pageContainer.getContext('2d');
			ctx.save();
			ctx.fillStyle = 'rgb(255, 255, 255)';
			ctx.fillRect(0, 0, me.pageContainer.width, me.pageContainer.height);
			ctx.restore();

			// see https://github.com/SunboX/ext_ux_pdf_panel/blob/master/ux/panel/PDF.js
			var textLayer = null; // TODO: this should be implemented...

			// Render PDF page into canvas context
			var renderContext = {
				canvasContext: ctx,
				viewport     : viewport,
				textLayer    : textLayer
			};
			page.render(renderContext);

			me.isRendering = false;

			if (me.loader) {
				me.loader.destroy();
			}

			if (me.rendered) {
				me.fireEvent('change', me, {
					current: me.currentPage,
					total  : me.pdfDoc.numPages
				});
			}
		});
	},

	moveFirst: function () {
		var me = this;
		if (me.fireEvent('beforechange', me, 1) !== false) {
			me.renderPage(1);
		}
	},

	movePrevious: function () {
		var me = this,
			prev = me.currentPage - 1;

		if (prev > 0) {
			if (me.fireEvent('beforechange', me, prev) !== false) {
				me.renderPage(prev);
			}
		}
	},

	moveNext: function () {
		var me = this,
			total = me.pdfDoc.numPages,
			next = me.currentPage + 1;

		if (next <= total) {
			if (me.fireEvent('beforechange', me, next) !== false) {
				me.renderPage(next);
			}
		}
	},

	moveLast: function () {
		var me = this,
			last = me.pdfDoc.numPages;

		if (me.fireEvent('beforechange', me, last) !== false) {
			me.renderPage(last);
		}
	},

	readPageFromInput: function () {
		var me = this, v = me.getBottomToolbar().inputItem.getValue(),
			pageNum = parseInt(v, 10);

		if (!v || isNaN(pageNum)) {
			me.getBottomToolbar().inputItem.setValue(me.currentPage);
			return false;
		}
		return pageNum;
	},

	onPagingFocus: function () {
		this.getBottomToolbar().inputItem.select();
	},

	onPagingBlur: function (e) {
		var curPage = this.getPageData().currentPage;
		this.getBottomToolbar().inputItem.setValue(curPage);
	},

	onPagingKeyDown: function (field, e) {
		var me = this,
			k = e.getKey(),
			increment = e.shiftKey ? 10 : 1,
			pageNum, total = me.pdfDoc.numPages;

		if (k == e.RETURN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum !== false) {
				pageNum = Math.min(Math.max(1, pageNum), total);
				if (me.fireEvent('beforechange', me, pageNum) !== false) {
					me.renderPage(pageNum);
				}
			}
		} else if (k == e.HOME || k == e.END) {
			e.stopEvent();
			pageNum = k == e.HOME ? 1 : total;
			field.setValue(pageNum);
		} else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum) {
				if (k == e.DOWN || k == e.PAGE_DOWN) {
					increment *= -1;
				}
				pageNum += increment;
				if (pageNum >= 1 && pageNum <= total) {
					field.setValue(pageNum);
				}
			}
		}
	},

	onScaleChange: function (combo, record) {
		var me = this;

		me.pageScale = record.get(combo.valueField);
		me.renderPage(me.currentPage);
	},

	displayFullscreen: function () {
		var pdfBoxCfg = {
			easing        : 'elasticOut',
			resizeDuration: 0.6,
			close         : '&#215;',
			hideInfo      : 'auto',
			href          : this.src,
			title         : this.tile
		};
		Ext.ux.PdfBox.open(pdfBoxCfg);
	},

	setSrc: function (src) {
		this.src = src;
		return this.getDocument();
	}
	,

	getDocument: function () {
		var me = this;
		if (!!me.src) {
			PDFJS.getDocument(me.src).then(function (pdfDoc) {
				me.pdfDoc = pdfDoc;
				me.onLoad();
			});
		}
		return me;
	}
})
;

Ext.reg('filesplugin.pdfjspanel', Zarafa.plugins.files.ui.snippets.PDFjsPanel);
Ext.namespace('Zarafa.plugins.files.ui.snippets');

Zarafa.plugins.files.ui.snippets.WebODFPanel = Ext.extend(Ext.Panel, {
	/**
	 * @cfg {Object} odfCanvas is Object of Odf.odfCanvas class.
	 * This object is responsible for rendering or opening
	 * the WebODF document in light box.
	 */
	odfCanvas: null,

	/**
	 * @cfg {Array} pages The pages in which all pages(slide) should be placed.
	 * This is only useful for ODP type of document.
	 */
	pages : [],

	/**
	 * @cfg {Number} currentPage The currentPage which contain index of
	 * the current page(slide) of the ODP document.
	 */
	currentPage : 0,

	/**
	 * @cfg {String} src The path to the odf file.
	 */
	src: null,

	/**
	 * @cfg {String} title The title of the odf document.
	 */
	title: '',

	/**
	 * @cfg{String} loadingMessage
	 * The text displayed when loading the PDF.
	 */
	loadingMessage: 'Loading PDF, please wait...',

	/**
	 * @cfg{String} beforePageText
	 * The text displayed before the input item.
	 */
	beforePageText: 'Page',

	/**
	 * @cfg{String} afterPageText
	 * Customizable piece of the default paging text. Note that this string is formatted using
	 *{0} as a token that is replaced by the number of total pages. This token should be preserved when overriding this
	 * string if showing the total page count is desired.
	 */
	afterPageText: 'of {0}',

	/**
	 * @cfg{String} firstText
	 * The quicktip text displayed for the first page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	firstText: 'First Page',

	/**
	 * @cfg{String} prevText
	 * The quicktip text displayed for the previous page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	prevText: 'Previous Page',

	/**
	 * @cfg{String} nextText
	 * The quicktip text displayed for the next page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	nextText: 'Next Page',

	/**
	 * @cfg{String} lastText
	 * The quicktip text displayed for the last page button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	lastText: 'Last Page',

	/**
	 * @cfg{String} fullscreenText
	 * The quicktip text displayed for the fullscreen button.
	 * **Note**: quick tips must be initialized for the quicktip to show.
	 */
	fullscreenText: 'Fullscreen',

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the input field used to display and change the current page number.
	 */
	inputItemWidth: 30,

	/**
	 * @cfg{Number} inputItemWidth
	 * The width in pixels of the combobox used to change display scale of the PDF.
	 */
	scaleWidth: 60,

	/**
	 * @property {String} panelID The id of the webodf canvas panel
	 */
	panelID: null,

	/**
	 * @constructor
	 */
	constructor: function (config) {
		config = config || {};

		config = Ext.applyIf(config, {
			xtype : 'filesplugin.webodfpanel',
			border: false,
			header: false, // hide title
			items : [{
				xtype    : 'component',
				cls: 'webodfpanel-outerDocumentContainer',
				autoEl   : {
					cn: [
						{tag: 'div', 'class': 'webodfpanel-canvas'}
					]
				},
				listeners: {
					afterrender: this.initODF.createDelegate(this)
				}
			}],
			bbar : this.getPagingToolbar()
		});

		Zarafa.plugins.files.ui.snippets.WebODFPanel.superclass.constructor.call(this, config);
	},

	getPagingToolbar: function () {
		var me = this;

		return {
			xtype: 'toolbar',
			height: 25,
			items: [{
				ref         : 'first',
				tooltip     : me.firstText,
				overflowText: me.firstText,
				iconCls     : 'x-tbar-page-first',
				disabled    : true,
				handler     : me.moveFirst,
				scope       : me
			}, {
				ref         : 'prev',
				tooltip     : me.prevText,
				overflowText: me.prevText,
				iconCls     : 'x-tbar-page-prev',
				disabled    : true,
				handler     : me.movePrevious,
				scope       : me
			}, '-', me.beforePageText, {
				xtype          : 'numberfield',
				ref            : 'inputItem',
				name           : 'inputItem',
				cls            : 'x-tbar-page-number',
				minValue       : 1,
				allowDecimals  : false,
				allowNegative  : false,
				enableKeyEvents: true,
				selectOnFocus  : true,
				submitValue    : false,
				width          : me.inputItemWidth,
				disabled       : true,
				margins        : '-1 2 3 2',
				listeners      : {
					scope  : me,
					keydown: me.onPagingKeyDown,
					blur   : me.onPagingBlur
				}
			}, {
				xtype  : 'tbtext',
				ref    : 'afterTextItem',
				text   : String.format(me.afterPageText, 1),
				margins: '0 5 0 0'
			}, '-', {
				ref         : 'next',
				tooltip     : me.nextText,
				overflowText: me.nextText,
				iconCls     : 'x-tbar-page-next',
				disabled    : true,
				handler     : me.moveNext,
				scope       : me
			}, {
				ref         : 'last',
				tooltip     : me.lastText,
				overflowText: me.lastText,
				iconCls     : 'x-tbar-page-last',
				disabled    : true,
				handler     : me.moveLast,
				scope       : me
			}, '->', {
				xtype       : 'combo',
				ref         : 'scaleCombo',
				triggerAction: 'all',
				lazyInit    : false,
				forceSelection: true,
				editable: false,
				autoSelect: true,
				disabled    : true,
				hidden: true, // dont show this element . (for now... TODO)
				width       : me.scaleWidth,
				store       : {
					xtype: 'jsonstore',
					autoDestroy : true,
					fields: ['scale', 'text'],
					data  : [
						{
							scale: 0.5,
							text: '50%'
						},{
							scale: 0.75,
							text: '75%'
						},{
							scale: 1,
							text: '100%'
						},{
							scale: 1.25,
							text: '125%'
						},{
							scale: 1.5,
							text: '150%'
						},{
							scale: 2,
							text: '200%'
						},{
							scale: 4,
							text: '400%'
						}
					]
				},
				valueField  : 'scale',
				displayField: 'text',
				mode        : 'local',
				listeners   : {
					scope : me,
					select : me.onScaleChange
				}
			}, {
				ref         : 'fullscreen',
				tooltip     : me.fullscreenText,
				overflowText: me.fullscreenText,
				iconCls     : 'files_icon_action_fullscreen',
				disabled    : false,
				handler     : me.displayFullscreen,
				scope       : me
			}]
		};
	},

	initODF: function () {
		// init gui elements
		this.canvasContainer = Ext.DomQuery.selectNode('div[class*=webodfpanel-canvas]', this.el.dom);

		this.odfCanvas = new odf.OdfCanvas(this.canvasContainer);
		this.odfCanvas.load(this.src);
	},

	moveFirst: function () {
		var me = this;
		if (me.fireEvent('beforechange', me, 1) !== false) {
			me.renderPage(1);
		}
	},

	movePrevious: function () {
		var me = this,
			prev = me.currentPage - 1;

		if (prev > 0) {
			if (me.fireEvent('beforechange', me, prev) !== false) {
				me.renderPage(prev);
			}
		}
	},

	moveNext: function () {
		var me = this,
			total = me.pdfDoc.numPages,
			next = me.currentPage + 1;

		if (next <= total) {
			if (me.fireEvent('beforechange', me, next) !== false) {
				me.renderPage(next);
			}
		}
	},

	moveLast: function () {
		var me = this,
			last = me.pdfDoc.numPages;

		if (me.fireEvent('beforechange', me, last) !== false) {
			me.renderPage(last);
		}
	},

	readPageFromInput: function () {
		var me = this, v = me.getBottomToolbar().inputItem.getValue(),
			pageNum = parseInt(v, 10);

		if (!v || isNaN(pageNum)) {
			me.getBottomToolbar().inputItem.setValue(me.currentPage);
			return false;
		}
		return pageNum;
	},

	onPagingFocus: function () {
		this.getBottomToolbar().inputItem.select();
	},

	onPagingBlur: function (e) {
		var curPage = this.getPageData().currentPage;
		this.getBottomToolbar().inputItem.setValue(curPage);
	},

	onPagingKeyDown: function (field, e) {
		var me = this,
			k = e.getKey(),
			increment = e.shiftKey ? 10 : 1,
			pageNum, total = me.pdfDoc.numPages;

		if (k == e.RETURN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum !== false) {
				pageNum = Math.min(Math.max(1, pageNum), total);
				if (me.fireEvent('beforechange', me, pageNum) !== false) {
					me.renderPage(pageNum);
				}
			}
		} else if (k == e.HOME || k == e.END) {
			e.stopEvent();
			pageNum = k == e.HOME ? 1 : total;
			field.setValue(pageNum);
		} else if (k == e.UP || k == e.PAGE_UP || k == e.DOWN || k == e.PAGE_DOWN) {
			e.stopEvent();
			pageNum = me.readPageFromInput();
			if (pageNum) {
				if (k == e.DOWN || k == e.PAGE_DOWN) {
					increment *= -1;
				}
				pageNum += increment;
				if (pageNum >= 1 && pageNum <= total) {
					field.setValue(pageNum);
				}
			}
		}
	},

	onScaleChange: function (combo, record) {
		var me = this;

		me.pageScale = record.get(combo.valueField);
		me.renderPage(me.currentPage);
	},

	displayFullscreen: function () {
		var webodfCfg = {
			resizeDuration : 0.40,
			overlayDuration : 0.6,
			href : this.src,
			title : this.title
		};
		Zarafa.plugins.webodf.WebOdfBox.open(webodfCfg);
	}
});

Ext.reg('filesplugin.webodfpanel', Zarafa.plugins.files.ui.snippets.WebODFPanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesFolderNode
 * @extends Ext.tree.AsyncTreeNode
 *
 * This will register itself as 'folder' nodetype in the {@link Ext.tree.TreePanel#nodeTypes} object.
 */
Zarafa.plugins.files.ui.FilesFolderNode = Ext.extend(Ext.tree.AsyncTreeNode, {
	/**
	 * @cfg {Zarafa.core.data.IPFRecord} folder the folder for which this tree node will be created
	 */
	folder : undefined,

	/**
	 * @cfg {Boolean} isNodeSelected the isNodeSelected is true when the folder is selected or checked, false otherwise.
	 */
	isNodeSelected : false,

	/**
	 * @cfg {String/Ext.XTemplate} tpl The template which must be applied on the {@link #text} for
	 * rendering into the {@link Ext.tree.TreePanel tree}.
	 */
	tpl : new Ext.XTemplate('{text:htmlEncode}', { compiled : true }),

	/*
	 * @constructor
	 * @param {Object} config configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		var folder = config.folder;

		// If a folder is provided we have an extra opportunity to
		// apply some extra defaults.
		if (Ext.isDefined(folder)) {
			Ext.applyIf(config, {
				id :folder.get('id')
			});
		}

		Ext.applyIf(config, {
			containerCls : 'zarafa-tree-container',
			cls: 'zarafa-tree-node'
		});

		Zarafa.plugins.files.ui.FilesFolderNode.superclass.constructor.call(this, config);

		if (Ext.isString(this.tpl)) {
			this.tpl = new Ext.XTemplate(this.tpl, { compiled : true });
		}

		if (folder) {
			this.updateUI(folder);
		}
	},

	/**
	 * @return {Zarafa.core.data.IPFRecord} The folder which is represented by this node
	 */
	getFolder : function()
	{
		return this.attributes.folder;
	},

	/**
	 * Updates UI of node
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder
	 */
	updateUI : function(folder)
	{
		// Update attributes
		this.attributes.folder = folder;

		// pass the text through text renderer
		this.setText(this.getTextFromFolder(folder));

		// Only update the icon class if the icon is not an svg icon
		// Otherwise we will get errors
		if ( !this.ui.getIconEl() || this.ui.getIconEl().tagName !== 'svg' ){
			// pass the icon through the icon renderer
			this.setIcon(folder.getIcon());

			// pass the class through to the renderer
			this.setContainerCls(this.attributes.containerCls);
		}
	},

	/**
	 * Sets the icon for this node
	 * @param {String} iconCls
	 */
	setIcon : function(iconCls)
	{
		var oldIconCls = this.iconCls;
		this.iconCls = this.attributes.iconCls = iconCls;

		if (this.rendered) {
			this.getUI().onIconChange(this, iconCls, oldIconCls);
		}
	},

	/**
	 * Sets the class for this node's container
	 * @param {String} cls
	 */
	setContainerCls : function(cls)
	{
		var oldCls = this.containerCls;
		this.containerCls = this.attributes.containerCls = cls;

		if (this.rendered) {
			this.getUI().onContainerClsChange(this, cls, oldCls);
		}
	},

	/**
	 * Obtain the Display Name for the current {@link #folder}.
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder for which the display name is requested
	 * @return {String} The name of the folder which must be shown.
	 * @protected
	 */
	getTextFromFolder : function(folder)
	{
		return folder.getDisplayName();
	},

	/**
	 * Update the node based on the {@link #folder} data.
	 * @param {Boolean} deep True to call {@link #update} on all
	 * {@link #childNodes}.
	 */
	update : function(deep) {
		var folder;

		if (this.ownerTree && this.ownerTree.model) {
			folder = this.ownerTree.model.getFolder(this.getFolder().get('id'));
		}

		if (deep) {
			for (var i = 0, len = this.childNodes.length; i < len; i++) {
				this.childNodes[i].update(deep);
			}
		}
	}
});

Ext.tree.TreePanel.nodeTypes.filesfolder = Zarafa.plugins.files.ui.FilesFolderNode;
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.NavigatorTreePanel
 * @extends Zarafa.plugins.files.ui.Tree
 * @xtype filesplugin.navigatortreepanel
 *
 * The hierarchy tree panel implementation for files.
 */
Zarafa.plugins.files.ui.NavigatorTreePanel = Ext.extend(Zarafa.plugins.files.ui.Tree, {

	/**
	 * @property {String} nodeToSelect is the path of the node that should be selected.
	 */
	nodeToSelect : null,

	/**
	 * @cfg {@link Zarafa.plugins.files.data.FilesRecordStore filesStore} which contains
	 * {@link Zarafa.plugins.files.data.FilesRecord FilesRecord}.
	 */
	filesStore : undefined,

	/**
	 * @constructor
	 * @param config
	 */
	constructor: function (config)
	{
		config = config || {};

		Ext.applyIf(config, {
			xtype : 'filesplugin.navigatortreepanel',
			loadMask : true

		});

		Zarafa.plugins.files.ui.NavigatorTreePanel.superclass.constructor.call(this, config);

		this.mon(this.store, 'removeFolder', this.onFolderRemove, this);
	},

	/**
	 * Event handler which is fired when the {@link #store} fires the
	 * {@link Zarafa.hierarchy.data.HierarchyStore#removeFolder} event handlerr. This will check
	 * if the folder is currently opened, and will deselect that folder.
	 *
	 * @param {Zarafa.plugins.files.data.FilesHierarchyStore} store The store which fired the event
	 * @param {Zarafa.plugins.files.data.FilesStoreRecord} storeRecord The store from where the folder is
	 * removed
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord} folder The folder which was removed from the store
	 * @private
	 */
	onFolderRemove : function(store, storeRecord, folder)
	{
		if (this.model) {
			this.model.removeFolder(folder);
		}
	},

	/**
	 * Function called by Extjs when the {@link Zarafa.plugins.files.ui.Tree TreePanel}
	 * has been {@link #render rendered}. At this time all events can be registered.
	 * @private
	 */
	initEvents : function()
	{
		Zarafa.plugins.files.ui.NavigatorTreePanel.superclass.initEvents.apply(this, arguments);

		this.on({
			"click" : this.onNodeClick,
			"beforenodedrop" : this.onBeforeNodeDrop,
			"nodedragover" : this.onNodeDragOver,
			"afterrender" : this.onAfterRender,
			"contextmenu" : this.onContextMenu,
			scope : this
		});

		this.mon(container, 'folderselect', this.onFolderSelect, this);
		this.mon(this.store, 'load', this.onHierarchyStoreLoad, this);
	},

	/**
	 * The {@link Ext.tree.TreePanel#beforenodedrop} event handler. It will move dropped nodes to the new
	 * location.
	 *
	 * @param event
	 * @returns {*}
	 */
	onBeforeNodeDrop: function (event)
	{
		if (Ext.isArray(event.data.selections)) {
			event.cancel = false;

			Ext.each(event.data.selections, function (record) {
				record.setDisabled(true);
			});

			return Zarafa.plugins.files.data.Actions.moveRecords(event.data.selections, event.target.getFolder(), {hierarchyStore : this.store});
		}
	},

	/**
	 * The {@link Ext.tree.TreePanel#nodedragover} event handler. This function will check if dropping a node on
	 * this hovered node is allowed. (For example: same account check)
	 *
	 * @param event
	 * @returns {boolean}
	 */
	onNodeDragOver: function (event)
	{
		var ret = true;
		var targetFolder = event.target.getFolder();
		var filesStore = targetFolder.getFilesStore();
		var targetedAccountID = filesStore.get('backend_config').current_account_id;

		Ext.each(event.data.selections, function (record) {
			var parentFolder = this.store.getFolder(record.get('parent_entryid'));

			if (!Ext.isDefined(parentFolder)) {
				ret = false;
				return false;
			}

			var accountID = parentFolder.getFilesStore().get('backend_config').current_account_id;

			if (targetedAccountID !== accountID) {
				ret = false;
				return false;
			}

			if (targetFolder.get('entryid') === record.get('entryid')  || parentFolder.get("entryid") === targetFolder.get('entryid')) {
				ret = false;
				return false;
			}
		}, this);

		return ret;
	},

	/**
	 * The {@link Ext.tree.TreePanel#click} event handler. This function will expand the node after it was clicked.
	 *
	 * @param node
	 */
	onNodeClick: function (node)
	{
		container.selectFolder(node.getFolder());
	},

	/**
	 * Fires when the {@link Zarafa.core.Container} fires the
	 * {@link Zarafa.core.Container#folderselect} event. This
	 * will search for the corresponding node in the tree,
	 * and will mark the given folder as {@link #selectFolderInTree selected}.
	 *
	 * @param {Zarafa.hierarchy.data.MAPIFolderRecord|Array} folder The folder which
	 * is currently selected.
	 * @private
	 */
	onFolderSelect : function(folder)
	{
		if (Array.isArray(folder)) {

			// If we have multi selected folder then select previously selected node in tree.
			if (folder.length > 1 && this.model) {
				folder = this.model.getDefaultFolder();
			} else {
				folder = folder[0];
			}
		}

		// Select the node of selected folder.
		if (folder) {
			if (Ext.isFunction(folder.isHomeFolder) && folder.isHomeFolder()) {
				this.selModel.clearSelections();
			} else {
				this.selectFolderInTree(folder);
			}
		}
	},

	/**
	 * Event handler triggered when {@link Zarafa.plugins.files.data.FilesHierarchyStore FilesHierarchyStore}.
	 * @param {Object} loader TreeLoader object.
	 * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
	 * @param {Object} response The response object containing the data from the server.
	 * @private
	 */
	onHierarchyStoreLoad : function()
	{
		// TODO: Need to find proper way to refresh the hierarchy.
		this.root.reload();
		this.onFolderSelect(this.model.getDefaultFolder());
	},

	/**
	 * The {@link Ext.tree.TreePanel#afterrender} event handler. The DropZone needs to set up after the panel
	 * has been rendered.
	 *
	 * @param treepanel
	 */
	onAfterRender: function (treepanel)
	{
		// TODO: Create HierarchyItemDropZone, HierarchyFolderDropZone and HierarchyTreeDropZone
		// instead of Ext.tree.TreeDropZone. it will help in future to support drag and drop folder/file
		// inter files account.
		this.dragZone.lock();
		this.dropZone = new Ext.tree.TreeDropZone(this, {
			ddGroup: this.ddGroup,
			appendOnly: this.ddAppendOnly === true,
			getDropPoint : function(e, n, dd)
			{
				return 'append';
			}
		});
	},

	/**
	 * Eventhandler for the context menu event. This will show the default content menu.
	 *
	 * @param node
	 * @param event
	 */
	onContextMenu: function (node, event)
	{
		var component = Zarafa.core.data.SharedComponentType['zarafa.plugins.files.treecontextmenu'];
		Zarafa.core.data.UIFactory.openContextMenu(component, node.getFolder(), {
			position: event.getXY(),
			model : this.model
		});
	}
});

Ext.reg('filesplugin.navigatortreepanel', Zarafa.plugins.files.ui.NavigatorTreePanel);
Ext.namespace('Zarafa.plugins.files.ui');

/**
 * @class Zarafa.plugins.files.ui.FilesRootFolderNode
 * @extends Zarafa.plugins.files.ui.FilesFolderNode
 *
 * This will register itself as 'rootfolder' nodetype in the {@link Ext.tree.TreePanel#nodeTypes} object.
 */
Zarafa.plugins.files.ui.FilesRootFolderNode = Ext.extend(Zarafa.plugins.files.ui.FilesFolderNode, {
	/*
	 * @constructor
	 * @param {Object} config configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		var containerCls = 'zarafa-tree-root-container';
		var nodeCls = 'zarafa-tree-root-node';

		config.folder.isSubTreeFolder();
		if (config.folder) {
			containerCls += ' zarafa-tree-ipm-subtree-container';
			nodeCls += ' zarafa-tree-ipm-subtree-node';
			config.id = config.folder.get('id');
		}

		Ext.applyIf(config, {
			containerCls : containerCls,
			cls : nodeCls,
			expanded: true,
			allowDrag : false,
			draggable : false
		});

		Zarafa.plugins.files.ui.FilesRootFolderNode.superclass.constructor.call(this, config);
	}
});

Ext.tree.TreePanel.nodeTypes.filesrootfolder = Zarafa.plugins.files.ui.FilesRootFolderNode;
