(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[17],{

/***/ 108:
/***/ (function(module, exports) {

module.exports = function escape(url) {
    if (typeof url !== 'string') {
        return url
    }
    // If url is already wrapped in quotes, remove them
    if (/^['"].*['"]$/.test(url)) {
        url = url.slice(1, -1);
    }
    // Should url be wrapped?
    // See https://drafts.csswg.org/css-values-3/#urls
    if (/["'() \t\n]/.test(url)) {
        return '"' + url.replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"'
    }

    return url
}


/***/ }),

/***/ 16:
/***/ (function(module, exports) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function(useSourceMap) {
	var list = [];

	// return the list of modules as css string
	list.toString = function toString() {
		return this.map(function (item) {
			var content = cssWithMappingToString(item, useSourceMap);
			if(item[2]) {
				return "@media " + item[2] + "{" + content + "}";
			} else {
				return content;
			}
		}).join("");
	};

	// import a list of modules into the list
	list.i = function(modules, mediaQuery) {
		if(typeof modules === "string")
			modules = [[null, modules, ""]];
		var alreadyImportedModules = {};
		for(var i = 0; i < this.length; i++) {
			var id = this[i][0];
			if(typeof id === "number")
				alreadyImportedModules[id] = true;
		}
		for(i = 0; i < modules.length; i++) {
			var item = modules[i];
			// skip already imported module
			// this implementation is not 100% perfect for weird media query combinations
			//  when a module is imported multiple times with different media queries.
			//  I hope this will never occur (Hey this way we have smaller bundles)
			if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
				if(mediaQuery && !item[2]) {
					item[2] = mediaQuery;
				} else if(mediaQuery) {
					item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
				}
				list.push(item);
			}
		}
	};
	return list;
};

function cssWithMappingToString(item, useSourceMap) {
	var content = item[1] || '';
	var cssMapping = item[3];
	if (!cssMapping) {
		return content;
	}

	if (useSourceMap && typeof btoa === 'function') {
		var sourceMapping = toComment(cssMapping);
		var sourceURLs = cssMapping.sources.map(function (source) {
			return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
		});

		return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
	}

	return [content].join('\n');
}

// Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
	// eslint-disable-next-line no-undef
	var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
	var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;

	return '/*# ' + data + ' */';
}


/***/ }),

/***/ 17:
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

var stylesInDom = {};

var	memoize = function (fn) {
	var memo;

	return function () {
		if (typeof memo === "undefined") memo = fn.apply(this, arguments);
		return memo;
	};
};

var isOldIE = memoize(function () {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
});

var getElement = (function (fn) {
	var memo = {};

	return function(selector) {
		if (typeof memo[selector] === "undefined") {
			var styleTarget = fn.call(this, selector);
			// Special case to return head of iframe instead of iframe itself
			if (styleTarget instanceof window.HTMLIFrameElement) {
				try {
					// This will throw an exception if access to iframe is blocked
					// due to cross-origin restrictions
					styleTarget = styleTarget.contentDocument.head;
				} catch(e) {
					styleTarget = null;
				}
			}
			memo[selector] = styleTarget;
		}
		return memo[selector]
	};
})(function (target) {
	return document.querySelector(target)
});

var singleton = null;
var	singletonCounter = 0;
var	stylesInsertedAtTop = [];

var	fixUrls = __webpack_require__(40);

module.exports = function(list, options) {
	if (typeof DEBUG !== "undefined" && DEBUG) {
		if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};

	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (!options.singleton && typeof options.singleton !== "boolean") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
	if (!options.insertInto) options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (!options.insertAt) options.insertAt = "bottom";

	var styles = listToStyles(list, options);

	addStylesToDom(styles, options);

	return function update (newList) {
		var mayRemove = [];

		for (var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];

			domStyle.refs--;
			mayRemove.push(domStyle);
		}

		if(newList) {
			var newStyles = listToStyles(newList, options);
			addStylesToDom(newStyles, options);
		}

		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];

			if(domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();

				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom (styles, options) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];

		if(domStyle) {
			domStyle.refs++;

			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}

			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];

			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}

			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles (list, options) {
	var styles = [];
	var newStyles = {};

	for (var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = options.base ? item[0] + options.base : item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};

		if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
		else newStyles[id].parts.push(part);
	}

	return styles;
}

function insertStyleElement (options, style) {
	var target = getElement(options.insertInto)

	if (!target) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}

	var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];

	if (options.insertAt === "top") {
		if (!lastStyleElementInsertedAtTop) {
			target.insertBefore(style, target.firstChild);
		} else if (lastStyleElementInsertedAtTop.nextSibling) {
			target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			target.appendChild(style);
		}
		stylesInsertedAtTop.push(style);
	} else if (options.insertAt === "bottom") {
		target.appendChild(style);
	} else if (typeof options.insertAt === "object" && options.insertAt.before) {
		var nextSibling = getElement(options.insertInto + " " + options.insertAt.before);
		target.insertBefore(style, nextSibling);
	} else {
		throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
	}
}

