@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,308 lines (1,194 loc) • 84.4 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"./PluginBase",
"sap/ui/core/Element",
"sap/base/Log",
"sap/ui/core/Lib",
"sap/ui/unified/FileUploader",
"sap/m/upload/UploaderHttpRequestMethod",
"sap/m/upload/UploadItem",
"sap/base/util/deepEqual",
"sap/m/library",
"sap/m/IllustratedMessageType",
"sap/m/IllustratedMessage",
"sap/m/IllustratedMessageSize",
"sap/m/upload/UploaderTableItem",
"sap/ui/core/dnd/DragDropInfo",
"sap/ui/core/dnd/DropInfo",
"sap/m/upload/FilePreviewDialog",
"sap/ui/base/Event",
"sap/m/Dialog",
"sap/m/Label",
"sap/m/Input",
"sap/m/MessageBox",
"sap/m/Button",
"sap/ui/core/Core",
"sap/m/Text",
"sap/m/VBox",
"sap/ui/model/BindingMode"
], function (PluginBase, Element, Log, Library1, FileUploader, UploaderHttpRequestMethod, UploadItem, deepEqual, Library, IllustratedMessageType, IllustratedMessage, IllustratedMessageSize, Uploader, DragDropInfo, DropInfo, FilePreviewDialog, EventBase, Dialog, Label, Input, MessageBox, Button, Core, TextField, VBox, BindingMode) {
"use strict";
/**
* Constructor for a new UploadSetwithTable plugin.
*
* @param {string} [sId] ID for the new <code>UploadSetwithTable</code>, generated automatically if no id is given
* @param {object} [mSettings] Initial settings for the new <code>UploadSetwithTable</code>
*
* @class
* The <code>UploadSetwithTable</code> plugin enables uploading within a table when it is added as a dependent to the table control.
* <br> This plugin provides an Upload button to upload files from the local file system. To do so, the user of the plugin must place {@link sap.m.upload.ActionsPlaceholder Actions Placeholder} control at the desired table area and then associate the actions of the plugin with the placeholder.
* <br> The plugin provides the ability to upload files from the local system as well as from cloud file picker and includes other notable features such as validation, file preview, download.
*
* <br> The following controls support this plugin:
* <ul>
* <li>{@link sap.ui.mdc.Table MDC Table}</li>
* <li>{@link sap.m.Table Responsive Table}</li>
* <li>{@link sap.ui.table.Table Grid Table}</li>
* <li>{@link sap.ui.table.TreeTable Tree Table}</li>
* </ul>
*
* <caption>Consider the following before using the plugin: </caption>
* <ul>
* <li>It gets activated when it is added as a dependent to the table control. It gets deactivated when it is removed from the table control or when the table control is destroyed.</li>
* <li>It fires onActivated and onDeactivated events when it is activated and deactivated, respectively.</li>
* <li>Configuring the rowConfiguration aggregation (type {@link sap.m.upload.UploadItemConfiguration UploadItemConfiguration}) of this plugin is mandatory to use the features such as file preview, download etc.</li>
* <li>For the plugin to work with the tree table control, the isDirectoryPath property of the rowConfiguration aggregation must be set. This indicates if the context of the row is a directory or a file. It helps the plugin with the file preview feature.</li>
* <li>It works only with the table control when the table is bound to the model to perform the operations such as rename, download etc.</li>
* </ul>
*
* @example <caption>Connecting the plugin to table control</caption>
* <pre>
* oTable.addDependent(new UploadSetwithTable({
* uploadUrl: "uploadUrl",
* fileTypes: ["jpg", "jpeg", "png"],
* actions: "uploadButton" // Associating the actions with the ID of the ActionPlaceholder control in the table, where the upload button should be rendered.
* }));
*
* // example of sap.m.upload.ActionsPlaceholder control placed in the table toolbar for rendering the upload button.
* new ActionPlaceHolder({
* id: "uploadButton", // ID of the ActionPlaceholder control to be associated with the plugin actions.
* placeholderFor: UploadSetwithTableActionPlaceHolder.UploadButtonPlaceholder
* });
* </pre>
*
* @extends sap.ui.core.Element
* @version 1.146.0
* @author SAP SE
* @public
* @since 1.124
* @alias sap.m.plugins.UploadSetwithTable
* @borrows sap.m.plugins.PluginBase.findOn as findOn
*/
var UploadSetwithTable = PluginBase.extend("sap.m.plugins.UploadSetwithTable", /** @lends sap.m.plugins.UploadSetwithTable.prototype */ {
metadata: {
library: "sap.m",
properties: {
/**
* File types that are allowed to be uploaded.
* <br>If this property is not set, any file can be uploaded.
*/
fileTypes: {type: "string[]", defaultValue: null},
/**
* Defined maximum length for a name of files that are to be uploaded.
* <br>If set to <code>null</code> or <code>0</code>, any file can be uploaded regardless length of its name.
*/
maxFileNameLength: {type: "int", defaultValue: null},
/**
* Defined size limit in megabytes for files that are to be uploaded.
* <br>If set to <code>null</code> or <code>0</code>, files of any size can be uploaded.
*/
maxFileSize: {type: "float", defaultValue: null},
/**
* Media types of files that are allowed to be uploaded.
* <br>If this property is not set, any file can be uploaded.
*/
mediaTypes: {type: "string[]", defaultValue: null},
/**
* Url where the uploaded files are stored.
*/
uploadUrl: {type: "string", defaultValue: null},
/**
* HTTP request method chosen for file upload.
*/
httpRequestMethod: {type: "sap.m.upload.UploaderHttpRequestMethod", defaultValue: UploaderHttpRequestMethod.Post},
/**
* Lets the user select multiple files from the same folder and then upload them.
*
* If multiple property is set to false, the plugin shows an error message if more than one file is chosen for drag & drop.
*/
multiple: {type: "boolean", group: "Behavior", defaultValue: false},
/**
* If set to true, the button used for uploading files becomes invisible.
*/
uploadButtonInvisible: {type: "boolean", group: "Appearance", defaultValue: false},
/**
* Defines whether the upload action is allowed.
*/
uploadEnabled: {type: "boolean", defaultValue: true},
/** Callback function to perform additional validations or configurations for the item queued up for upload and to finally trigger the upload.
* @callback sap.m.plugins.UploadSetwithTable.itemValidationHandler
* @param {sap.m.plugins.UploadSetwithTable.ItemInfo} oItemInfo The info of the item queued for upload.
* @returns {Promise<sap.m.upload.UploadItem>} oPromise, once resolved the UploadSetWithTable plugin initiates the upload.
* @public
**/
/**
* @typedef {object} sap.m.plugins.UploadSetwithTable.ItemInfo
* @description Item info object sent as paramter to {@link sap.m.plugins.UploadSetwithTable.itemValidationHandler itemValidationHandler callback}
* @property {sap.m.upload.UploadItem} oItem Current item queued for upload.
* @property {number} iTotalItemsForUpload Total count of items queued for upload.
* @property {sap.m.plugins.UploadSetwithTable} oSource Source on which the callback was invoked.
* @public
**/
/**
* Defines a {@link sap.m.plugins.UploadSetwithTable.itemValidationHandler callback function} that is invoked when each UploadItem is queued up for upload.
* This callback is invoked with {@link sap.m.plugins.UploadSetwithTable.ItemInfo parameters} and the callback is expected to return a promise to the plugin. Once the promise is resolved, the plugin initiates the upload process.
* Configure this property only when any additional configuration or validations are to be performed before the upload of each item.
* The upload process is triggered manually by resolving the promise returned to the plugin.
**/
itemValidationHandler: {type: "function", defaultValue: null},
/**
* Lets the user upload entire files from directories and sub directories.
*/
directory: {type: "boolean", group: "Behavior", defaultValue: false},
/**
* Enables CloudFile picker feature to upload files from cloud.
*/
cloudFilePickerEnabled: { type: "boolean", group: "Behavior", defaultValue: false },
/**
* Url of the FileShare OData V4 service supplied for CloudFile picker control.
*/
cloudFilePickerServiceUrl: { type: "sap.ui.core.URI", group: "Data", defaultValue: "" },
/**
* The text of the CloudFile picker button. The default text is "Upload from cloud" (translated to the respective language).
*/
cloudFilePickerButtonText: { type: 'string', defaultValue: "" },
/**
* @typedef {object} sap.m.plugins.UploadSetwithTable.FilenameValidationConfigMode
* @description Key property of {@link sap.m.plugins.UploadSetwithTable.FilenameValidationConfig FileNameValidationConfig}. Used to determine the mode for file name validation.
* @property {string} mode - The file name validation mode. The allowed values are 'include', 'exclude', or 'override'.
* <br> <br> If the mode is 'include', the specified characters are added to the default restricted character set.
* <br> If the mode is 'exclude', the specified characters are excluded from the default resrtricted character set.
* <br> If the mode is 'override', the specified characters replace the entire default restricted character set.
* <br> If the mode is not set, the default restricted file name character set is used.
* @public
* @since 1.136
*/
/**
* @typedef {object} sap.m.plugins.UploadSetwithTable.FilenameValidationConfig An object type that represents the file name validation configuration.
* @description This property type is used to define the file name validation configuration. Object is passed to {@link sap.m.plugins.UploadSetwithTable fileNameValidationConfig property}
* @property {sap.m.plugins.UploadSetwithTable.FilenameValidationConfigMode} mode The file name validation config mode.
* @property {string} characters The file name validation configuration characters.
* <br> <br> The default restricted filename character set is: \:/*?"<>|[]{}@#$
* @public
* @since 1.136
**/
/**
* File name validation configuration.
* <br> Set this property to configure the file name validation characters and the validation mode.
* <br> This configuration is used to validate the file name when a file is selected for renaming.
* <br> For the plugin to pick up this configuration, mode and characters of the property must be set to validate the file name.
* <br> see {@link sap.m.plugins.UploadSetwithTable.FilenameValidationConfigMode mode} to configure the file name validation mode.
* <br> <br> The default restricted filename character set is: \:/*?"<>|[]{}@#$
* @type {sap.m.plugins.UploadSetwithTable.FilenameValidationConfig}
* @public
* @since 1.136
*/
fileNameValidationConfig: { type: 'object', defaultValue: null }
},
aggregations: {
/**
* Defines the uploader to be used. If not specified, the default implementation is used.
*/
uploader: {type: "sap.m.upload.UploaderTableItem", multiple: false},
/**
* Header fields to be included in the header section of an XHR request.
*/
headerFields: {type: "sap.ui.core.Item", multiple: true, singularName: "headerField"},
/**
* Row configuration information for each uploadItem in the model.
*/
rowConfiguration: {type: "sap.m.upload.UploadItemConfiguration", multiple: false},
/**
* An illustrated message is displayed when no data is loaded.
*/
noDataIllustration: { type: "sap.m.IllustratedMessage", multiple: false }
},
defaultAggregation: "rowConfiguration",
associations: {
/**
* Dialog with a carousel to preview files uploaded.
* <br>If it is not defined, the plugin creates and uses the instance of {@link sap.m.upload.FilePreviewDialog FilePreviewDialog}.
*/
previewDialog: {type: "sap.m.upload.FilePreviewDialog", multiple: false},
/**
* Actions provided by the plugin.
* <br> {@link sap.m.UploadSetwithTableActionPlaceHolder UploadSetwithTableActionPlaceHolder} enum is used to determine the action control to be rendered.
* <br> Action buttons are rendered instead of the placeholder.
* <br> For example, if the "placeholderFor" property is set to UploadButtonPlaceholder, the Upload button is rendered.
* <br> Note: The action buttons are rendered only when the association to the placeholder control is set.
*/
actions: {type: "sap.m.upload.ActionsPlaceholder", multiple: true}
},
events: {
/**
* The event is triggered when the file name is changed.
*/
itemRenamed: {
parameters: {
/**
* The renamed UI element is of UploadItem type.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* The event is triggered when the file renaming process is canceled.
* @since 1.142
*/
itemRenameCanceled: {
parameters: {
/**
* The renamed UI element is of UploadItem type.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired right before the upload process begins.
*/
beforeUploadStarts: {
parameters: {
/**
* The file whose upload is just about to start.
*/
item: {type: "sap.m.upload.UploadItem"}
},
allowPreventDefault: true
},
/**
* This event is fired right after the upload process is finished.
* <br>Based on the backend response of the application, listeners can use the parameters to determine if the upload was successful or if it failed.
*/
uploadCompleted: {
parameters: {
/**
* The file whose upload has just been completed.
*/
item: {type: "sap.m.upload.UploadItem"},
/**
* Response message that comes from the server.
*
* On the server side this response has to be put within the "body" tags of the response
* document of the iFrame. It can consist of a return code and an optional message. This does not
* work in cross-domain scenarios.
*/
response : {type : "string"},
/**
* ReadyState of the XHR request.
*
* Required for receiving a <code>readyState</code> is to set the property <code>sendXHR</code>
* to true. This property is not supported by Internet Explorer 9.
*/
readyState : {type : "string"},
/**
* Status of the XHR request.
*
* Required for receiving a <code>status</code> is to set the property <code>sendXHR</code> to true.
* This property is not supported by Internet Explorer 9.
*/
status : {type : "string"},
/**
* Http-Response which comes from the server.
*
* Required for receiving <code>responseXML</code> is to set the property <code>sendXHR</code> to true.
*
* This property is not supported by Internet Explorer 9.
*/
responseXML : {type : "string"},
/**
* Http-Response which comes from the server.
*
* Required for receiving <code>responseText</code> is to set the property <code>sendXHR</code> to true.
*
* This property is not supported by Internet Explorer 9.
*/
responseText : {type : "string"},
/**
* Http-Response-Headers which come from the server.
*
* Provided as a JSON-map, i.e. each header-field is reflected by a property in the <code>headers</code>
* object, with the property value reflecting the header-field's content.
*
* Required for receiving <code>headers</code> is to set the property <code>sendXHR</code> to true.
* This property is not supported by Internet Explorer 9.
*/
headers : {type : "object"}
}
},
/**
* This event is fired in either of the following cases:
* <ul>
* <li>When a file that is selected to be uploaded fails to meet the file type restriction
* (<code>fileType</code> property).</li>
* <li>When the file type restriction changes, and the file to be uploaded fails to meet the new
* restriction.</li>
* </ul>
*/
fileTypeMismatch: {
parameters: {
/**
* The file that fails to meet the file type restriction specified in the
* <code>fileType</code> property.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired in either of the following cases:
* <ul>
* <li>When a file that is selected to be uploaded fails to meet the file name length restriction specified in the
* <code>maxFileNameLength</code> property.</li>
* <li>When the file name length restriction changes, and the file to be uploaded fails to meet the new
* restriction.</li>
* </ul>
*/
fileNameLengthExceeded: {
parameters: {
/**
* The file that fails to meet the file name length restriction specified in the
* <code>maxFileNameLength</code> property.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired in either of the following cases:
* <ul>
* <li>When a file that is selected to be uploaded fails to meet the file size restriction specified in the
* <code>maxFileSize</code> property.</li>
* <li>When the file size restriction changes, and the file to be uploaded fails to meet the new
* restriction.</li>
* </ul>
*/
fileSizeExceeded: {
parameters: {
/**
* The file that fails to meet the file size restriction specified in the
* <code>maxFileSize</code> property.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired in either of the following cases:
* <ul>
* <li>When a file that is selected to be uploaded fails to meet the media type restriction specified in the
* <code>mediaTypes</code> property.</li>
* <li>When the media type restriction changes, and the file to be uploaded fails to meet the new
* restriction.</li>
* </ul>
*/
mediaTypeMismatch: {
parameters: {
/**
* The file that fails to meet the media type restriction specified in the
* <code>mediaTypes</code> property.
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired just before initiating the file upload process when a file is selected to be uploaded.
* Use this event to set additional info dynamically, specific for each item before upload process is initiated.
*/
beforeInitiatingItemUpload: {
parameters: {
/**
* Items in ready state for upload process
*/
item: {type: "sap.m.upload.UploadItem"}
}
},
/**
* This event is fired when plugin is activated.
*/
onActivated: {
parameters: {
/**
* The activated plugin instance.
*/
oPlugin: {type: "sap.m.plugins.UploadSetwithTable"}
}
},
/**
* This event is fired when plugin is deactivated.
*/
onDeactivated: {
parameters: {
/**
* Control to which the plugin was connected.
*/
control: {type: "sap.ui.core.Control"}
}
}
}
}
});
var UploadState = Library.UploadState;
var UploadSetwithTableActionPlaceHolder = Library.UploadSetwithTableActionPlaceHolder;
UploadSetwithTable.findOn = PluginBase.findOn;
UploadSetwithTable.DEFAULT_INVALID_FILENAME_CHARACTERSET = "\\:/*?\"<>|[]{}@#$";
/**
* Event Delegate that containts events, that need to be executed after control events.
*/
const EventDelegate = {
onAfterRendering: function() {
this.getConfig("setIsTableBound", this.getControl());
this.getConfig("setModelName", this.getControl());
}
};
UploadSetwithTable.prototype.init = function () {
this._oRb = Library1.getResourceBundleFor("sap.m");
setPluginBaseConfigs(); // Set PluginBase configurations
};
UploadSetwithTable.prototype.onActivate = function (oControl) {
this._filesTobeUploaded = [];
this._filePreviewDialogControl = null;
oControl.addDelegate(EventDelegate, false, this);
this.getConfig("setPluginDefaultSettings", this.getControl(), this);
this._setActions();
this.fireOnActivated({oPlugin: this});
};
UploadSetwithTable.prototype.onDeactivate = function (oControl) {
this.getConfig("cleanupPluginInstanceSettings", oControl, this);
this.fireOnDeactivated({control: oControl});
};
// Overriden Setter methods
UploadSetwithTable.prototype.setFileTypes = function (aNewTypes) {
var aTypes = aNewTypes || null;
if (typeof aTypes === "string") {
aTypes = aTypes.split(",");
}
aTypes = (aTypes || []).map(function (s) {
return s ? s.toLowerCase() : "";
});
if (!deepEqual(this.getFileTypes(), aTypes)) {
this.setProperty("fileTypes", aTypes, true);
this.getDefaultFileUploader().setFileType(aTypes);
}
return this;
};
UploadSetwithTable.prototype.setMaxFileNameLength = function (iNewMax) {
if (this.getMaxFileNameLength() !== iNewMax) {
this.setProperty("maxFileNameLength", iNewMax, true);
this.getDefaultFileUploader().setMaximumFilenameLength(iNewMax);
}
return this;
};
UploadSetwithTable.prototype.setMaxFileSize = function (iNewMax) {
if (this.getMaxFileSize() !== iNewMax) {
this.setProperty("maxFileSize", iNewMax, true);
this.getDefaultFileUploader().setMaximumFileSize(iNewMax);
}
return this;
};
UploadSetwithTable.prototype.setMediaTypes = function (aNewTypes) {
var aTypes = aNewTypes || null;
if (typeof aTypes === "string") {
aTypes = aTypes.split(",");
}
aTypes = (aTypes || []).map(function (s) {
return s ? s.toLowerCase() : "";
});
if (!deepEqual(this.getMediaTypes(), aTypes)) {
this.setProperty("mediaTypes", aTypes, true);
this.getDefaultFileUploader().setMimeType(aTypes);
}
return this;
};
UploadSetwithTable.prototype.setUploadButtonInvisible = function (bUploadButtonInvisible) {
if (bUploadButtonInvisible !== this.getUploadButtonInvisible()) {
var bVisible = !bUploadButtonInvisible;
this.getDefaultFileUploader().setVisible(bVisible);
this.setProperty("uploadButtonInvisible", bUploadButtonInvisible, true);
}
return this;
};
UploadSetwithTable.prototype.setMultiple = function (bMultiple) {
if (this.getMultiple() !== bMultiple) {
this.setProperty("multiple", bMultiple);
this.getDefaultFileUploader().setMultiple(bMultiple);
}
return this;
};
UploadSetwithTable.prototype.setUploadEnabled = function (bEnable) {
if (bEnable !== this.getUploadEnabled()) {
this.getDefaultFileUploader().setEnabled(bEnable);
if (bEnable && !this._oDragDropConfig) {
this.getConfig("setDragDropConfig", this.getControl(), this);
} else if (!bEnable) {
this.getConfig("resetDragDropConfig", this.getControl(), this);
}
this.setProperty("uploadEnabled", bEnable, false);
}
return this;
};
UploadSetwithTable.prototype.setDirectory = function (bDirectory) {
if (this.getDirectory() !== bDirectory) {
this.setProperty("directory", bDirectory);
this.getDefaultFileUploader().setDirectory(bDirectory);
if (bDirectory) {
this.setProperty("multiple", false); // disable multiple files selection when directory selection is enabled.
}
}
return this;
};
UploadSetwithTable.prototype.setNoDataIllustration = function(oNoDatIllustration) {
this._vNoDataIllustration = oNoDatIllustration;
this.getConfig("setDefaultIllustrations", this.getControl(), this);
return this;
};
UploadSetwithTable.prototype.getNoDataIllustration = function() {
return this._vNoDataIllustration;
};
UploadSetwithTable.prototype.setFileNameValidationConfig = function (oConfig) {
// set property to null if no config is passed
if (oConfig === undefined || oConfig === null) {
this.setProperty("fileNameValidationConfig", null);
return this;
}
// Validate that it's an object
if (typeof oConfig !== 'object') {
throw new Error("fileNameValidationConfig must be a non-null object with mode and characters properties");
}
// Extract keys from the object
const keys = Object.keys(oConfig);
const expectedKeys = ['mode', 'characters'];
// Check for exact match in keys (no more, no less)
const missingKeys = expectedKeys.filter((key) => !keys.includes(key));
const extraKeys = keys.filter((key) => !expectedKeys.includes(key));
if (missingKeys.length > 0 || extraKeys.length > 0) {
throw new Error(
`fileNameValidationConfig must contain only the following properties: ${expectedKeys.join(', ')}. ` +
(missingKeys.length > 0 ? `Missing: ${missingKeys.join(', ')}. ` : '') +
(extraKeys.length > 0 ? `Unexpected: ${extraKeys.join(', ')}.` : '')
);
}
// Validate `mode` is a string and one of the allowed values
const validModes = ['include', 'exclude', 'override'];
if (typeof oConfig.mode !== 'string' || !validModes.includes(oConfig.mode)) {
throw new Error(`fileNameValidationConfig.mode must be one of ${validModes.join(', ')}`);
}
// Validate `characters` is a string
if (typeof oConfig.characters !== 'string') {
throw new Error("fileNameValidationConfig.characters must be a string.");
}
// Call the default property setter (important!)
this.setProperty("fileNameValidationConfig", oConfig);
return this;
};
// Public API's
/**
* Returns an instance of the default <code>sap.ui.unified.FileUploader</code> icon/button, used for adding files
* from the open file dialog of the operating system. It can be customized, for example made invisible or assigned a different icon.
* @return {sap.ui.unified.FileUploader} Instance of the default <code>sap.ui.unified.FileUploader</code>.
* @public
*/
UploadSetwithTable.prototype.getDefaultFileUploader = function () {
var sTooltip = this._oRb.getText("UPLOADSET_WITH_TABLE_UPLOADBUTTON_TEXT");
if (!this._oFileUploader) {
this._oFileUploader = new FileUploader(this.getId() + "-uploader", {
buttonOnly: true,
buttonText: sTooltip,
tooltip: sTooltip,
iconOnly: false,
enabled: this.getUploadEnabled(),
icon: "",
iconFirst: false,
style: "Transparent",
name: "UploadSetwithTableFileUploader",
sameFilenameAllowed: true,
fileType: this.getFileTypes(),
mimeType: this.getMediaTypes(),
maximumFilenameLength: this.getMaxFileNameLength(),
maximumFileSize: this.getMaxFileSize(),
multiple: this.getDirectory() ? false : this.getMultiple(),
useMultipart: false,
sendXHR: true,
change: [this._onFileUploaderChange, this],
typeMissmatch: [this._fireFileTypeMismatch, this],
fileSizeExceed: [this._fireFileSizeExceed, this],
filenameLengthExceed: [this._fireFilenameLengthExceed, this],
visible: !this.getUploadButtonInvisible(),
directory: this.getDirectory()
});
}
return this._oFileUploader;
};
/**
* Returns sap icon based on the passed mediaType and filename
* @param {string} mediaType The media type of the selected file
* @param {string} fileName The name of the selected file
* @public
* @returns {string} sap icon.
*/
UploadSetwithTable.getIconForFileType = function (mediaType, fileName) {
return UploadItem._getIconByMimeType(mediaType, fileName);
};
/**
* Attaches all necessary handlers to the given uploader instance, so that the progress and status of the upload can be
* displayed and monitored.
* This is necessary in case when custom uploader is used.
* @param {sap.m.upload.UploaderTableItem} oUploader Instance of <code>sap.m.upload.UploaderTableItem</code> to which the default request handlers are attached.
* @public
*/
UploadSetwithTable.prototype.registerUploaderEvents = function (oUploader) {
oUploader.attachUploadStarted(this._onUploadStarted.bind(this));
oUploader.attachUploadCompleted(this._onUploadCompleted.bind(this));
oUploader.attachUploadProgressed(this._onUploadProgressed.bind(this));
oUploader.attachUploadTerminated(this._onUploadAborted.bind(this));
};
/**
* Invokes native files selection handler.
* @public
*/
UploadSetwithTable.prototype.fileSelectionHandler = function() {
var oUploaderInstance = this.getDefaultFileUploader();
if (oUploaderInstance && oUploaderInstance.oFileUpload && oUploaderInstance.oFileUpload.click) {
oUploaderInstance.oFileUpload.click();
}
};
/**
* API to determine the unit for file size in KB/MB/GB.
* API recommended for file size formatting purpose.
* @param {int} iFileSize fileSize to determine units
* @public
* @returns {string} sFileSizeWithUnit file size in KB/MB/GB default unit is KB
*/
UploadSetwithTable.getFileSizeWithUnits = function(iFileSize) {
var iKilobyte = 1024;
var iMegabyte = iKilobyte * 1024;
var iGigabyte = iMegabyte * 1024;
if (typeof iFileSize === "number") {
if (iFileSize < iMegabyte) {
return (iFileSize / iKilobyte).toFixed(2) + " KB";
} else if (iFileSize < iGigabyte) {
return (iFileSize / iMegabyte).toFixed(2) + " MB";
} else {
return (iFileSize / iGigabyte).toFixed(2) + " GB";
}
}
return iFileSize;
};
/**
* API to upload file using URL
* @param {string} sName file name to be set for the file that is to be uploaded.
* @param {string} sUrl Url for the file.
* @param {Promise} oPromise Promise when resolved, the control initiates the upload process.
* @returns {sap.m.upload.UploadItem} oItem, UploadItem instance created with the file object.
* @public
*/
UploadSetwithTable.prototype.uploadItemViaUrl = function (sName, sUrl, oPromise) {
var oFileObject = new File([new Blob([])], sName);
var oItem = new UploadItem({
uploadState: UploadState.Ready
});
oItem._setFileObject(oFileObject);
oItem.setFileName(oFileObject.name);
oItem.setUrl(sUrl);
oPromise
.then(() => this._initateItemUpload(oItem).bind(this))
.catch(() => oItem.destroy()); // cancelling the upload.
return oItem;
};
/**
* API to upload Item without file
* @param {Promise} oPromise Promise when resolved, control initiates the upload process.
* @return {sap.m.upload.UploadItem} oItem, UploadItem instance created with the file object.
* @public
*/
UploadSetwithTable.prototype.uploadItemWithoutFile = function (oPromise) {
var oFileObject = new File([new Blob([])], '-');
var oItem = new UploadItem({
uploadState: UploadState.Ready
});
oItem._setFileObject(oFileObject);
oItem.setFileName(oFileObject.name);
oPromise
.then(() => this._initateItemUpload(oItem))
.catch(() => oItem.destroy()); // cancelling the upload.
return oItem;
};
/**
* Previews file.
* @param {sap.ui.model.Context} oBindingContext Context of the row containing the file to be previewed.
* @public
*/
UploadSetwithTable.prototype.openFilePreview = function (oBindingContext) {
const oRowConfiguration = this.getRowConfiguration();
if (!oRowConfiguration) {
Log.error("Row configuration is not set for the plugin. File preview is not possible.");
return;
}
this.getConfig("openFilePreview", oBindingContext, this.getControl(), this);
};
/**
* Downloads the file. Only possible when the context passed has a valid URL specified.
* @param {sap.ui.model.Context} oBindingContext Context of the item to be downloaded.
* @param {boolean} bAskForLocation Whether to ask for a location where to download the file or not.
* @public
*/
UploadSetwithTable.prototype.download = function (oBindingContext, bAskForLocation) {
const oRowConfiguration = this.getRowConfiguration();
if (!oRowConfiguration) {
Log.error("Row configuration is not set for the plugin. Download is not possible.");
return;
}
this.getConfig("download", {
oBindingContext: oBindingContext,
bAskForLocation: bAskForLocation
}, this, this.getControl());
};
/**
* API to rename the document of an item.
* @param {sap.ui.model.Context} oBindingContext Context of the item to be renamed.
* @public
*/
UploadSetwithTable.prototype.renameItem = async function (oBindingContext) {
const oRowConfiguration = this.getRowConfiguration();
if (!oRowConfiguration) {
Log.error("Row configuration is not set for the plugin. Rename action is not possible.");
return;
}
if (oBindingContext) {
const oItem = await this.getItemForContext(oBindingContext, true);
const oDialog = this._getFileRenameDialog(oItem);
oDialog.open();
}
};
// Private API's
/**
* Internal API return the dialog for document rename.
* @param {sap.m.upload.UploadItem} oItem item to be renamed.
* @private
* @returns {sap.m.Dialog} oDialog, created dialog instance
*/
UploadSetwithTable.prototype._getFileRenameDialog = function(oItem) {
const oSplit = UploadItem._splitFileName(oItem.getFileName());
let iMaxLength = this.getMaxFileNameLength();
const iFileExtensionLength = oSplit.extension ? oSplit.extension.length + 1 : 0;
iMaxLength = iMaxLength ? iMaxLength : 0;
let iNameMaxLength = iMaxLength - iFileExtensionLength;
iNameMaxLength = iNameMaxLength < 0 ? 0 : iNameMaxLength;
// Input field
const oInput = new Input({
type: Library.InputType.Text,
value: oSplit.name,
width: "75%",
maxLength: iNameMaxLength,
liveChange: [this._handleItemNameValidation, this]
});
oInput.addStyleClass("sapUiTinyMarginTop");
oInput.addStyleClass("sapUiMediumMarginBegin");
// Test field for extension
const sExtension = oSplit.extension ? `.${oSplit.extension}` : "";
const oTextField = new TextField({
text: sExtension
});
oTextField.addStyleClass("sapUiTinyMarginBegin");
oTextField.addStyleClass("sapUiTinyMarginTop");
oInput.addAriaDescribedBy(oTextField.getId());
// Label for Input
const oLabel = new Label({
text: this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_INPUT_LABEL"),
labelFor: oInput.getId(),
required: true
});
oLabel.addStyleClass("sapUiMediumMarginTop");
oLabel.addStyleClass("sapUiMediumMarginBegin");
oLabel.addStyleClass("sapUiSmallMarginEnd");
const oVBox = new VBox({
items: [oLabel]
});
// Dialog creation
var oDialog = new Dialog({
title: this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_DIALOG_TEXT"),
contentWidth: "33.375rem",
contentHeight: "10.125rem",
content: [oVBox, oInput, oTextField],
beginButton: new Button({
type: Library.ButtonType.Emphasized,
text: this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_APPLY_BUTTON_TEXT"),
press: this._handleItemRenameConfirmation.bind(this),
enabled: oInput.getValueState() !== "Error"
}),
endButton: new Button({
text: this._oRb.getText("UPLOADSET_WITH_TABLE_CANCELBUTTON_TEXT"),
press: this._handleItemRenameCancel.bind(this)
}),
customData: {
key: "item",
value: oItem
},
afterClose: function () {
oDialog.destroy();
},
escapeHandler: (oPromise) => {
oDialog.close();
this.fireItemRenameCanceled({item: oItem});
oPromise?.reject();
}
});
return oDialog;
};
/**
* Handler for item rename cancel operation.
* @param {object} oEvent cancel button click event.
* @private
*/
UploadSetwithTable.prototype._handleItemRenameCancel = function(oEvent) {
const oDialog = oEvent.getSource().getParent();
const oInput = oDialog.getContent()[1];
const oItem = oDialog && oDialog.data ? oDialog.data().item : null;
const oSplit = UploadItem._splitFileName(oItem.getFileName());
// Check if there are changes made to the existing file name.
if (oItem && oInput && oSplit.name !== oInput.getValue()) {
MessageBox.warning(this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_DISCARD_POPUP_CHANGES_TEXT"), {
actions: [this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_SAVE_BUTTON_TEXT"), this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_DISCARD_CHANGES_BUTTON_TEXT")],
emphasizedAction: this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_SAVE_BUTTON_TEXT"),
onClose: (sAction) => {
if (sAction !== this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_SAVE_BUTTON_TEXT")) {
oDialog.close();
this.fireItemRenameCanceled({item: oItem});
} else {
// fire beginbutton event to save the filename
var oBeginButton = oDialog.getBeginButton();
var oEvent = new EventBase("click", oBeginButton);
oBeginButton.firePress(oEvent);
}
}
});
} else {
oDialog.close();
this.fireItemRenameCanceled({item: oItem});
}
};
/**
* Handler for item rename confirm operation.
* @param {object} oEvent confirm button click event.
* @private
*/
UploadSetwithTable.prototype._handleItemRenameConfirmation = function(oEvent) {
const oDialog = oEvent.getSource().getParent();
const oInput = oDialog.getContent()[1];
if (oInput && oInput.getValueState() === "Error") {
oInput.focus(oInput);
oInput.setShowValueStateMessage(true);
return;
}
const oItem = oDialog && oDialog.data ? oDialog.data().item : null;
const oSplit = UploadItem._splitFileName(oItem.getFileName());
// update only if there is change
if (oItem && oSplit.name !== oInput.getValue()) {
// const oContext = oItem.data("context");
if (oSplit && oSplit.extension) {
oItem.setFileName(oInput.getValue() + "." + oSplit.extension);
} else {
oItem.setFileName(oInput.getValue());
}
oDialog.close();
this.fireItemRenamed({item: oItem});
} else {
oDialog.close();
this.fireItemRenameCanceled({item: oItem});
}
};
/**
* Handler for file name validation.
* @param {object} oEvent Input keyevent.
* @private
*/
UploadSetwithTable.prototype._handleItemNameValidation = function(oEvent) {
const oInput = oEvent.getSource();
let sValue = oInput.getValue();
sValue = sValue.trim();
// empty file validation
if (sValue === "") {
oInput.setProperty("valueState", "Error", true);
oInput.setValueStateText(this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_EMPTY_NAME_VALIDATION_ERROR_MESSAGE"));
oInput.setShowValueStateMessage(true);
return;
}
const sValidationCharacterSet = this._getFileNameValidationChracters();
const sEscapedSpecialCharcters = sValidationCharacterSet.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
const oCharacterRegex = new RegExp(`[${sEscapedSpecialCharcters}]`);
if (oCharacterRegex.test(sValue)) {
oInput.setShowValueStateMessage(true);
oInput.setProperty("valueState", "Error", true);
oInput.setValueStateText(this._oRb.getText("UPLOADSET_WITH_TABLE_DOCUMENT_RENAME_SPLC_VALIDATION_ERROR_MESSAGE", [sValidationCharacterSet]));
} else {
oInput.setShowValueStateMessage(false);
oInput.setProperty("valueState", "None", true);
}
};
UploadSetwithTable.prototype._getFileNameValidationChracters = function() {
const {mode, characters} = this.getFileNameValidationConfig() || {};
switch (mode) {
case "include": {
// remove duplicate characters and the concatenate with default invalid characters
const aUniqueChars = [...new Set((UploadSetwithTable.DEFAULT_INVALID_FILENAME_CHARACTERSET + characters).split(""))];
const sUniqueChars = aUniqueChars.join("");
return sUniqueChars;
}
case "exclude": {
const aExcludeChars = [...new Set(characters.split(""))];
const sExcludeChars = aExcludeChars.join("");
// remove duplicate characters and the concatenate with default invalid characters
return excludeCharacters(UploadSetwithTable.DEFAULT_INVALID_FILENAME_CHARACTERSET, sExcludeChars);
}
case "override":
return characters;
default:
return UploadSetwithTable.DEFAULT_INVALID_FILENAME_CHARACTERSET;
}
function excludeCharacters(inputStr, charsToRemove) {
for (var char of charsToRemove) {
inputStr = inputStr.split(char).join("");
}
return inputStr;
}
};
UploadSetwithTable.prototype._onFileUploaderChange = function (oEvent) {
var oFiles = oEvent.getParameter("files");
if (oFiles && oFiles.length) {
// var aSelectedItems = this.getConfig("getSelectedItems", oControl);
// var oSelectedItem = aSelectedItems && aSelectedItems.length == 1 ? aSelectedItems[0] : null;
// var bEmptyFileSelected = oSelectedItem ? oSelectedItem && oSelectedItem.getFileName && oSelectedItem.getFileName() === "-" : false;
// // update existing file after upload
// if (bEmptyFileSelected) {
// this._oItemToUpdate = oFiles[0];
// }
this._processSelectedFileObjects(oFiles);
}
};
UploadSetwithTable.prototype._processSelectedFileObjects = function (oFiles) {
var aFiles = [];
// Need to explicitly copy the file list, FileUploader deliberately resets its form completely
// along with 'files' parameter when it (mistakenly) thinks that all is done.
for (var i = 0; i < oFiles.length; i++) {
aFiles.push(oFiles[i]);
}
aFiles.forEach((oFile) => {
var oItem = new UploadItem({
uploadState: UploadState.Ready
});
this._setInitialFileSettings({
item: oItem,
file: oFile,
fileList: aFiles
});
if (this.getItemValidationHandler() && typeof this.getItemValidationHandler() === "function" ) {
const oItemInfo = {
oItem: oItem,
iTotalItemsForUpload: aFiles.length,
oSource: this
};
var oPromise = this.getItemValidationHandler()(oItemInfo);
try {
if (oPromise && typeof oPromise?.then === "function") {
oPromise
.then((item) => {
if (item instanceof UploadItem) {
this._initateItemUpload(item);
}
})
.catch((item) => {
// Reset variable to avoid update if upload rejected.
if (item && this._oItemToUpdate && item instanceof UploadItem && item.getId() === this._oItemToUpdate.getId()) {
this._oItemToUpdate = null;
}
});
} else {
oItem.destroy();
// if promise is not returned to the ItemValidation hook log error and destroy the item
Log.error("Invalid usage, missing Promise: ItemValidationHandler callback expects Promise to be returned.");
}
} catch (error) {
oItem.destroy();
// If the ItemValidationHandler throws an error, log it and destroy the item
Log.error("Invalid usage, missing Promise: ItemValidationHandler callback expects Promise to be returned.", error);
}
} else {
/* if no validation handler is provided control continues with normal upload else waits for the application to manually
trigger the upload by resolving the promise */
this._initateItemUpload(oItem);
}
});
};
// Private method to set the initial file settings
UploadSetwithTable.prototype._setInitialFileSettings = function(oSettings) {
const { item } = oSettings;
let file = oSettings.file;
const sFileName = file.name;
const sMimeType = file.type;
if (!sMimeType) {
const sDocument = UploadItem._splitFileName(sFileName, true);
const sFileExtension = sDocument?.extension;
switch (sFileExtension) {
case UploadItem.FILETYPES.VDS:
// Create a new file object with the correct MIME type
file = new File([file] , sFileName, {type: UploadItem.MEDIATYPES.VDS});
// programatically set the MIME type to the item
item.setMediaType(UploadItem.MEDIATYPES.VDS);
break;
default:
break;
}
}
item.setParent(this); // setting the parent as UploadSetwithTable for file validations
item._setFileObject(file);
item.setFileName(sFileName);
};
UploadSetwithTable.prototype._fireFileTypeMismatch = function (oItem) {
var aMediaTypes = this.getMediaTypes();
var aFileTypes = this.getFileTypes();
var sFileType = oItem.getParameter("fileType");
var sMediaType = oItem.getParameter("mimeType");
var bMediaRestricted = (!!aMediaTypes && (aMediaTypes.length > 0) && !!sMediaType && aMediaTypes.indexOf(sMediaType) === -1);
var bFileRestricted = (!!aFileTypes && (aFileTypes.length > 0) && !!sFileType && aFileTypes.indexOf(sFileType) === -1);
var parts = [new Blob([])];
var oFileMetaData = {
type: oItem.getParameter('mimeType'),
webkitRelativePath: '',
name: oItem.getParameter('fileName')
};
var oFileObject = new File(parts, oItem.getParameter('fileName'), oFileMetaData);
var oMismatchItem = new UploadItem();
oMismatchItem._setFileObject(oFileObject);
oMismatchItem.setFileName(oFileObject.name);
if (bMediaRestricted){
this.fireMediaTypeMismatch({item: oMismatchItem});
} else if (bFileRestricted){
this.fireFileTypeMismatch({item: oMismatchItem});
}
};
UploadSetwithTable.prototype._fireFilenameLengthExceed = function (oItem) {
var oTargetItem = new UploadItem();
oTargetItem.setFileName(oItem.getParameter('fileName'));
this.fireFileNameLengthExceeded({item: oTargetItem});
};
UploadSetwithTable.prototype._fireFileSizeExceed = function (oItem) {
var oTargetItem = new UploadItem();
oTargetItem.setFileName(oItem.getParameter('fileName'));
this.fireFileSizeExceeded({item: oTargetItem});
};
UploadSetwithTable.prototype._onUploadStarted = function (oEvent) {
var oItem = oEvent.getParameter("item");
oItem.setUploadState(UploadState.Uploading);
};
UploadSetwithTable.prototype._onUploadCompleted = function (oEvent) {
var oItem = oEvent.getParameter("item"),
oResponseXHRParams = oEvent.getParameter("responseXHR"),
sResponse = null;
if (oResponseXHRParams.responseXML) {
sResponse = oResponseXHRParams.responseXML.documentElement.textContent;
}
var oXhrParams = {
"item": oItem,
"response": oResponseXHRParams.response,
"responseXML": sResponse,
"responseText": oResponseXHRParams.responseText,
"readyState": oResponseXHRParams.readyState,
"status": oResponseXHRParams.status,
"headers": oResponseXHRParams.headers
};
if (this._oItemToUpdate) {
this._oItemToUpdate.setFileName(oItem.getFileName());
this._oItemToUpdate._setFileObject(oItem.getFileObject());
this._oItemToUpdate = null;
}
oItem.setUploadState(UploadState.Complete);
this.fireUploadCompleted(oXhrParams);
};
UploadSetwithTable.prototype._onUploadProgressed = function (oEvent) {
var oItem = oEvent.getParameter("item");
oItem.fireUploadProgress({
loaded: oEvent.getParameter("loaded"),
total: oEvent.getParameter("total")
});
};
UploadSetwithTable.prototype._uploadItemIfGoodToGo = function (oItem) {
if (oItem.getUploadState() === UploadState.Ready && !oItem._isRestricted()) {
if (this.fireBeforeUploadStarts({item: oItem})) {
const aHeaderFields = this.getHeaderFields()?.length ? this.getHeaderFields() : [];
const aItemHeaderFields = oItem.getHeaderFields()?.length ? oItem.getHeaderFields() : [];
const oHeaderFields = [...aHeaderFields, ...aItemHeaderFields]; //Merging headers for request.
this._getActiveUploader().uploadItem(oItem, oHeaderFields);
}
}
};
UploadSetwithTable.prototype._getActiveUploader = function () {
return this.getUploader() || this._getImplicitUploader();
};
UploadSetwithTable.prototype._getImplicitUploader = function () {
if (!this._oUploader) {
this._oUploader = new Uploader({
httpRequestMethod : this.getHttpRequestMethod()
});
this._oUploader.setUploadUrl(this.getUploadUrl());
this.registerUploaderEvents(this._oUploader);
this.addDependent(this._oUploader);
}
return this._oUploader;
};
UploadSetwithTable.prototype._initateItemUpload = function(oItem) {
this.fireBeforeInitiatingItemUpload({item: oItem});
if (this._oItemToUpdate) {
// Registering item to be update with selected file contents post successful upload.
this._oItemToUpdate = oItem;
}
this._uploadItemIfGoodToGo(oItem);
};
/**
* Drag and drop of files implmentation subject to change depending on the thr UX feedback for folder upload scenarios and warning message display scenarios
* @param {sap.ui.base.Event} oEvent Drop Event when file is dropped on the Table.
* @private
*/
UploadSetwithTable.prototype._onDropFile = function (oEvent) {
oEvent.preventDefault();
if (!this.getUploadEnabled()) {
Log.error("Upload is not enabled, to continue uploading with drag and drop of files enable property 'UploadEnabled' ");
return;
}
let oItems = oEvent.getParameter("browserEvent")?.dataTransfer?.items || [];
oItems = Array.from(oItems);
// Filtering out only webkitentries (files/folders system entries) by excluding non file / directory types.
oItems = oItems.filter(function(item){
return item.webkitGetAsEntry() ? true : false;
});
const aEntryTypes = oItems.map(function (oEntry) {
const oWebKitEntry = oEntry.webkitGetAsEntry();
return {
entryType: oWebKitEntry && oWebKitEntry.isFile ? 'File' : 'Directory'
};
});
// handlding multiple property drag & drop scenarios
if (oItems && oItems.length > 1 && !this.getMultiple() && !this.getDirectory()) {
// Handling drag and drop of multiple files to upload with multiple property set
const sMessage = this._oRb.getText("UPLOADSET_WITH_TABLE_MULTIPLE_RESTRICTED");
Log.warning("Multiple files upload is retsricted for this multiple property set");
MessageBox.error(sMessage);
return;
} else if (oItems && oItems.length > 1 && this.getMultiple() && !isFileOrFolderEntry('File', aEntryTypes)) {
const sMessageDropFilesOnly = this._oRb.getText("UPLOADSET_WITH_TABLE_DIRECTORY_RESTRICTED");
Log.warning("Multiple files upload is retsricted, drag & drop only files");
MessageBox.error(sMessageDropFilesOnly);
return;
}
// handling directory property drag & drop scenarios
if (oItems && oItems.length && !this.getDirectory() && isFileOrFolderEntry('Directory', aEntryTypes)) {
const sMessageDirectory = this._oRb.getText("UPLOADSET_WITH_TABLE_DIRECTORY_RESTR