@sapui5/sap.ui.export 1.132.1 → 1.134.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sapui5/sap.ui.export",
3
- "version": "1.132.1",
3
+ "version": "1.134.0",
4
4
  "description": "SAPUI5 Library sap.ui.export",
5
5
  "homepage": "https://sap.github.io/ui5-tooling/pages/SAPUI5/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -5,7 +5,7 @@
5
5
  <vendor>SAP SE</vendor>
6
6
  <copyright>SAPUI5
7
7
  * (c) Copyright 2009-2024 SAP SE. All rights reserved.</copyright>
8
- <version>1.132.1</version>
8
+ <version>1.134.0</version>
9
9
 
10
10
  <documentation>UI5 library: sap.ui.export</documentation>
11
11
 
@@ -27,7 +27,7 @@ sap.ui.define([
27
27
  * @class The <code>sap.ui.export.ExportBase</code> class allows you to export table data from a UI5 application to certain formats. This class is an abstract class that requires specific implementations for each file format.
28
28
  *
29
29
  * @author SAP SE
30
- * @version 1.132.1
30
+ * @version 1.134.0
31
31
  *
32
32
  * @since 1.96
33
33
  * @alias sap.ui.export.ExportBase
@@ -28,7 +28,7 @@ sap.ui.define([
28
28
  * @class The <code>sap.ui.export.ExportHandler</code> class allows you to export table data from an SAPUI5 application.
29
29
  *
30
30
  * @author SAP SE
31
- * @version 1.132.1
31
+ * @version 1.134.0
32
32
  *
33
33
  * @since 1.102
34
34
  * @alias sap.ui.export.ExportHandler
@@ -9,6 +9,7 @@ sap.ui.define([
9
9
  "sap/base/Log",
10
10
  "sap/base/i18n/Formatting",
11
11
  "sap/base/i18n/Localization",
12
+ "sap/base/i18n/date/CalendarType",
12
13
  "sap/ui/core/Element",
13
14
  "sap/ui/core/Fragment",
14
15
  "sap/ui/core/InvisibleMessage",
@@ -23,7 +24,7 @@ sap.ui.define([
23
24
  "sap/ui/performance/trace/FESRHelper",
24
25
  "sap/ui/util/openWindow",
25
26
  "sap/ui/VersionInfo"
26
- ], function(library, Filter, BaseConfig, Log, Formatting, Localization, Element, Fragment, InvisibleMessage, Library, Locale, DateFormat, coreLibrary, syncStyleClass, LocaleData, JSONModel, ResourceModel, FESRHelper, openWindow, VersionInfo) {
27
+ ], function(library, Filter, BaseConfig, Log, Formatting, Localization, CalendarType, Element, Fragment, InvisibleMessage, Library, Locale, DateFormat, coreLibrary, syncStyleClass, LocaleData, JSONModel, ResourceModel, FESRHelper, openWindow, VersionInfo) {
27
28
  "use strict";
28
29
 
29
30
  // eslint-disable-next-line
@@ -31,7 +32,6 @@ sap.ui.define([
31
32
 
32
33
  // Shortcuts
33
34
  const ValueState = coreLibrary.ValueState;
34
- const CalendarType = coreLibrary.CalendarType;
35
35
  const FileType = library.FileType;
36
36
  const EdmType = library.EdmType;
37
37
  const Destination = library.Destination;
@@ -75,7 +75,7 @@ sap.ui.define([
75
75
  fileTypeCollection: [],
76
76
  fileType: "XLSX",
77
77
  destinationCollection: [
78
- {key: Destination.LOCAL, text: oResourceBundle.getText("DESTINATION_LOCAL")}
78
+ { key: Destination.LOCAL, text: oResourceBundle.getText("DESTINATION_LOCAL") }
79
79
  ],
80
80
  destination: Destination.LOCAL,
81
81
  paperSize: "DIN_A4",
@@ -88,12 +88,12 @@ sap.ui.define([
88
88
  capabilities: oExportCapabilities,
89
89
  fitToPage: false,
90
90
  paperSizeCollection: [
91
- {key: "DIN_A4", text: oResourceBundle.getText("PAPER_SIZE_A4")},
92
- {key: "US_LETTER", text: oResourceBundle.getText("PAPER_SIZE_US_LETTER")}
91
+ { key: "DIN_A4", text: oResourceBundle.getText("PAPER_SIZE_A4") },
92
+ { key: "US_LETTER", text: oResourceBundle.getText("PAPER_SIZE_US_LETTER") }
93
93
  ],
94
94
  orientationCollection: [
95
- {key:"LANDSCAPE", text: oResourceBundle.getText("ORIENTATION_LAND")},
96
- {key:"PORTRAIT", text: oResourceBundle.getText("ORIENTATION_PORT")}
95
+ { key:"LANDSCAPE", text: oResourceBundle.getText("ORIENTATION_LAND") },
96
+ { key:"PORTRAIT", text: oResourceBundle.getText("ORIENTATION_PORT") }
97
97
  ],
98
98
  fontSize: 10,
99
99
  signature: false,
@@ -144,7 +144,7 @@ sap.ui.define([
144
144
  * @class Utilities related to export to enable reuse in integration scenarios (e.g. tables).
145
145
  *
146
146
  * @author SAP SE
147
- * @version 1.132.1
147
+ * @version 1.134.0
148
148
  *
149
149
  * @since 1.59
150
150
  * @alias sap.ui.export.ExportUtils
@@ -246,10 +246,6 @@ sap.ui.define([
246
246
  * @private
247
247
  */
248
248
  getExportSettingsViaDialog: function(mCustomConfig, oExportCapabilities, bRemoteDestination, oOpener, fnCallback) {
249
- function isPDFFileShareExportEnabled() {
250
- return document.location.search.includes("sap-ui-xx-pdfFileShareExport=true") && oExportCapabilities?.PDF?.UploadToFileShare;
251
- }
252
-
253
249
  return new Promise(function (fnResolve, fnReject) {
254
250
  let oExportSettingsDialog;
255
251
 
@@ -306,8 +302,7 @@ sap.ui.define([
306
302
  return sValue === FileType.XLSX || sValue === FileType.GSHEET;
307
303
  },
308
304
  isDestinationEnabled: function(sFileType) {
309
- // URL Parameter dependend activation of PDF File Share Export
310
- return isPDFFileShareExportEnabled() ? sFileType !== FileType.GSHEET : sFileType === FileType.XLSX;
305
+ return oExportCapabilities?.PDF?.UploadToFileShare ? sFileType !== FileType.GSHEET : sFileType === FileType.XLSX;
311
306
  },
312
307
  hasDestinations: function(aDestinationCollection) {
313
308
  return aDestinationCollection.length > 1;
@@ -357,11 +352,6 @@ sap.ui.define([
357
352
 
358
353
  switch (oSelectedItem.getKey()) {
359
354
  case FileType.PDF:
360
- // URL Parameter dependend activation of PDF File Share Export
361
- if (!isPDFFileShareExportEnabled()) {
362
- oExportConfigModel.setProperty("/destination", Destination.LOCAL);
363
- }
364
-
365
355
  oExportConfigModel.setProperty("/includeFilterSettings", false);
366
356
  fnSetSemanticStepName(oExportConfigModel, oExportSettingsDialog.getBeginButton());
367
357
  break;
@@ -1218,7 +1208,7 @@ sap.ui.define([
1218
1208
  *
1219
1209
  * @param {object} oContext Context object
1220
1210
  * @param {string} [oContext.application] Name of the application (default: "SAP UI5")
1221
- * @param {string} [oContext.version] Application version (default: "1.132.1")
1211
+ * @param {string} [oContext.version] Application version (default: "1.134.0")
1222
1212
  * @param {string} [oContext.title] Title that will be written to the file (NOT the filename)
1223
1213
  * @param {string} [oContext.modifiedBy] Optional user context that will be written to the file
1224
1214
  * @param {string} [oContext.sheetName] Name of the data sheet - Maximum length of 31 characters
@@ -22,11 +22,13 @@ sap.ui.define([
22
22
  const EdmType = Library.EdmType;
23
23
  const Status = Library.Status;
24
24
 
25
+ const MIME_TYPE = "application/pdf";
26
+
25
27
  /**
26
28
  * @class The <code>sap.ui.export.PortableDocument</code> class allows you to export table data from a UI5 application to a Portable Document Format (*.PDF) file.
27
29
  *
28
30
  * @author SAP SE
29
- * @version 1.132.1
31
+ * @version 1.134.0
30
32
  *
31
33
  * @since 1.96
32
34
  * @alias sap.ui.export.PortableDocument
@@ -130,9 +132,11 @@ sap.ui.define([
130
132
  type: "odata",
131
133
  version: bV4ODataModel ? 4 : 2,
132
134
  dataUrl: oDataUrl.toString(),
133
- serviceUrl: this._resolveServiceUrl(sServiceUrl),
135
+ commonServiceUrl: this._resolveCommonServiceUrl(sServiceUrl),
136
+ serviceUrl: PortableDocument.harmonizeUrl(sServiceUrl),
134
137
  headers: bV4ODataModel ? oModel.getHttpHeaders(true) : oModel.getHeaders(),
135
- count: ExportUtils.getCountFromBinding(oBinding)
138
+ count: ExportUtils.getCountFromBinding(oBinding),
139
+ useBatch: bV4ODataModel || oModel.bUseBatch
136
140
  };
137
141
  }
138
142
 
@@ -149,10 +153,8 @@ sap.ui.define([
149
153
  *
150
154
  * @private
151
155
  */
152
- PortableDocument.prototype._resolveServiceUrl = function(sCurrentServiceUrl) {
153
- if (!sCurrentServiceUrl.endsWith("/")) {
154
- sCurrentServiceUrl += "/";
155
- }
156
+ PortableDocument.prototype._resolveCommonServiceUrl = function(sCurrentServiceUrl) {
157
+ sCurrentServiceUrl = PortableDocument.harmonizeUrl(sCurrentServiceUrl);
156
158
 
157
159
  const isRelative = /^[\./]/.test(sCurrentServiceUrl);
158
160
  let sReference = this._mCapabilities.DocumentDescriptionReference;
@@ -241,7 +243,7 @@ sap.ui.define([
241
243
  if (this._mCapabilities.FitToPage) {
242
244
  oDocumentDescription.Format.FitToPage = {
243
245
  IsEnabled: mSettings.fitToPage,
244
- MinimumFontSize: 4
246
+ MinimumFontSize: 1
245
247
  };
246
248
  }
247
249
 
@@ -284,12 +286,14 @@ sap.ui.define([
284
286
  }
285
287
  }
286
288
 
289
+ /* FileShare */
287
290
  if (this._mCapabilities.UploadToFileShare && this._mCloudFileInfo) {
288
291
  oDocumentDescription.FileName = this._mCloudFileInfo.FileShareItemName;
289
292
  oDocumentDescription.FileShare = {
290
293
  Repository: this._mCloudFileInfo.FileShare,
291
294
  Folder: this._mCloudFileInfo.ParentFileShareItem
292
295
  };
296
+ mSettings.dataSource.headers["SAP-Upload-To-File-Share"] = true;
293
297
  }
294
298
 
295
299
  /* Eliminate duplicate or unknown columns before adding them to the DocumentDescription */
@@ -360,7 +364,7 @@ sap.ui.define([
360
364
  const iVersion = oDataSource.version || 2;
361
365
 
362
366
  return iVersion === 4 ? new ODataModel({
363
- serviceUrl: oDataSource.serviceUrl
367
+ serviceUrl: oDataSource.commonServiceUrl
364
368
  }) : this._oModel;
365
369
  };
366
370
 
@@ -481,40 +485,26 @@ sap.ui.define([
481
485
  const oModel = this._getModel(oDataSource);
482
486
  const sPath = "/" + this._getEntitySetName(oDataSource);
483
487
 
484
- if (!oModel || !oModel.isA(["sap.ui.model.odata.v4.ODataModel", "sap.ui.model.odata.v2.ODataModel"])) {
485
- return Promise.reject("Unsupported Model");
488
+ if (!oModel?.isA(["sap.ui.model.odata.v4.ODataModel", "sap.ui.model.odata.v2.ODataModel"])) {
489
+ throw new Error("Unsupported Model");
486
490
  }
487
491
 
488
- return new Promise((fnResolve, fnReject) => {
489
-
490
- if (oModel.isA("sap.ui.model.odata.v4.ODataModel")) {
491
- const oBinding = oModel.bindList(sPath);
492
-
493
- oBinding.attachCreateCompleted((oEvent) => {
494
- if (oEvent.getParameter("success")) {
495
- fnResolve(oEvent.getParameter("context").getObject()["Id"]);
496
- } else {
497
- fnReject();
498
- }
499
- });
500
-
501
- oBinding.create(oDocumentDescription);
502
- } else {
503
- const bUseBatch = oModel.bUseBatch;
492
+ if (oModel.isA("sap.ui.model.odata.v4.ODataModel")) {
493
+ const oBinding = oModel.bindList(sPath);
494
+ const oContext = oBinding.create(oDocumentDescription);
504
495
 
505
- oModel.setUseBatch(false);
496
+ return oContext.created().then(() => oContext.getObject()["Id"]);
497
+ } else {
498
+ return new Promise((fnResolve, fnReject) => {
506
499
  oModel.create(sPath, oDocumentDescription, {
500
+ groupId: "documentDescription",
507
501
  success: (oData) => {
508
- oModel.setUseBatch(bUseBatch);
509
502
  fnResolve(oData["Id"]);
510
503
  },
511
- error: (oError) => {
512
- oModel.setUseBatch(bUseBatch);
513
- fnReject(oError);
514
- }
504
+ error: fnReject
515
505
  });
516
- }
517
- });
506
+ });
507
+ }
518
508
  };
519
509
 
520
510
  /**
@@ -551,9 +541,9 @@ sap.ui.define([
551
541
  this._request = new AbortController();
552
542
 
553
543
  const sDocumentDescriptionId = await this.postDocumentDescription(oDocumentDescription, mSettings.dataSource);
554
- const response = await this.sendRequest(mSettings.dataSource.dataUrl, sDocumentDescriptionId);
544
+ const oPDFBlob = await this.sendRequest(mSettings.dataSource, sDocumentDescriptionId);
555
545
 
556
- ExportUtils.saveAsFile(response, mSettings.fileName);
546
+ ExportUtils.saveAsFile(oPDFBlob, mSettings.fileName);
557
547
  ExportUtils.announceExportStatus(Status.FINISHED, { assertive: true });
558
548
  } catch (oError) {
559
549
  PortableDocument.showErrorMessage(oError);
@@ -565,30 +555,31 @@ sap.ui.define([
565
555
  /**
566
556
  * Requests the generated PDF via HTTP GET from the OData service.
567
557
  *
568
- * @param {string} sUrl Absolute data URL of the OData entity that should be exported as PDF
558
+ * @param {object} oDataSource DataSource settings of the export configuration
559
+ * @param {string} oDataSource.dataUrl Absolute data URL of the OData entity that should be exported as PDF
569
560
  * @param {string} sDocumentDescriptionId GUID of the DocumentDescription that should be used for creating the PDF
570
561
  * @returns {Promise} A Promise that gets resolved after the XHR request
571
562
  *
572
563
  * @private
573
564
  */
574
- PortableDocument.prototype.sendRequest = async function(sUrl, sDocumentDescriptionId) {
565
+ PortableDocument.prototype.sendRequest = async function(oDataSource, sDocumentDescriptionId) {
575
566
  if (this._request?.signal.aborted) {
576
567
  throw null; // Explicitly reject the Promise without an error to indicate prior user cancellation
577
568
  }
578
569
 
579
- sUrl = this._applyResultSize(sUrl);
570
+ oDataSource.dataUrl = this._applyResultSize(oDataSource.dataUrl);
571
+
572
+ const oRequest = oDataSource.useBatch ? PortableDocument.createBatchRequest(oDataSource, sDocumentDescriptionId, this._request) : PortableDocument.createGetRequest(oDataSource, sDocumentDescriptionId, this._request);
580
573
 
581
574
  try {
582
- const response = await fetch(sUrl, {
583
- signal: this._request?.signal,
584
- headers: {
585
- "Accept": this.getMimeType(),
586
- "SAP-Document-Description-Id": sDocumentDescriptionId
587
- }
588
- });
575
+ let response = await fetch(oRequest);
589
576
 
590
- if (response.redirected && response.url !== sUrl) {
591
- openWindow(response.url, "_blank");
577
+ if (oDataSource.useBatch) {
578
+ response = await PortableDocument.getBatchResponse(response);
579
+ }
580
+
581
+ if (response.status === 301 && response.headers.has("Location")) {
582
+ openWindow(response.headers.get("Location"), "_blank");
592
583
  return undefined;
593
584
  }
594
585
 
@@ -606,6 +597,264 @@ sap.ui.define([
606
597
  }
607
598
  };
608
599
 
600
+ /**
601
+ * Ensures that the given URL ends with a forward slash.
602
+ *
603
+ * @param {string} sUrl URL that is harmonized
604
+ *
605
+ * @returns {string} URL that ends with a forward slash
606
+ * @static
607
+ * @private
608
+ */
609
+ PortableDocument.harmonizeUrl = function(sUrl) {
610
+ return sUrl.endsWith("/") ? sUrl : sUrl + "/";
611
+ };
612
+
613
+ /**
614
+ * Creates <code>Request</code> with method GET for the given <code>dataSource</code> and <code>SAP-Document-Description-Id</code>.
615
+ *
616
+ * @param {object} oDataSource <code>DataSource</code> settings of the export configuration
617
+ * @param {string} sDocumentDescriptionId GUID of the <code>DocumentDescription</code> that is used for creating the PDF
618
+ * @param {AbortController} oAbortController <code>AbortController</code> instance that is used to cancel the request
619
+ *
620
+ * @returns {Request} Request object that is used to fetch the PDF document
621
+ * @static
622
+ * @private
623
+ */
624
+ PortableDocument.createGetRequest = function(oDataSource, sDocumentDescriptionId, oAbortController) {
625
+ const oHeaders = new Headers(oDataSource.headers);
626
+
627
+ oHeaders.set("Accept", MIME_TYPE);
628
+ oHeaders.set("SAP-Document-Description-Id", sDocumentDescriptionId);
629
+
630
+ return new Request(oDataSource.dataUrl, {
631
+ signal: oAbortController.signal,
632
+ headers: oHeaders
633
+ });
634
+ };
635
+
636
+ /**
637
+ * Creates a $batch request with a single GET request for the given <code>dataSource</code> and <code>SAP-Document-Description-Id</code>.
638
+ *
639
+ * @param {object} oDataSource DataSource settings of the export configuration
640
+ * @param {string} sDocumentDescriptionId GUID of the DocumentDescription that is used for creating the PDF
641
+ * @param {AbortController} oAbortController AbortController instance that is used to cancel the request
642
+ *
643
+ * @returns {Request} Request object that is used to fetch the PDF document
644
+ * @static
645
+ * @private
646
+ */
647
+ PortableDocument.createBatchRequest = function(oDataSource, sDocumentDescriptionId, oAbortController) {
648
+ const sUrl = oDataSource.dataUrl.split(oDataSource.serviceUrl)[1];
649
+ const sBoundary = "batch_" + PortableDocument._createGuid();
650
+ const oHeaders = new Headers(oDataSource.headers);
651
+
652
+ /* Header adjustments for inner request */
653
+ oHeaders.set("Accept", MIME_TYPE);
654
+ oHeaders.set("SAP-Document-Description-Id", sDocumentDescriptionId);
655
+
656
+ const sBody = PortableDocument.createRequestBody(sUrl, sBoundary, oHeaders);
657
+
658
+ /* Header adjustments for outer request */
659
+ oHeaders.set("Accept", "multipart/mixed");
660
+ oHeaders.set("Content-Type", "multipart/mixed;boundary=" + sBoundary);
661
+ oHeaders.delete("SAP-Document-Description-Id");
662
+ oHeaders.delete("SAP-Upload-To-File-Share");
663
+
664
+ return new Request(oDataSource.serviceUrl + "$batch", {
665
+ body: sBody,
666
+ cache: "no-store",
667
+ headers: oHeaders,
668
+ method: "POST",
669
+ signal: oAbortController.signal
670
+ });
671
+ };
672
+
673
+ /**
674
+ * Creates the request body for a $batch request.
675
+ *
676
+ * @param {string} sUrl The URL that is requested
677
+ * @param {string} sBoundary The boundary id for the inner request
678
+ * @param {Headers} oHeaders The headers that are sent with the request
679
+ *
680
+ * @returns {string} The request body
681
+ * @static
682
+ * @private
683
+ */
684
+ PortableDocument.createRequestBody = function(sUrl, sBoundary, oHeaders) {
685
+ const aBody = [];
686
+
687
+ aBody.push("--" + sBoundary);
688
+ aBody.push("Content-Type: application/http");
689
+ aBody.push("Content-Transfer-Encoding: binary");
690
+ aBody.push("");
691
+ aBody.push(`GET ${sUrl} HTTP/1.1`);
692
+
693
+ /* Set header information */
694
+ for (const [sKey, sValue] of oHeaders.entries()) {
695
+ aBody.push(sKey + ":" + sValue);
696
+ }
697
+
698
+ aBody.push("");
699
+ aBody.push("");
700
+ aBody.push("--" + sBoundary + "--");
701
+ aBody.push("");
702
+
703
+ return aBody.join("\r\n");
704
+ };
705
+
706
+ /**
707
+ * Unwraps the $batch response and returns a <code>Response</code> object
708
+ * corresponding to the inner GET request.
709
+ *
710
+ * @param {Response} oResponse The <code>Response</code> of the $batch request
711
+ *
712
+ * @returns {Promise<Response>} A <code>Promise</code> that resolves with the <code>Response</code> of the inner GET request
713
+ * @async
714
+ * @static
715
+ * @private
716
+ */
717
+ PortableDocument.getBatchResponse = async function(oResponse) {
718
+ if (!oResponse.ok) {
719
+ // $batch response cannot be unwrapped when the outer request failed
720
+ return oResponse;
721
+ }
722
+
723
+ const oBuffer = await oResponse.arrayBuffer();
724
+ const oUint8 = new Uint8Array(oBuffer);
725
+
726
+ const {status, statusText, headers, pdfContent} = PortableDocument.parseResponseBody(oUint8);
727
+
728
+ return new Response(pdfContent, {
729
+ status: status,
730
+ statusText: statusText,
731
+ headers: headers
732
+ });
733
+ };
734
+
735
+ /**
736
+ * Detects the start index of the PDF content in the binary response.
737
+ * The start boundary is "%PDF-". If the response contains multiple PDFs,
738
+ * the first one is used.
739
+ *
740
+ * @param {Uint8Array} oTypedArray The binary data of the response
741
+ *
742
+ * @returns {number} The start index of the PDF content. If no PDF content is found, -1 is returned.
743
+ * @static
744
+ * @private
745
+ */
746
+ PortableDocument.getStartIndex = function(oTypedArray) {
747
+ return oTypedArray.findIndex((element, index) => {
748
+ const sStartBoundary = "%PDF-";
749
+
750
+ return element === sStartBoundary.charCodeAt(0)
751
+ && oTypedArray[index + 1] === sStartBoundary.charCodeAt(1)
752
+ && oTypedArray[index + 2] === sStartBoundary.charCodeAt(2)
753
+ && oTypedArray[index + 3] === sStartBoundary.charCodeAt(3)
754
+ && oTypedArray[index + 4] === sStartBoundary.charCodeAt(4);
755
+ });
756
+ };
757
+
758
+ /**
759
+ * Detects the end index of the PDF content in the binary response.
760
+ * The end boundary is "%%EOF". If the response contains multiple PDFs,
761
+ * the last one is used.
762
+ *
763
+ * @param {Uint8Array} oTypedArray The binary data of the response
764
+ *
765
+ * @returns {number} The start index of the PDF content. If no PDF content is found, -1 is returned.
766
+ * @static
767
+ * @private
768
+ */
769
+ PortableDocument.getEndIndex = function(oTypedArray) {
770
+ return oTypedArray.findLastIndex((element, index) => {
771
+ const sEndBoundary = "%%EOF";
772
+
773
+ return element === sEndBoundary.charCodeAt(4)
774
+ && oTypedArray[index - 1] === sEndBoundary.charCodeAt(3)
775
+ && oTypedArray[index - 2] === sEndBoundary.charCodeAt(2)
776
+ && oTypedArray[index - 3] === sEndBoundary.charCodeAt(1)
777
+ && oTypedArray[index - 4] === sEndBoundary.charCodeAt(0);
778
+ });
779
+ };
780
+
781
+ /**
782
+ * Parses the binary response of the inner request. It identifies the PDF content
783
+ * by its field boundaries %PDF- and %%EOF. The response status and its headers are
784
+ * evaluated as well.
785
+ *
786
+ * @param {Uint8Array} oTypedArray The binary response of the inner request
787
+ * @returns {object} An object containing the <code>status</code>, <code>statusText</code>, <code>headers</code>, and the PDF content
788
+ * @static
789
+ * @private
790
+ */
791
+ PortableDocument.parseResponseBody = function(oTypedArray) {
792
+ let oPDFContent;
793
+ const iPDFStartIndex = PortableDocument.getStartIndex(oTypedArray);
794
+ const iPDFEndIndex = PortableDocument.getEndIndex(oTypedArray);
795
+
796
+ if (iPDFStartIndex !== -1 && iPDFEndIndex !== -1) {
797
+ // Increase end index by one, since subarray does not include the second index
798
+ oPDFContent = oTypedArray.subarray(iPDFStartIndex, iPDFEndIndex + 1);
799
+ }
800
+
801
+ /*
802
+ * If iPDFStartIndex is -1, the responseText is missing the last character
803
+ * of the response which can be ignored because it is either a line break
804
+ * or the trailing "-" of the batch boundary.
805
+ */
806
+ const sResponseText = new TextDecoder().decode(oTypedArray.subarray(0, iPDFStartIndex));
807
+ const aLines = sResponseText.split("\r\n");
808
+
809
+ // Remove all lines prior to HTTP status
810
+ aLines.splice(0, aLines.findIndex((sLine) => /^HTTP\/1\.[0|1] ([1-9][0-9]{2})/.test(sLine)));
811
+
812
+ const [, iStatusCode, sStatusText] = aLines[0].match(/^HTTP\/1\.[0|1] ([1-9][0-9]{2}) ?([\w ]*)$/);
813
+ const reHeaders = /^([\w\-]+): ?(.+)$/;
814
+ const oHeaders = aLines
815
+ .filter((sLine) => reHeaders.test(sLine))
816
+ .reduce((oAccumulator, sLine) => {
817
+ const [, sKey, sValue] = sLine.match(reHeaders);
818
+
819
+ oAccumulator.set(sKey, sValue);
820
+
821
+ return oAccumulator;
822
+ }, new Headers());
823
+
824
+ return {
825
+ status: iStatusCode,
826
+ statusText: sStatusText,
827
+ headers: oHeaders,
828
+ pdfContent: oPDFContent
829
+ };
830
+ };
831
+
832
+ /**
833
+ * Creates a pseudo random GUID. This algorithm is not suitable for
834
+ * cryptographic purposes and should not be used for such.
835
+ *
836
+ * *** IMPORTANT ***
837
+ * This is a fallback solution due to missing crypto features in
838
+ * headless chrome that is used for the UI5 voter.
839
+ *
840
+ * @returns {string} Generated GUID
841
+ *
842
+ * @static
843
+ * @private
844
+ */
845
+ PortableDocument._createGuid = function() {
846
+ if (globalThis.crypto?.randomUUID) {
847
+ return globalThis.crypto.randomUUID();
848
+ }
849
+
850
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
851
+ const r = Math.random() * 16 | 0, // Bitwise OR is equivalent to Math.floor() but faster
852
+ v = c === "x" ? r : ((r & 0x3) | 0x8); // In case of c != "x", the value is always between 0x8 and 0xB
853
+
854
+ return v.toString(16);
855
+ });
856
+ };
857
+
609
858
  /**
610
859
  * Returns the specific MIME type
611
860
  *
@@ -615,7 +864,7 @@ sap.ui.define([
615
864
  * @public
616
865
  */
617
866
  PortableDocument.prototype.getMimeType = function() {
618
- return "application/pdf";
867
+ return MIME_TYPE;
619
868
  };
620
869
 
621
870
  /**
@@ -90,7 +90,7 @@ sap.ui.define([
90
90
  * <li><code>workbook.context</code> - Context object that will be applied to the generated file. It may contain the following fields:</li>
91
91
  * <ul>
92
92
  * <li><code>application</code> (string) - The application that creates the XLSX document (default: "SAP UI5")</li>
93
- * <li><code>version</code> (string) - Application version that creates the XLSX document (default: "1.132.1")</li>
93
+ * <li><code>version</code> (string) - Application version that creates the XLSX document (default: "1.134.0")</li>
94
94
  * <li><code>title</code> (string) - Title of the XLSX document (NOT the filename)</li>
95
95
  * <li><code>modifiedBy</code> (string) - User context for the XLSX document</li>
96
96
  * <li><code>sheetName</code> (string) - The label of the data sheet</li>
@@ -174,7 +174,7 @@ sap.ui.define([
174
174
  * columns: aColumns,
175
175
  * context: {
176
176
  * application: 'Debug Test Application',
177
- * version: '1.132.1',
177
+ * version: '1.134.0',
178
178
  * title: 'Some random title',
179
179
  * modifiedBy: 'John Doe',
180
180
  * metaSheetName: 'Custom metadata',
@@ -286,7 +286,7 @@ sap.ui.define([
286
286
  * @class The <code>sap.ui.export.Spreadsheet</code> class allows you to export table data from a UI5 application to a spreadsheet file.
287
287
  *
288
288
  * @author SAP SE
289
- * @version 1.132.1
289
+ * @version 1.134.0
290
290
  *
291
291
  * @since 1.50
292
292
  * @alias sap.ui.export.Spreadsheet
@@ -21,7 +21,7 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
21
21
  * Utility class to perform spreadsheet export.
22
22
  *
23
23
  * @author SAP SE
24
- * @version 1.132.1
24
+ * @version 1.134.0
25
25
  *
26
26
  * @alias sap.ui.export.SpreadsheetExport
27
27
  * @private
@@ -15,7 +15,7 @@ sap.ui.define(["sap/ui/core/Lib"], function(Library) {
15
15
  * @namespace
16
16
  * @alias sap.ui.export
17
17
  * @author SAP SE
18
- * @version 1.132.1
18
+ * @version 1.134.0
19
19
  * @public
20
20
  */
21
21
 
@@ -34,7 +34,7 @@ sap.ui.define(["sap/ui/core/Lib"], function(Library) {
34
34
  interfaces: [],
35
35
  controls: [],
36
36
  elements: [],
37
- version: "1.132.1"
37
+ version: "1.134.0"
38
38
  });
39
39
 
40
40
  /**
@@ -24,7 +24,7 @@
24
24
  * @param {object} mSettings Data service related part of the export configuration
25
25
  *
26
26
  * @author SAP SE
27
- * @version 1.132.1
27
+ * @version 1.134.0
28
28
  *
29
29
  * @constructor
30
30
  * @class DataProviderBase
@@ -20,7 +20,7 @@ sap.ui.define(['sap/ui/base/Object'], function(BaseObject) {
20
20
  * convenience functions like <code>sap.ui.export.util.Filter#setType</code> to improve the result.
21
21
  *
22
22
  * @author SAP SE
23
- * @version 1.132.1
23
+ * @version 1.134.0
24
24
  *
25
25
  * @since 1.110
26
26
  * @alias sap.ui.export.util.Filter