function removeStyleElement (style) {
	if (style.parentNode === null) return false;
	style.parentNode.removeChild(style);

	var idx = stylesInsertedAtTop.indexOf(style);
	if(idx >= 0) {
		stylesInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement (options) {
	var style = document.createElement("style");

	options.attrs.type = "text/css";

	addAttrs(style, options.attrs);
	insertStyleElement(options, style);

	return style;
}

function createLinkElement (options) {
	var link = document.createElement("link");

	options.attrs.type = "text/css";
	options.attrs.rel = "stylesheet";

	addAttrs(link, options.attrs);
	insertStyleElement(options, link);

	return link;
}

function addAttrs (el, attrs) {
	Object.keys(attrs).forEach(function (key) {
		el.setAttribute(key, attrs[key]);
	});
}

function addStyle (obj, options) {
	var style, update, remove, result;

	// If a transform function was defined, run it on the css
	if (options.transform && obj.css) {
	    result = options.transform(obj.css);

	    if (result) {
	    	// If transform returns a value, use that instead of the original css.
	    	// This allows running runtime transformations on the css.
	    	obj.css = result;
	    } else {
	    	// If the transform function returns a falsy value, don't add this css.
	    	// This allows conditional loading of css
	    	return function() {
	    		// noop
	    	};
	    }
	}

	if (options.singleton) {
		var styleIndex = singletonCounter++;

		style = singleton || (singleton = createStyleElement(options));

		update = applyToSingletonTag.bind(null, style, styleIndex, false);
		remove = applyToSingletonTag.bind(null, style, styleIndex, true);

	} else if (
		obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function"
	) {
		style = createLinkElement(options);
		update = updateLink.bind(null, style, options);
		remove = function () {
			removeStyleElement(style);

			if(style.href) URL.revokeObjectURL(style.href);
		};
	} else {
		style = createStyleElement(options);
		update = applyToTag.bind(null, style);
		remove = function () {
			removeStyleElement(style);
		};
	}

	update(obj);

	return function updateStyle (newObj) {
		if (newObj) {
			if (
				newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap
			) {
				return;
			}

			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;

		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag (style, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (style.styleSheet) {
		style.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = style.childNodes;

		if (childNodes[index]) style.removeChild(childNodes[index]);

		if (childNodes.length) {
			style.insertBefore(cssNode, childNodes[index]);
		} else {
			style.appendChild(cssNode);
		}
	}
}

function applyToTag (style, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		style.setAttribute("media", media)
	}

	if(style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		while(style.firstChild) {
			style.removeChild(style.firstChild);
		}

		style.appendChild(document.createTextNode(css));
	}
}

function updateLink (link, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/*
		If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
		and there is no publicPath defined then lets turn convertToAbsoluteUrls
		on by default.  Otherwise default to the convertToAbsoluteUrls option
		directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls) {
		css = fixUrls(css);
	}

	if (sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = link.href;

	link.href = URL.createObjectURL(blob);

	if(oldSrc) URL.revokeObjectURL(oldSrc);
}


/***/ }),

/***/ 2037:
/***/ (function(module, exports) {

/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2019 Metaways Infosystems GmbH (http://www.metaways.de)
 */
Ext.namespace('Tine.OnlyOfficeIntegrator');
Tine.OnlyOfficeIntegrator.Application = Ext.extend(Tine.Tinebase.Application, {
  hasMainScreen: false,

  /**
   * Get translated application title
   *
   * @return {String}
   */
  getTitle: function getTitle() {
    return this.i18n._('Only Office Integrator');
  },
  init: function init() {}
});

/***/ }),

/***/ 2038:
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2019 Metaways Infosystems GmbH (http://www.metaways.de)
 */


const {
  retryAllRejectedPromises
} = __webpack_require__(373);

Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog = Ext.extend(Ext.Component, {
  /**
   * @cfg {String} JSON encoded node record
   */
  recordData: null,

  /**
   * @property {DocsAPI.DocEditor}
   */
  docEditor: null,

  /**
   * @private
   */
  windowNamePrefix: 'OnlyOfficeEditWindow_',
  border: false,
  style: 'height: 100%',
  initComponent: function initComponent() {
    this.app = Tine.Tinebase.appMgr.get('OnlyOfficeIntegrator');
    this.autoEl = {
      tag: 'div',
      cn: {
        tag: 'div',
        id: Ext.id(),
        cls: 'tine-viewport-waitcycle'
      }
    };
    this.onlyOfficeUrl = Tine.Tinebase.configManager.get('onlyOfficePublicUrl', 'OnlyOfficeIntegrator');
    this.initRecord(this.recordData);
    this.apiIsInjected = this.injectAPI();
    this.window.on('beforeclose', this.onBeforeCloseWindow, this);
    const tokenKeepAliveInterval = Tine.Tinebase.configManager.get('tokenLiveTime', 'OnlyOfficeIntegrator') * 800;
    window.setInterval(_.bind(this.tokenKeepAlive, this), tokenKeepAliveInterval);
    Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.superclass.initComponent.call(this);
  },
  initRecord: function initRecord(recordData) {
    this.record = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
    this.isTempfile = !!_.get(this.record, 'json.session_id');
    this.isAttachment = !!String(this.record.get('path')).match(/^\/records\//);
    this.enableHistory = !this.isTempfile;
  },
  injectAPI: async function injectAPI() {
    const apiUrl = this.onlyOfficeUrl + '/web-apps/apps/api/documents/api.js';
    const script = document.createElement("script");
    script.setAttribute("src", apiUrl);
    script.setAttribute("type", "text/javascript");
    document.getElementsByTagName('head')[0].appendChild(script);
    return Object(util_waitFor_es6__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"])(() => {
      return !!_.get(window, 'DocsAPI.DocEditor');
    }, 5000);
  },
  afterRender: async function afterRender() {
    const [config] = await Promise.all([this.getSignedConfig(), this.apiIsInjected]);
    this.window.onlyOfficeDocumentKey = _.get(config, 'document.key');
    this.window.onlyOfficeDocumentDirty = false;
    this.window.setTitle([Tine.title, 'ONLYOFFICE', this.record.get('name')].join(' - '));

    const localeParts = _.split(Tine.Tinebase.registry.get('locale').locale, '_');

    const lang = localeParts[0];
    const region = lang + '-' + localeParts[1] ? localeParts[1] : _.toUpper(lang);

    _.assign(config.editorConfig, {
      "lang": lang,
      "region": region
    });

    _.merge(config, {
      // only available in developer edition
      "customization": {
        "logo": {
          "image": Tine.logo,
          "imageEmbedded": Tine.logo,
          "url": Tine.weburl
        }
      },
      "events": {
        "onDocumentStateChange": _.bind(this.onDocumentStateChange, this),
        "onRequestInsertImage": _.bind(this.onRequestInsertImage, this),
        "onOutdatedVersion": _.bind(this.onOutdatedVersion, this),
        // offer exportAs for attachments/uploads?
        "onRequestSaveAs": _.bind(this.onRequestSaveAs, this)
      }
    });

    if (this.enableHistory) {
      _.merge(config, {
        "events": {
          "onRequestHistory": _.bind(this.onRequestHistory, this),
          "onRequestHistoryClose": _.bind(this.onRequestHistoryClose, this),
          "onRequestHistoryData": _.bind(this.onRequestHistoryData, this),
          "onRequestRestore": _.bind(this.onRequestRestore, this)
        }
      });
    }

    this.docEditor = new DocsAPI.DocEditor(this.autoEl.cn.id, config);
    Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.superclass.afterRender.call(this);
  },
  onDocumentStateChange: function onDocumentStateChange(e) {
    if (e.data) {
      this.window.onlyOfficeDocumentDirty = true;
    } // OO activity count's as user presence


    Tine.Tinebase.PresenceObserver.lastPresence = new Date().getTime();
  },
  onRequestInsertImage: function onRequestInsertImage() {
    if (!this.filePickerDialog) {
      this.filePickerDialog = new Tine.Filemanager.FilePickerDialog({
        windowTitle: this.app.i18n._('Select Image'),
        singleSelect: true,
        constraint: /(jpg|gif|png|tiff)$/
      });
      this.filePickerDialog.on('apply', async node => {
        const config = await Tine.OnlyOfficeIntegrator.getEmbedUrlForNodeId(node.id);
        this.docEditor.insertImage(_.assign(config, {
          "fileType": node.path.split('.').pop()
        }));
      });
      this.filePickerDialog.openWindow({
        closeAction: 'hide'
      });
    }

    this.filePickerDialog.window.show();
  },
  onOutdatedVersion: async function onOutdatedVersion() {
    if (this.isAttachment) {
      let [,, model, recordId] = this.record.get('path').split('/'); // arg: how to get recordProxy here in a generic way?

      window.location.reload();
    } else {
      const recordData = await Tine.Filemanager.getNode(this.record.id);
      this.record = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
    }

    this.docEditor.destroyEditor();
    this.afterRender();
  },
  onRequestSaveAs: function onRequestSaveAs(event) {
    const title = event.data.title;
    const url = event.data.url;
    const win = new Tine.Tinebase.widgets.file.SelectionDialog.openWindow({
      windowId: 'OnlyOfficeEditDialog.SaveAs' + this.id,
      mode: 'target',
      locationTypesEnabled: 'fm_node',
      windowTitle: this.app.i18n._('Save Copy As'),
      fileName: title,
      initialPath: this.record.get('path').match(/.*\//)[0],
      constraint: new RegExp('\\.' + url.split('.').pop() + '$'),
      listeners: {
        apply: async node => {
          const path = _.get(node, 'fm_path');

          const loadMask = new Ext.LoadMask(Ext.getBody(), {
            msg: this.app.i18n._('Exporting Document ...'),
            removeMask: true
          });
          loadMask.show();
          const recordData = await Tine.OnlyOfficeIntegrator.exportAs(url, path, true);

          if (_.get(recordData, 'name')) {
            window.postal.publish({
              channel: "recordchange",
              topic: 'Filemanager.Node.create',
              data: recordData
            });
          }

          loadMask.hide(); // switch to new document

          this.record = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
          this.docEditor.destroyEditor();
          this.afterRender();
        }
      }
    });
  },
  onRequestHistory: async function onRequestHistory() {
    const history = await Tine.OnlyOfficeIntegrator.getHistory(this.window.onlyOfficeDocumentKey);

    const keys = _.split(this.window.onlyOfficeDocumentKey, ',');

    this.window.onlyOfficeDocumentKey = _.join(_.uniq(_.compact(_.concat(keys, _.map(history, 'key')))), ',');
    this.docEditor.refreshHistory(history);
  },
  onRequestHistoryClose: async function onRequestHistoryClose() {
    this.onOutdatedVersion();
  },
  onRequestHistoryData: async function onRequestHistoryData(event) {
    const version = event.data;

    const keys = _.split(this.window.onlyOfficeDocumentKey, ',');

    try {
      const historyData = await Tine.OnlyOfficeIntegrator.getHistoryData(_.get(keys, '[0]'), version); // make sure history tokens are keeped alive

      keys.push(_.get(historyData, 'key'));
      keys.push(_.get(historyData, 'previous.key'));
      this.window.onlyOfficeDocumentKey = _.join(_.uniq(_.compact(keys)), ',');
      this.docEditor.setHistoryData(historyData);
    } catch (err) {
      this.docEditor.setHistoryData({
        "error": this.app.i18n._("Version details for version ".concat(version, " not available.")),
        "version": version
      });
    }
  },
  // @TODO: let user choose where to restore
  //        don't forget attachments
  onRequestRestore: async function onRequestRestore(event) {
    const url = event.data.url;
    const version = event.data.version;
    const fileLocationSrc = {
      type: this.isAttachment ? 'attachement' : 'fm_node',
      revision: version
    };

    if (this.isAttachment) {
      const [,, model, recordId] = this.record.get('path').split('/');

      _.assign(fileLocationSrc, {
        model: model,
        record_id: recordId,
        file_name: this.record.get('name')
      });
    } else {
      fileLocationSrc.fm_path = this.record.get('path');
    }

    const title = this.app.formatMessage('Overwrite Existing File?');
    const msg = this.app.formatMessage('You are about to replace "{filename}" by the old version {version}. All newer changes will be lost. Do you want to continue?', {
      version: version,
      filename: this.record.get('name')
    });
    const confirm = await new Promise(resolve => {
      Ext.MessageBox.confirm(title, msg, btn => {
        resolve(btn === 'yes');
      });
    });

    if (confirm) {
      const loadMask = new Ext.LoadMask(this.getEl(), {
        msg: this.app.formatMessage('Restoring ...'),
        removeMask: true
      });
      loadMask.show();
      await Tine.Tinebase.restoreRevision(fileLocationSrc);
      loadMask.hide();
      this.onRequestHistoryClose();
    }
  },
  onBeforeCloseWindow: function onBeforeCloseWindow() {
    /**
     * NOTE: Tine.OnlyOfficeIntegrator.tokenSignOut is done in Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.onEditorWindowClose
     *       as we need to use the main/parent window as this window can't process the request any more
     */
  },
  getSignedConfig: async function getSignedConfig() {
    if (this.createNewPromise) {
      this.initRecord(await this.createNewPromise);
    }

    const recordId = this.record.getId();
    let config = null; // we see this in the logs, reloaded opener?

    if (!recordId) {
      Ext.MessageBox.show({
        buttons: Ext.Msg.OK,
        icon: Ext.MessageBox.ERROR,
        title: this.app.i18n._('No Document Specified'),
        msg: this.app.i18n._('Something went wrong, please try again later.'),
        fn: () => {
          this.window.close();
        }
      });
      return config;
    }

    try {
      if (this.isAttachment) {
        let [,, model, recordId] = this.record.get('path').split('/');
        config = await Tine.OnlyOfficeIntegrator.getEditorConfigForAttachment(model, recordId, this.record.id);
      } else if (this.isTempfile) {
        config = await Tine.OnlyOfficeIntegrator.getEditorConfigForTempFileId(recordId, this.record.get('name'));
      } else {
        config = await Tine.OnlyOfficeIntegrator.getEditorConfigForNodeId(recordId);
      }
    } catch (err) {
      // if (_.get(err, 'data.code') === 647) {
      Ext.MessageBox.show({
        buttons: Ext.Msg.OK,
        icon: Ext.MessageBox.WARNING,
        title: this.app.i18n._('Outdated Revision'),
        msg: this.app.i18n._('This revision of the document is outdated, please open the latest revision'),
        fn: () => {
          this.window.close();
        }
      });
    }

    return config;
  },
  tokenKeepAlive: function tokenKeepAlive() {
    const keys = _.compact(_.split(this.window.onlyOfficeDocumentKey, ','));

    if (!keys.length) return;
    retryAllRejectedPromises([_.partial(Tine.OnlyOfficeIntegrator.tokenKeepAlive, keys)], {
      maxAttempts: 5,
      delay: 1200
    });
  },
  // fake container
  setSize: Ext.emptyFn,
  doLayout: Ext.emptyFn
});

Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.openWindow = function (config) {
  const win = Tine.WindowFactory.getWindow({
    width: 1024,
    height: 768,
    name: Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.prototype.windowNamePrefix + config.id,
    contentPanelConstructor: 'Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog',
    contentPanelConstructorConfig: config
  });
  win.on('beforeclose', _.partial(Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.onEditorWindowClose, _, config.recordData));
  return win;
};

Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.onEditorWindowClose = async function (win, recordData) {
  const app = Tine.Tinebase.appMgr.get('OnlyOfficeIntegrator');

  const keys = _.compact(_.split(win.onlyOfficeDocumentKey, ','));

  const dirty = win.onlyOfficeDocumentDirty;
  const record = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
  const isTempFile = !!_.get(record, 'json.session_id');
  const isAttachment = !isTempFile && String(record.get('path')).match(/^\/records\//);
  let loadMask = false;

  if (keys.length) {
    _.each(keys, key => {
      Tine.OnlyOfficeIntegrator.tokenSignOut(key);
    });

    if (dirty) {
      if (isTempFile) {
        loadMask = new Ext.LoadMask(Ext.getBody(), {
          msg: app.i18n._('Saving Document ...'),
          removeMask: true
        });
        loadMask.show();
      }

      const saveResult = await Tine.OnlyOfficeIntegrator.waitForDocumentSave(keys[0], 20);
      const model = isTempFile ? 'Tinebase.TempFile' : isAttachment ? 'Tinebase.Tree_Node' : 'Filemanager.Node';
      window.postal.publish({
        channel: "recordchange",
        topic: model + '.update',
        data: saveResult
      });
    }

    if (loadMask) {
      loadMask.hide();
    }
  }
};

/***/ }),

/***/ 2039:
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _OnlyOfficeTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(674);


__webpack_require__(675);

Promise.all([Tine.Tinebase.appMgr.isInitialised('Tinebase'), Tine.Tinebase.appMgr.isInitialised('Filemanager'), Tine.Tinebase.appMgr.isInitialised('OnlyOfficeIntegrator')]).then(() => {
  const app = Tine.Tinebase.appMgr.get('OnlyOfficeIntegrator');

  const allowOpen = fileNode => {
    return Object(_OnlyOfficeTypes__WEBPACK_IMPORTED_MODULE_0__["getType"])(_.get(fileNode, 'data.name')) && (_.get(fileNode, 'data.type') === 'file' || _.get(fileNode, 'data.tempFile')) && !String(_.get(fileNode, 'data.contenttype')).match(/^vnd\.adobe\.partial-upload.*/) && !+_.get(fileNode, 'data.is_quarantined');
  };

  const actionConfig = {
    app: app,
    allowMultiple: false,
    disabled: true,
    iconCls: 'action_onlyoffice_edit',
    text: app.i18n._('Open Document'),
    handler: function handler() {
      const record = this.selections[0];
      const tempFile = record.get('tempFile');
      const recordData = tempFile ? JSON.stringify(tempFile) : record.toString();
      const win = Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.openWindow({
        recordData: recordData,
        id: record.id
      });
    },
    actionUpdater: function actionUpdater(action, grants, records, isFilterSelect) {
      const fileName = _.get(records, '[0].data.name');

      const type = Object(_OnlyOfficeTypes__WEBPACK_IMPORTED_MODULE_0__["getType"])(fileName);
      const iconCls = Object(_OnlyOfficeTypes__WEBPACK_IMPORTED_MODULE_0__["isEditable"])(fileName) && _.get(grants, 'editGrant') ? 'action_onlyoffice_edit' : type ? 'action_onlyoffice_view' : 'action_onlyoffice';
      const enabled = !isFilterSelect && records && records.length === 1 && allowOpen(records[0]);
      action.setDisabled(!enabled);
      action.baseAction.setDisabled(!enabled); // WTF?

      action.setIconClass(iconCls);
      action.baseAction.setIconClass(iconCls);
    }
  };
  const editDocumentAction = new Ext.Action(_.assign(actionConfig, {
    scope: actionConfig
  })); // filemanager

  Ext.ux.ItemRegistry.registerItem('Filemanager-Node-GridPanel-ContextMenu', editDocumentAction, 2);
  Ext.ux.ItemRegistry.registerItem('Filemanager-Node-GridPanel-ActionToolbar-leftbtngrp', Ext.apply(new Ext.Button(editDocumentAction), {
    scale: 'medium',
    rowspan: 2,
    iconAlign: 'top'
  }), 2); // upload grids

  Ext.ux.ItemRegistry.registerItem('Tinebase-FileUploadGrid-Toolbar', editDocumentAction, 5);
  Ext.ux.ItemRegistry.registerItem('Tinebase-FileUploadGrid-ContextMenu', editDocumentAction, 5); // preview panel

  Ext.ux.ItemRegistry.registerItem('Tine-Filemanager-DocumentPreview', editDocumentAction, 100); // dblclick actions

  Tine.on('filesystem.fileDoubleClick', (fileNode, dblClickHandlers) => {
    const dbClickPreference = Tine.Tinebase.registry.get('preferences').get('fileDblClickAction');

    if (allowOpen(fileNode) && dbClickPreference === 'openwithonlyoffice') {
      dblClickHandlers.push({
        prio: 1000,
        fn: _.bind(editDocumentAction.execute, editDocumentAction)
      });
    }
  });
});

/***/ }),

/***/ 2040:
/***/ (function(module, exports, __webpack_require__) {

var escape = __webpack_require__(108);
exports = module.exports = __webpack_require__(16)(false);
// imports


// module
exports.push([module.i, ".action_onlyoffice_add {\n  background-image: url(" + escape(__webpack_require__(2041)) + ") !important;\n}\n.action_onlyoffice_edit {\n  background-image: url(" + escape(__webpack_require__(2042)) + ") !important;\n}\n.action_onlyoffice_view {\n  background-image: url(" + escape(__webpack_require__(2043)) + ") !important;\n}\n.action_onlyoffice {\n  background-image: url(" + escape(__webpack_require__(2044)) + ") !important;\n}\n", ""]);

// exports


/***/ }),

/***/ 2041:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cstyle type='text/css'%3E .st0%7Bfill:%23020203;%7D %3C/style%3E %3Cg%3E %3Cpath class='st0' d='M19.5,6.4l14.3,5.9l-14.3,5.8L5.1,12.3L19.5,6.4 M19.5,4.2c-0.2,0-0.4,0-0.6,0.1L2.3,11.2 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2c0-0.5-0.4-0.9-0.9-1.1L20.1,4.4C19.9,4.3,19.7,4.2,19.5,4.2L19.5,4.2z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M26.8,25.8v0.6l-6.7,2.7c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L2.2,22.4c-0.5-0.2-0.8-0.7-0.8-1.2 c0-0.5,0.4-0.9,0.9-1.1l5.8-2.4L18.7,22c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l10.6-4.3l4.9,2h-2.9c-0.3,0-0.5,0.1-0.7,0.3 s-0.3,0.4-0.3,0.7v3.7l-0.9,0.4h-3c-0.3,0-0.5,0.1-0.7,0.3C26.9,25.3,26.8,25.5,26.8,25.8z M27.8,30.5c-0.3,0-0.5-0.1-0.7-0.3 c-0.2-0.2-0.3-0.4-0.3-0.7v-1.2L20.2,31c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L8.1,26.7l-5.8,2.4 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l11.6-4.7v-3H27.8z M33.5,22.2v4.3h-4.3c-0.2,0-0.3,0.1-0.4,0.2s-0.2,0.3-0.2,0.4v1.2c0,0.2,0.1,0.3,0.2,0.4s0.3,0.2,0.4,0.2h4.3v4.3 c0,0.2,0.1,0.3,0.2,0.4s0.3,0.2,0.4,0.2h1.2c0.2,0,0.3-0.1,0.4-0.2s0.2-0.3,0.2-0.4v-4.3h4.3c0.2,0,0.3-0.1,0.4-0.2 s0.2-0.3,0.2-0.4V27c0-0.2-0.1-0.3-0.2-0.4s-0.3-0.2-0.4-0.2h-4.3v-4.3c0-0.2-0.1-0.3-0.2-0.4s-0.3-0.2-0.4-0.2h-1.2 c-0.2,0-0.3,0.1-0.4,0.2S33.5,22,33.5,22.2z'/%3E %3C/g%3E %3C/svg%3E\""

/***/ }),

/***/ 2042:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cstyle type='text/css'%3E .st0%7Bfill:%23020203;%7D %3C/style%3E %3Cg%3E %3Cpath class='st0' d='M18.8,6.9l14.3,5.9l-14.3,5.8L4.4,12.8L18.8,6.9 M18.8,4.7c-0.2,0-0.4,0-0.6,0.1L1.6,11.6 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2L18,20.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2L36,13.9 c0.5-0.2,0.8-0.7,0.8-1.2c0-0.5-0.4-0.9-0.9-1.1L19.4,4.8C19.2,4.8,19,4.7,18.8,4.7L18.8,4.7z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M30.6,18.4l-0.5-0.2l-10.6,4.3c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L7.4,18.2l-5.8,2.4 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2L18,29.6c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l1.8-0.7l0.1-2.3L30.6,18.4z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M35.9,29.6L33,28.4l-5.8,5.2l-6.1-0.5l0.1-2.3l-1.7,0.7c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2 L7.4,27.2l-5.8,2.4c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2L18,38.6c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2L36,31.9 c0.5-0.2,0.8-0.7,0.8-1.2C36.8,30.3,36.5,29.8,35.9,29.6z'/%3E %3C/g%3E %3Cpath d='M26.4,30.7l2-1.8L26,26.3l-2,1.8l0,0.9l1.6,0.1l-0.1,1.6L26.4,30.7z M34.1,19.4c-0.2-0.2-0.3-0.2-0.5,0l-6,5.4 c-0.2,0.2-0.2,0.4,0,0.5c0.2,0.2,0.3,0.2,0.5,0l6-5.4C34.2,19.8,34.3,19.6,34.1,19.4z M34,17.1l4.4,4.9L26.9,32.3L22.3,32l0.2-4.7 L34,17.1z M41,19.6L39.5,21L35,16.1l1.6-1.4c0.3-0.3,0.7-0.4,1.1-0.4c0.4,0,0.8,0.2,1.1,0.5l2.3,2.6c0.3,0.3,0.4,0.7,0.4,1.1 C41.5,18.9,41.3,19.3,41,19.6z'/%3E %3C/svg%3E\""

/***/ }),

