Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[CAP] Fiori Elements – Add section with PDFViewer

I have a CAP Service that provides a PDF file that I needed to display in a Fiori Elements frontend using the sap.m.PDFViewer. The viewer should be placed in a section on the object page after navigating from the ListReport main page.

My CAP Service has the following annotations to provide the PDF.

  entity pdfFiles : cuid, managed {
    content            : LargeBinary @stream @Core.MediaType: mediaType @Core.ContentDisposition.Filename: fileName @Core.ContentDisposition.Type: 'inline';
    mediaType          : String      @Core.IsMediaType: true;
    fileName           : String      @mandatory;
  }

Add a custom section to your view following this example: https://sapui5.hana.ondemand.com/test-resources/sap/fe/core/fpmExplorer/index.html#/customElements/customSectionContent
Two steps are necessary.

1. Add a new section via the manifest. The template path should match your app namespace.

        "ObjectPage": {
          "type": "Component",
          "id": "ObjectPage",
          "name": "sap.fe.templates.ObjectPage",
          "viewLevel": 1,
          "options": {
            "settings": {
              "editableHeaderContent": false,
              "entitySet": "pdfFiles",
             "content": {
                "body": {
                  "sections" : {
                    "myCustomSection": {
                      "template": "sap.fe.core.fpmExplorer.customSectionContent.CustomSection",
                      "title": "{i18n>pdfSection}",
                      "position": {
                        "placement": "After",
                        "anchor": "StandardSection"
                      }
                    }
                  }
                }
              }
            }
          }
        }

2. Add the section content by defining a new fragment in the file CustomSection.fragment.xml

<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:macro="sap.fe.macros">
	<ScrollContainer
		height="100%"
		width="100%"
		horizontal="true"
		vertical="true">
		<FlexBox direction="Column" renderType="Div" class="sapUiSmallMargin">
			<PDFViewer source="{content}" title="{fileName}" height="1200px">
				<layoutData>
					<FlexItemData growFactor="1" />
				</layoutData>
			</PDFViewer>
		</FlexBox>
	</ScrollContainer>
</core:FragmentDefinition>

On the ObjectPage you will now have a new section containing the PDFViewer.

[ABAP] OData – GET_STREAM implementation to return a PDF

  METHOD /iwbep/if_mgw_appl_srv_runtime~get_stream.

* This method get's called when a media file is queried with $value. A binary stream will be returned.

    TRY.
        DATA(file_id) = VALUE zfile_id( it_key_tab[ name = 'file_id' ]-value ).
      CATCH cx_sy_itab_line_not_found.
        RETURN. " leave here when no file_id provided
    ENDTRY.
  
    DATA(ls_file) = get_file( file_id ) " read your file you want to return (if it's not yet a binary stream, convert it)

    DATA(ls_stream) = VALUE ty_s_media_resource( value     = ls_file-value
                                                 mime_type = ls_file-mimetype ). " in my case it's 'application/pdf'

    " necessary to display the filename instead of $value in the viewer title
    TRY.
        " create pdf object
        DATA(lo_fp)     = cl_fp=>get_reference( ).
        DATA(lo_pdfobj) = lo_fp->create_pdf_object( connection = 'ADC' ).
        lo_pdfobj->set_document( pdfdata = ls_stream-value ).
        " set title
        lo_pdfobj->set_metadata( VALUE #( title = ls_file-filename ) ).
        lo_pdfobj->execute( ).
        " get pdf with title
        lo_pdfobj->get_document( IMPORTING pdfdata = ls_stream-value ).

      CATCH cx_fp_runtime_internal
            cx_fp_runtime_system
            cx_fp_runtime_usage INTO DATA(lo_fpex).
    ENDTRY.

    copy_data_to_ref( EXPORTING is_data = ls_stream
                      CHANGING  cr_data = er_stream ).

    " necessary for the pdf to be opened inline instead of a download (also sets the filename when downloaded)
    /iwbep/if_mgw_conv_srv_runtime~set_header( VALUE #( name  = 'content-disposition'
                                                        value = |inline; filename={ ls_file-filename }| ) ).

  ENDMETHOD

Quick way to open a PDFViewer in your UI5 App:

			const pdfViewer = new PDFViewer()
			pdfViewer.setSource("/sap/opu/odata/ZMY_SEVICE" + my_path + "/$value")  // my_path could be something like this "/PdfSet('file_id')"
			pdfViewer.setTitle("My PDFViewer Title") // title of the popup, not the viewer
			pdfViewer.open()