Skip to Content
Technical Articles

Fiori Elements List report – display and upload images (part 1)

Recently, in one custom application I had the opportunity of creating a custom app displaying a list of SKUs (articles /materials) using Fiori Elements List report floorplan. The custom app (sample shown below) had a feature of uploading and displaying the images of the articles which was developed using annotation and a redefinition of the data provider class which I thought worth sharing in case if someone needs to implement a similar functionality.

My Preparation:

“Do in Rome SAP as Romans SAPers (SAPients or .. alright, call them however you like) do”

As it seemed to be quite a common requirement and SAP already has it in some of their standard Fiori elements apps, I explored how standard SAP has achieved this. One of the best way to explore standard apps (if one does not have a custom pre-configured SAP on-prem or cloud access) is to spin up a trial SAP solution instance from SAP Cloud Appliance Library on preferred cloud provider (AWS, Azure or GCP).

Note:ABAP examples in this blog post are based on ABAP Programming Model for SAP Fiori, i.e., CDS views with BOPF enabled and subsequent implementation of OData service with Reference data source approach (ABAP 7.51 and above). The OData service is registered on SAP Netweaver Gateway system and consumed by the Fiori app.

Implementation:

The implementation of displaying and uploading is described below in two sections:

  1. Build backend data model

  2. UI coding

1. Build backend data model

The fiori app is developed using Fiori Elements List report template. Since SAP Netweaver Gateway supports media type entity, we need the image data to be provided by the backend as an entity type with ‘Media’ type annotation (m:HasStream=”true”).

Below is an illustration of the entity data model:

My implementation was based on CDS views, but a code-based implementation of data model in SEGW OData service builder will also do.

The primary entity is bound to the smart table of the list report which allows us to display the fields like article number(matnr), article description [among others] in the fiori list report app. The primary entity Z_C_SKUIMAGEType is associated to the image source entity (Z_I_IMAGEType) which has the ‘mime_type’ property to enable this entity to be represented as stream data over gateway.

 

Alright! Enough of the explanation, let’s get right to the implementation steps.

a. CDS views: Create the CDS views as shown below.

Note: the code snippets are sample only, adapt appropriately to suit your own requirements [e.g., replace/remove <namespace> with your own namespace.]

We are using two CDS views and accordingly two entity types will be generated as illustrated above.

CDS 1 : ZCDS_C_SKUIMAGE


.....

@ObjectModel:
{
 updateEnabled: true
}

define view ZCDS_C_SKUIMAGE
  as select from z<namespace>_cat_sku as CatalogSku
  inner join makt as SKUDesc 
  on ( CatalogSku.matnr = SKUDesc.matnr )

  association [1..*] to ZCDS_I_IMAGE as _Image 
  on ( CatalogSku.matnr = _Image.CatalogSKU )
{

  key  CatalogSku.matnr          as CatalogSKU,
       CatalogSku.ctlg_itm_key   as CatalogItmKey,
       SKUDesc.maktx                as CatalogSKUDesc,
       _Image,
       //creating URL
       concat(replace('/sap/opu/odata/sap/Z<namespace>_IMAGES_SRV/ZCDS_I_IMAGE(CatalogSKU=''matnr'',', 'matnr', CatalogSku.matnr),
       replace('image_key=imageKey)/$value', 'imageKey',
       cast (_Image.image_key as abap.char( 20 )))) as GetUrl
}
where SKUDesc.spras = $session.system_language
  and CatalogSku.inactive = ''

CDS 2: ZCDS_I_IMAGE

....

@ObjectModel:{
 createEnabled: true,
 updateEnabled: true,
 deleteEnabled: true

}

define view ZCDS_I_IMAGE 
as select from z<namespace>_cat_sku as Image
{
    key Image.matnr         as CatalogSKU,     
    key Image.image_key     as image_key,  
        Image.ctlg_itm_key  as CatalogItmKey,
        Image.image_name,
        Image.mime_type,
       @Semantics.imageUrl: true
        Image.image_url as ImageUrl,
} where Image.inactive = ''