/***/ 2043:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cstyle type='text/css'%3E .st0%7Bfill:%23020203;%7D .st1%7Bfill:%23FFFFFF;%7D %3C/style%3E %3Cg id='open_office_mixed'%3E %3Cg%3E %3Cpath class='st0' d='M35.8,20.1L30,17.7L19.4,22c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L7.3,17.7l-5.8,2.4 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2C36.7,20.8,36.3,20.3,35.8,20.1z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M35.8,29.1L30,26.7L19.4,31c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L7.3,26.7l-5.8,2.4 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2C36.7,29.8,36.3,29.3,35.8,29.1z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M18.7,6.4L33,12.3l-14.3,5.8L4.3,12.3L18.7,6.4 M18.7,4.3c-0.2,0-0.4,0-0.6,0.1L1.5,11.2 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2c0-0.5-0.4-0.9-0.9-1.1L19.3,4.4C19.1,4.3,18.9,4.3,18.7,4.3L18.7,4.3z'/%3E %3C/g%3E %3C/g%3E %3Cpath class='st1' d='M41.7,13.8l-0.4,0.6c-0.1,0.1,0.2,1.8-1.9,3.5c-2.9,2.3-5.6,3.6-8.6,3.6c-3.1,0-6.6-1.3-9.5-3.6 c-2.2-1.7-3.4-3.4-3.5-3.5l-0.4-0.6l0.4-0.6c0.1-0.1,1.3-1.8,3.5-3.5c2.9-2.3,6-3.8,9.1-3.8c3.1,0,6.2,1.5,9.1,3.8 c2.2,1.7,1.9,3.4,1.9,3.5L41.7,13.8z M37.7,16.1 M26.5,12.3'/%3E %3Cpath d='M41.7,13.8l-0.4,0.5c0,0.1-1.1,1.4-3,2.8c-2.5,1.8-5.2,2.8-7.8,2.8c-2.6,0-5.3-1-7.8-2.8c-1.9-1.4-2.9-2.7-3-2.8l-0.4-0.5 l0.4-0.5c0-0.1,1.1-1.4,3-2.8c2.5-1.8,5.2-2.8,7.8-2.8c2.6,0,5.3,1,7.8,2.8c1.9,1.4,2.9,2.7,3,2.8L41.7,13.8z M30.5,17.7 c2.2,0,4-1.8,4-4c0-2.2-1.8-4-4-4c-1,0-2,0.4-2.7,1.1l-0.6-0.7c-1.2,0.5-2.3,1.1-3.1,1.7c-1,0.7-1.7,1.4-2.1,1.9 c0.4,0.5,1.2,1.2,2.1,1.9c0.8,0.6,1.9,1.2,3.1,1.7c-1-0.9-1.6-2.2-1.6-3.6c0-0.4,0-0.8,0.1-1.1l0.9,0.2c-0.1,0.3-0.1,0.7-0.1,1 C26.6,16,28.4,17.7,30.5,17.7L30.5,17.7z M37,15.7c1-0.7,1.7-1.4,2.1-1.9c-0.4-0.5-1.2-1.2-2.1-1.9c-0.8-0.6-1.9-1.2-3.1-1.7 c1,0.9,1.6,2.2,1.6,3.6c0,1.4-0.6,2.7-1.6,3.6C35.1,16.9,36.2,16.3,37,15.7L37,15.7z M37,15.7 M29.7,13l-1.6-1.8 c0.6-0.6,1.4-0.9,2.2-0.9c1.8,0,3.2,1.4,3.2,3.2c0,1.8-1.4,3.2-3.2,3.2c-1.8,0-3.2-1.4-3.2-3.2c0-0.3,0-0.6,0.1-0.9L29.7,13z M27.3,12.6'/%3E %3C/svg%3E\""