As can be seen, the second CDS view is based on a transparent table Z<namespace>_cat_sku which stores the information of article image like image_key, mime_type, image_url for materials.

An image key is unique for each image uploaded for an article. One can choose to use GUID or similar approach to make it unique, however to make the subject matter simple, the example just uses a number generated from a number range object.

Also in the first CDS view, we have a logic to populate the property ‘GetUrl’. Do not stress on the exact logic as shown above, the idea is that the property will be bound to the Image column in fiori list report app. Since the image data is served over gateway, we need to have a URL that points to the image media entity with $value OData query option. A sample URL is as below:

"/sap/opu/odata/sap/ZFIAP_SOS_IMAGES_SRV/ZCDS_I_SOS_IMAGE(CatalogSKU='000000000600000106',image_key=0001)/$value".

 

Later, we shall see that the URL above will fetch the actual image content as a stream data as we shall implement some logic in /iwbep/if_mgw_appl_srv_runtime~get_stream method of DPC_EXT class.

b. OData service : Now in SEGW transaction code, import the above CDS entity with association as reference data source to create the OData service. Below is what resulted after generating the objects in SEGW:

c. Redefine MPC_EXT class: Once the runtime artefacts are generated from SEGW tcode, redefine method DEFINE of MPC_EXT class.

METHOD define.

    super->define( ).

    DATA:
      lr_entity   TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
      lr_property TYPE REF TO /iwbep/if_mgw_odata_property.

    lr_entity = model->get_entity_type( iv_entity_name = 'ZCDS_I_IMAGEType' ).

*  specifying mimetype property of media set
    IF lr_entity IS BOUND.
* setting a data object as media type means that it gets a special semantic by having a url *  and allows streaming etc.
      lr_entity->set_is_media( ).
      lr_property = lr_entity->get_property( iv_property_name = 'mime_type' ).
      lr_property->set_as_content_type( ).


    ENDIF.

  ENDMETHOD.

d. Redefine DPC_EXT class GET_STREAM method: Redefine method /iwbep/if_mgw_appl_srv_runtime~get_stream of DPC_EXT class and add some logic to get the image file content.

METHOD /iwbep/if_mgw_appl_srv_runtime~get_stream.

DATA: ls_key          TYPE /iwbep/s_mgw_name_value_pair,
          ...
          lv_matnr        TYPE matnr,
          ls_image        TYPE z<namespace>_cat_sku,
          ls_lheader      TYPE ihttpnvp,
          ls_stream       TYPE ty_s_media_resource,
          lv_filename     TYPE string,
          lt_tab          TYPE solix_tab,
          ls_tab          TYPE solix,
          lv_leng         TYPE i,
          lv_buffer       TYPE xstring,
          lv_imageurl     TYPE zzimage_url.

CASE iv_entity_name.
      WHEN 'ZCDS_I_IMAGEType'.

*----------IMPORTANT READING -------------------------------------------*
*Get the key field values of the entity and retrive the image file path
*For simplification, we are showing the image storage is in 
*application server directory, but adapt to your own chosen storage type
*(content repository etc.)
*-----------------------------------------------------------------------*
...

   CALL FUNCTION 'CONVERSION_EXIT_MATN1_INPUT'
            EXPORTING
              input        = ls_key-value
            IMPORTING
              output       = lv_matnr
            EXCEPTIONS
              length_error = 1
              OTHERS       = 2.
          IF sy-subrc <> 0.
            CLEAR lv_matnr.
          ENDIF.
*         Get the image details from catalog AKu table
          SELECT SINGLE * FROM z<namespace>_cat_sku
            INTO ls_image
            WHERE matnr = lv_matnr.
          IF sy-subrc = 0.