/***/ }),

/***/ 2044:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cstyle type='text/css'%3E .st0%7Bfill:%23020203;%7D %3C/style%3E %3Cg%3E %3Cpath class='st0' d='M39,21.7c0,0.5-0.3,1-0.8,1.2l-16.5,6.7c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L3.7,22.9 c-0.5-0.2-0.8-0.7-0.8-1.2c0-0.5,0.4-0.9,0.9-1.1l5.8-2.4l10.6,4.3c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l10.6-4.3l5.8,2.4 C38.6,20.8,39,21.2,39,21.7z M38.1,29.6l-5.8-2.4l-10.6,4.3c-0.2,0.1-0.5,0.2-0.7,0.2c-0.3,0-0.5,0-0.7-0.2L9.6,27.2l-5.8,2.4 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2C39,30.3,38.6,29.8,38.1,29.6z'/%3E %3C/g%3E %3Cg%3E %3Cpath class='st0' d='M20.9,6.9l14.3,5.9l-14.3,5.8L6.6,12.8L20.9,6.9 M20.9,4.7c-0.2,0-0.4,0-0.6,0.1L3.8,11.6 c-0.5,0.2-0.9,0.6-0.9,1.1c0,0.5,0.3,1,0.8,1.2l16.5,6.7c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.5,0,0.7-0.2l16.5-6.7 c0.5-0.2,0.8-0.7,0.8-1.2c0-0.5-0.4-0.9-0.9-1.1L21.6,4.8C21.4,4.8,21.1,4.7,20.9,4.7L20.9,4.7z'/%3E %3C/g%3E %3C/svg%3E\""

/***/ }),

/***/ 2045:
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(675);

Promise.all([Tine.Tinebase.appMgr.isInitialised('Filemanager'), Tine.Tinebase.appMgr.isInitialised('OnlyOfficeIntegrator')]).then(() => {
  const app = Tine.Tinebase.appMgr.get('OnlyOfficeIntegrator');

  const createDocument = async function createDocument(type) {
    const baseAction = this.ownerCt.ownerCt.baseAction;
    const action = baseAction.initialConfig;
    const filteredContainers = action.filteredContainers;
    const path = await action.getPath(baseAction, type);
    const name = _.isFunction(action.getName) ? await action.getName(baseAction, type) : null;
    const createNewPromise = Tine.OnlyOfficeIntegrator.createNew(type, path, name);
    const win = Tine.OnlyOfficeIntegrator.OnlyOfficeEditDialog.openWindow({
      recordData: JSON.stringify({}),
      id: new Date().getTime(),
      createNewPromise: createNewPromise
    });
    const recordData = await createNewPromise;
    recordData.path = Tine.Filemanager.Model.Node.sanitize(path + '/' + recordData.name);

    if (_.isFunction(action.onNewNode)) {
      action.onNewNode(recordData, baseAction);
    }
  };

  const createDocumentConfig = {
    app: app,
    allowMultiple: true,
    disabled: true,
    iconCls: 'action_onlyoffice_add',
    scope: this,
    text: app.i18n._('Add Document'),
    menu: [{
      text: app.i18n._('Add Text'),
      iconCls: 'mime-type-application-slash-vnd-dot-msword',
      handler: _.partial(createDocument, 'text')
    }, {
      text: app.i18n._('Add Spreadsheet'),
      iconCls: 'mime-type-application-slash-vnd-dot-ms-excel',
      handler: _.partial(createDocument, 'spreadsheet')
    }, {
      text: app.i18n._('Add Presentation'),
      iconCls: 'mime-type-application-slash-vnd-dot-ms-powerpoint',
      handler: _.partial(createDocument, 'presentation')
    }],
    actionUpdater: function actionUpdater(action, grants, records, isFilterSelect, filteredContainers) {
      const recordData = _.get(filteredContainers, '[0]');

      const isAttachment = _.get(recordData, 'container_id.account_grants', false); // hack for attachments where grants are not evaluated


      let enabled = _.get(action, 'isUploadGrid', false);

      if (isAttachment) {
        enabled = _.get(recordData, 'container_id.account_grants.editGrant', false);
      } else if (recordData) {
        var _node$data;

        const node = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
        enabled = Tine.Filemanager.nodeActionsMgr.checkConstraints('create', node, [{
          type: 'file'
        }]) && (node === null || node === void 0 ? void 0 : (_node$data = node.data) === null || _node$data === void 0 ? void 0 : _node$data.path) !== '/shared/';
      }

      action.baseAction.filteredContainers = filteredContainers;
      action.setDisabled(!enabled);
      action.baseAction.setDisabled(!enabled); // WTF?
    }
  }; // filemanager -> path

  const fileManagerCreateDocumentAction = new Ext.Action(Ext.applyIf({
    getPath: async function getPath(baseAction, type) {
      return _.get(baseAction, 'filteredContainers[0].path');
    },
    getName: async function getName(baseAction, type) {
      return new Promise(resolve => {
        const nameMap = {
          'text': ['New Document', '.docx'],
          'spreadsheet': ['New Spreadsheet', '.xlsx'],
          'presentation': ['New Presentation', '.pptx']
        };
        const grid = baseAction.initialConfig.selectionModel.grid.ownerCt.ownerCt;
        const localDocument = new Tine.Filemanager.Model.Node(Tine.Filemanager.Model.Node.getDefaultData({
          name: app.i18n._hidden(nameMap[type][0]) + nameMap[type][1],
          type: 'file',
          account_grants: {
            addGrant: true,
            editGrant: true,
            deleteGrant: true
          }
        }));
        grid.newInlineRecord(localDocument, 'name', async localDocument => {
          const name = String(localDocument.get('name')).replace(new RegExp(nameMap[type][1] + '$'), '');

          const folderPath = _.get(baseAction, 'filteredContainers[0].path');

          localDocument.data.path = "".concat(folderPath).concat(name).concat(nameMap[type][1]);
          resolve(name); // we need to return a promise for gridPanel here as it needs to remove the local record!

          return new Promise(resolve => {
            baseAction.initialConfig.onNewNode = recordData => {
              const remoteDocument = Tine.Tinebase.data.Record.setFromJson(recordData, Tine.Filemanager.Model.Node);
              resolve(remoteDocument);
            };
          });
        });
      });
    }
  }, createDocumentConfig));
  Ext.ux.ItemRegistry.registerItem('Filemanager-Node-GridPanel-ContextMenu', fileManagerCreateDocumentAction, 2);
  Ext.ux.ItemRegistry.registerItem('Filemanager-Node-GridPanel-ActionToolbar-leftbtngrp', Ext.apply(new Ext.Button(fileManagerCreateDocumentAction), {
    scale: 'medium',
    rowspan: 2,
    iconAlign: 'top'
  }), 2); // upload grids -> tempFile

  const uploadGridCreateDocumentAction = new Ext.Action(Ext.applyIf({
    isUploadGrid: true,
    getPath: async function getPath(baseAction) {
      return 'tempFile';
    }
  }, createDocumentConfig));
  Ext.ux.ItemRegistry.registerItem('Tinebase-FileUploadGrid-Toolbar', uploadGridCreateDocumentAction, 5);
  Ext.ux.ItemRegistry.registerItem('Tinebase-FileUploadGrid-ContextMenu', uploadGridCreateDocumentAction, 5); // quickadds -> in personal folder

  const quickAddCreateDocumentAction = new Ext.Action(Ext.applyIf({
    disabled: false,
    getPath: async function getPath(baseAction) {
      return 'personal';
    }
  }, createDocumentConfig));
  Ext.ux.ItemRegistry.registerItem('Tine.widgets.grid.GridPanel.addButton', quickAddCreateDocumentAction);
});