*           Open the application server file and read the file contents
            OPEN DATASET ls_image-image_url FOR INPUT IN BINARY MODE.
            IF sy-subrc = 0.
              DO.
                READ DATASET ls_image-image_url INTO ls_tab.
                IF sy-subrc = 0.
                  APPEND ls_tab TO lt_tab.
                  CLEAR ls_tab.
                ELSE.
                  EXIT.
                ENDIF.
              ENDDO.
              CLOSE DATASET ls_image-image_url.
            ENDIF.
          ENDIF.

*         Convert the binary file content to rawstring
          CALL FUNCTION 'SCMS_BINARY_TO_XSTRING'
            EXPORTING
              input_length = ls_image-file_size
            IMPORTING
              buffer       = lv_buffer
            TABLES
              binary_tab   = lt_tab
            EXCEPTIONS
              failed       = 1
              OTHERS       = 2.
          IF sy-subrc = 0.
*            Pass the rawstring data to image stream
            ls_stream-value = lv_buffer.
            ls_stream-mime_type = ls_image-mime_type.

            lv_filename = ls_image-image_name.
            lv_filename = escape( val = lv_filename
                                  format = cl_abap_format=>e_url ).
            ls_lheader-name = 'Content-Disposition'.
            ls_lheader-value = |inline; filename="{ lv_filename }"|.
            set_header( is_header = ls_lheader ).

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

          ENDIF.
        ENDIF.

      WHEN OTHERS.
    ENDCASE.

e. Test OData service in GW client:

Once registered in gateway system, test your OData service, gateway client (/IWFND/GW_CLIENT tcode)

 

2. UI coding:

Create the list report app using template in SAP Web IDE and apply appropriate annotations to include the columns as per requirement.

For image column, add annotation “UI.IsImageUrl” to property ‘GetUrl’ which is bound to the Image column. Example annotations shown below:

<Annotations Target="Z<namespace>_IMAGES_SRV.ZCDS_C_SKUIMAGEType">
	<Annotation Term="UI.LineItem">
		<Collection>
			<Record Type="UI.DataField">
				<PropertyValue Property="Value" Path="GetUrl"/>
				<Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/>
				<PropertyValue Property="Label" String="{@i18n&gt;IMAGE}"/>
			</Record>
			<Record Type="UI.DataField">
				<PropertyValue Property="Value" Path="CatalogSKU"/>
			</Record>
			<Record Type="UI.DataField">
				<PropertyValue Property="Value" Path="CatalogSKUDesc"/>
			</Record>
		</Collection>
	</Annotation>
<Annotations Target="Z<namespace>_IMAGES_SRV.ZCDS_C_SKUIMAGEType/GetUrl">
	<Annotation Term="UI.IsImageUrl"/>
</Annotations>

Run the app, the image will be rendered nicely!

Following are a few snaps of the backend service GET requests from ‘Network’ tab of developer’s tool of chrome browser.

Conclusion:

In this blog post, we saw how to display image in a column of a fiori list report using media type entity and UI annotation. In the next part, we shall discuss how to upload image for each row in the list report on click of the button in another column.

4 Comments
You must be Logged on to comment or reply to a post.
  • Hi Sumit,

    This is really very helping article. Thanks for sharing this.

    Can you please also share where the images were uploaded in this case in sap? Were they uploaded on application directory in SAP.

     

    I am asking because, usually, we upload in SE78.

  • Thanks Vivek Sharma for your feedback.

    In this blog post, the storage was application server directory. But for other types of content repository, the backend code in GET_STREAM needs to be adapted accordingly. I have included this in the code sample also.

    *----------IMPORTANT READING -------------------------------------------*
    *Get the key field values of the entity and retrive the image file path
    *For simplification, we are showing the image storage is in 
    *application server directory, but adapt to your own chosen storage type
    *(content repository etc.)
    *-----------------------------------------------------------------------*

    Regards,

    Sumit