/***/ }),

/***/ 373:
/***/ (function(module, exports, __webpack_require__) {

// node core modules

// 3rd party modules

// local modules
const { responseByMode } = __webpack_require__(374);

const delay = (t, v) => new Promise(resolve => setTimeout(resolve.bind(null, v), t));

/**
 * Execute promise which is provided as a separate function in order to make a clean/original promise call.
 * If there was an error, log it and return rejected promise execution so it might be used again for retrial
 *
 * @param {Object} [logger]     Custom logger that can be provided
 * @returns {function(*=): Promise<T | {rejectedPromise: *, error: any, status: string}>}
 */
const reflectFactory = (logger) =>
  executePromise => {
    if (typeof executePromise === 'function') {
      return executePromise()
        .then(data => ({ data, status: 'resolved' }))
        .catch((error) => {
          logger && logger.error('Reflect promise error: ', error);
          return { error, status: 'rejected', rejectedPromise: executePromise };
        });
    }

    return executePromise.then(data => ({ data, status: 'resolved' }))
      .catch((error) => {
        logger && logger.error('Reflect promise error: ', error);
        return { error, status: 'rejected' };
      });
  };

/**
 * This method resolves all promises without failing, logs an error if there is logger provided and displays a final status of executed promise.
 * It can be "resolved" or "rejected". If "rejected", it also provides a reference to rejected function that returns a promise.
 * The purpose is to continue with the execution of all promises even if some of them were rejected
 *
 * @param {Array} listOfPromises      Array of promises, which are mapped into reflect function
 * @param {Object} [logger]           Custom logger that can be provided
 * @returns {Promise<any[]>}
 */
const reflectAllPromises = (listOfPromises, logger) => {
  const reflect = reflectFactory(logger);
  return Promise.all(listOfPromises.map(reflect));
};

/**
 * This method runs promises in parallel, and collects all rejected promises. Once all rejected promises are collected,
 * the retry mechanism kicks-in and retries rejected promises (also in parallel) until there are no more attempts.
 * If maximum retry attempts is exceeded, the method will return all rejected promises so the caller may try to
 * use different strategy for resolving them.
 *
 * @param {Array} listOfPromises              A list of functions that return a promise
 * @param {Object} retryParams                A configuration object, relevant for retrying mechanism
 * @param {Number} retryParams.maxAttempts    Maximum number of attempts by retry mechanism.
 *                                            If not provided, there will be no retries
 * @param {Number} retryParams.delay          Delay the method execution by certain period of time. The default value
 *                                            is 1000ms
 * @param {Object} [logger]                   Custom logger that can be provided
 * @returns {Promise<any[]>}
 */
const retryAllRejectedPromises = async (listOfPromises, retryParams, logger) => {
  const allPromises = await reflectAllPromises(listOfPromises, logger);

  const rejectedPromises = (allPromises || [])
    .filter(singlePromise => singlePromise.status === 'rejected')
    .map(({ rejectedPromise }) => rejectedPromise);

  if (rejectedPromises && rejectedPromises.length) {
    if (retryParams.maxAttempts > 0) {
      logger && logger.debug('Trying to run [%d] rejected promise(s), attempts left %d',
        rejectedPromises.length, retryParams.maxAttempts);

      retryParams.maxAttempts -= 1;

      await delay(retryParams.delay || 1000);
      return retryAllRejectedPromises(rejectedPromises, retryParams, logger);
    }

    logger && logger.debug('Failed to execute [%d] promise(s)', rejectedPromises.length);
    return rejectedPromises;
  }

  return [];
};

/**
 * This method runs promises in parallel, and collects all rejected promises. Once all rejected promises are collected,
 * the retry mechanism kicks-in and retries rejected promises in NEXT the event loop (also in parallel) until there are no more attempts.
 * This also means that the execution will not stop, and the method will always resolve to true!
 * If maximum retry attempts is exceeded, the method will log an error message about number of rejected promise executions,
 * but it will NOT return rejected promises.
 *
 * @param {Array} listOfPromises              A list of functions that return a promise
 * @param {Object} retryParams                A configuration object, relevant for retrying mechanism
 * @param {Number} retryParams.maxAttempts    Maximum number of attempts by retry mechanism.
 *                                            If not provided, there will be no retries
 * @param {Number} retryParams.delay          Delay the method execution by certain period of time. The default value
 *                                            is 1000ms
 * @param {Object} [logger]                   Custom logger that can be provided
 * @returns {Promise<void>}
 */
const reflectAndRetryAllRejectedPromises = async (listOfPromises, retryParams, logger) => {
  const allPromises = await reflectAllPromises(listOfPromises, logger);

  const rejectedPromises = (allPromises || [])
    .filter(singlePromise => singlePromise.status === 'rejected')
    .map(({ rejectedPromise }) => rejectedPromise);

  if (rejectedPromises && rejectedPromises.length) {
    if (retryParams.maxAttempts > 0) {
      setTimeout(async () => {
        logger && logger.debug('Trying to run [%d] rejected promise(s), attempts left %d',
          rejectedPromises.length, retryParams.maxAttempts);
        retryParams.maxAttempts -= 1;

        await reflectAndRetryAllRejectedPromises(rejectedPromises, retryParams, logger);
      }, retryParams.delay || 1000);
    } else {
      logger && logger.debug('Failed to execute [%d] promise(s)', rejectedPromises.length);
    }
  }
};

/**
 * This method is batching list of promises. The batches are invoked with `reflectAllPromises`, so both resolved
 * and rejected results are kept. Based on `responseMode` you can receive different data.
 *
 * @param {Number} maxBatchSize                   Number of batches to be invoked in parallel
 * @param {Number} delayInMs                      Delay between batch execution
 * @param {(
 * 'ONLY_RESOLVED' | 'ONLY_REJECTED' |
 *  'ALL' | 'ALL_SPLIT'
 * )} responseMode                                Different mode will provide different responses,
 *                                                depending on caller requirements.
 * @returns {Function}
 */
const batchPromises = ({ maxBatchSize = 2, delayInMs = 1000, responseMode = 'ALL' }) =>
  /**
   * @param {Array} promises        List of promises to batch
   * @returns {Promise<any[]>}
   */
  async promises => {
    if (!Object.keys(responseByMode).includes(responseMode)) {
      throw new Error('Invalid responseMode provided');
    }
    let allPromises = [];
    const resolvedPromises = [];
    const rejectedPromises = [];

    // prepare the batches
    const promiseBatches = promises.reduce((result, singlePromise) => {
      if (result[result.length - 1].length < maxBatchSize) {
        result[result.length - 1].push(singlePromise);
        return result;
      }

      result[result.length] = [singlePromise];
      return result;
    }, [[]]);

    for (const promiseBatch of promiseBatches) {
      const executedPromises = await reflectAllPromises(promiseBatch);
      executedPromises.forEach(({ status, data, error }) =>
        status === 'rejected'
          ? rejectedPromises.push(error)
          : resolvedPromises.push(data));

      allPromises.push(...executedPromises);
      await delay(delayInMs);
    }

    allPromises = allPromises.map(({ data, error }) => data || error);
    return responseByMode[responseMode]({ resolvedPromises, rejectedPromises, allPromises });
  };

/**
 * For provided list of promises and `raceTimeoutInMs` method will execute promises in parallel and wait for the
 * response for certain amount of time. After time is out (based on `raceTimeoutInMs`) caller may decide what response
 * to receive. By default method returns only promises that "won" the timeout race. Otherwise it can return all
 * reflected promises including promises that "lost" the race.
 *
 * @param {Array} listOfPromises                  A list of promises to race against time
 * @param {Number} [raceTimeoutInMs]              The time in milliseconds that each promise will race against
 * @param {String} [raceTimeoutMessage]           A custom message provided for promise which "lost" the race
 * @param {(
 * 'ONLY_RESOLVED' | 'ONLY_WINNER_PROMISES' | 'ALL'
 * )} responseMode                                Different mode will provide different responses,
 *                                                depending on caller requirements.
 * @returns {Promise<any[]>}
 */
const racePromisesWithTime = async ({ listOfPromises, raceTimeoutInMs = 1000, raceTimeoutMessage = 'Promise timeout limit reached', responseMode = 'ONLY_WINNER_PROMISES' }) => {
  const allPromises = await reflectAllPromises(listOfPromises.map(promise =>
    Promise.race([promise, delay(raceTimeoutInMs, { timeoutPromise: promise, raceTimeoutMessage })]))
  );

  const resolvedPromises = allPromises
    .map(({ data }) => data)
    .filter(Boolean);

  const winnerPromises = resolvedPromises
    .filter(data => data && !data.timeoutPromise);

  return responseByMode[responseMode]({ resolvedPromises, winnerPromises, allPromises });
};

module.exports = {
  reflectAllPromises,
  retryAllRejectedPromises,
  reflectAndRetryAllRejectedPromises,
  batchPromises,
  racePromisesWithTime,
  delay
};


/***/ }),

/***/ 374:
/***/ (function(module, exports) {

// node core modules

// 3rd party modules

// local modules

const responseByMode = {
  ALL: ({ allPromises }) => allPromises,
  ONLY_RESOLVED: ({ resolvedPromises }) => resolvedPromises,
  ONLY_WINNER_PROMISES: ({ winnerPromises }) => winnerPromises,
  ONLY_REJECTED: ({ rejectedPromises }) => rejectedPromises,
  ALL_SPLIT: ({ resolvedPromises, rejectedPromises }) => [resolvedPromises, rejectedPromises]
};

module.exports = {
  responseByMode
};


/***/ }),

/***/ 40:
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),

/***/ 62:
/***/ (function(module, exports, __webpack_require__) {

/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2019 Metaways Infosystems GmbH (http://www.metaways.de)
 */
__webpack_require__(2037);

__webpack_require__(674);

__webpack_require__(2038);

__webpack_require__(2039);

__webpack_require__(2045);

/***/ }),

/***/ 674:
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getType", function() { return getType; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isEditable", function() { return isEditable; });
/*
 * Tine 2.0
 *
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiß <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2019 Metaways Infosystems GmbH (http://www.metaways.de)
 */
let map = {
  // upstream supported actions
  actions: {
    view: [".pdf", ".djvu", ".xps"],
    edit: [".docx", ".xlsx", ".csv", ".pptx", ".txt"],
    convert: [".docm", ".doc", ".dotx", ".dotm", ".dot", ".odt", ".fodt", ".ott", ".xlsm", ".xls", ".xltx", ".xltm", ".xlt", ".ods", ".fods", ".ots", ".pptm", ".ppt", ".ppsx", ".ppsm", ".pps", ".potx", ".potm", ".pot", ".odp", ".fodp", ".otp", ".rtf", ".mht", ".html", ".htm", ".epub"]
  },
  // upstream supported types
  types: {
    spreadsheet: [".xls", ".xlsx", ".xlsm", ".xlt", ".xltx", ".xltm", ".ods", ".fods", ".ots", ".csv"],
    presentation: [".pps", ".ppsx", ".ppsm", ".ppt", ".pptx", ".pptm", ".pot", ".potx", ".potm", ".odp", ".fodp", ".otp"],
    text: [".doc", ".docx", ".docm", ".dot", ".dotx", ".dotm", ".odt", ".fodt", ".ott", ".rtf", ".txt", ".html", ".htm", ".mht", ".pdf", ".djvu", ".fb2", ".epub", ".xps"]
  }
};

const getType = function getType(name) {
  let type = null;
  const ext = ('.' + String(name).split('.').pop()).toLowerCase();

  _.each(map.types, (val, key) => {
    if (_.indexOf(val, ext) !== -1) {
      type = key;
      return false;
    }
  });

  return type;
};

const isEditable = function isEditable(name) {
  const hasType = getType(name);
  const ext = ('.' + String(name).split('.').pop()).toLowerCase();
  return hasType && _.indexOf(map.actions.view, ext) < 0;
};



/***/ }),

/***/ 675:
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__(2040);
if(typeof content === 'string') content = [[module.i, content, '']];
// Prepare cssTransformation
var transform;

var options = {"hmr":true}
options.transform = transform
// add the styles to the DOM
var update = __webpack_require__(17)(content, options);
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {}

/***/ })

}]);