SAP HR ESS-Personal Profile Picture not displayed

You are using HR ESS Personal Profile WebDynpro HRESS_A_PERSINFO and you can see picture being displayed in WebDynpro when accessing application within network. However, you cannot see picture when accessing application outside network.

In your system landscape a reverse proxy i.e. Web Dispatcher is used in the network. Web Dispatcher is reachable over internet and it redirects request over configured URL and Port to servers/services which are behind the firewall.

Picture which is displayed within Personal Profile Webdynpro are hosted using ContentServer service of SAP ERP server. Assuming everything else is configured right you can execute function module HRWPC_RFC_EP_READ_PHOTO_URI to see the URL of picture of an employee by passing their PERNR. URL being generated would look like below where myerpsys.com is internal URL or your content server.

http://myerpsys.com:1090//ContentServer/ContentServer.dll?get&pVersion=0047&contRep=AB&docId=005056A54E011ED78AD2175760508D…

To reach content server form outside network URL must contain name and port of the Web Dispatcher and not of your internal server name (myerpsys.com). There are a lots of topics on scn and also in sap notes on how to resolve this but none of them worked for me and I had to, in the end, put an enhancement in WebDynpro to generate URL and Port which is configured for Web Dispatcher and have it redirected to SAP ERP ContentServer service.

SAP Note 2465728 talk about adjusting entries in table HTTPURLLOC to generate Web Dispatcher URL and Port but no matter what is specified in this table makes no difference to URL generated. DB trace confirms that code does not look at table HTTPURLLOC while generating URL.

Resolution
The URL of content server images should point to the same as Web Dispatcher domain and protocol.

Follow steps below to fix this scenario:

Configure the /sap/bc/contentserver service and user authentication:SAP Note 685521 – Logon data for /sap/bc/contentserver service

If Web Dispatcher is using HTTPS Protocol: Enable HTTPS in MIME Content Repository: SAP Note 712330 – Accessing server content with HTTPS

Adjust the table HTTPURLLOC to call the URL at WebDispatcher host and port: https://help.sap.com/saphelp_nw70/helpdata/en/42/d547…
SAP Note 2465728

Another post on scn refer to same table.

Maintain an entry in the table HTTPURLLOC with following details:
protocol: https
host: <web despatcher host name>
port: <web despatcher port number>
check in SMICM
Note that
By default we generate URL to the actual backend system where the
content repository is setup. However if web despatcher is being used,
then corresponding settings needs to be done in the system.
check here HTTPURLLOC
https://archive.sap.com/discussions/thread/2059249

I am really curious to find out if someone had this working out-of-box by adjusting values in table HTTPURLLOC. In rest of blog I’ll explain what did worked for me or rather how I made it work.

Employee picture is displayed and uploaded using WebDynpro component HRESS_C_PER_PHOTO. URL to display picture is generated in method PROCESS_BEFORE_OUTPUT of COMPONENTCONTROLLER. Well, within method PROCESS_BEFORE_OUTPUT it make number of calls and eventually call end up in function module HRWPC_RFC_EP_READ_PHOTO_URI which generates the URL. URL is then placed into attribute IMAGE_URL of context node PHOTO. At the end of method PROCESS_BEFORE_OUTPUT of COMPONENTCONTROLLER URL is ready. This is where I put an post-exit method. To replace URL with new URL which use Web Dispatcher URL and Port which was given to me by one of Basis consultant. This port is used in Web Dispacther to redirect call to ContentServer.

Purpose of the code was to generate url

https://mywebdispacther.com:442//ContentServer/ContentServer.dll?get&pVersion=0047&contRep=AB&docId=005056A54E011ED78AD2175760508D…

instead of

http://myerpsys.com:1090//ContentServer/ContentServer.dll?get&pVersion=0047&contRep=AB&docId=005056A54E011ED78AD2175760508D…

Below post exit method read context node, get attribute PHOTO_URL of node and calls method zcl_hr_photo_redirect=>get_redirect_url where URL transformation takes place. If URL transformation is successful, which is returning parameter of method, then it replace new URL in context node’s attributes.

METHOD _pst_00o2tmsr60sq3asdkjaskdjkadjit1 . "Exit of PROCESS_BEFORE_OUTPUT (in ZENHANCEMENT_NAME )

  DATA lo_nd_photo TYPE REF TO if_wd_context_node.
  DATA lo_el_photo TYPE REF TO if_wd_context_element.
  DATA ls_photo TYPE wd_this->element_photo.
  DATA lv_image_url TYPE wd_this->element_photo-image_url.

  DATA lv_incoming_url    TYPE string.
  DATA lv_redirect_url TYPE string.
  DATA lv_success      TYPE boolean.

  lo_nd_photo = wd_context->get_child_node( name = wd_this->wdctx_photo ).

  IF lo_nd_photo IS INITIAL.
    RETURN .
  ENDIF.

  lo_el_photo = lo_nd_photo->get_element( ).

  IF lo_el_photo IS INITIAL.
    RETURN .
  ENDIF.

  lo_el_photo->get_attribute(
    EXPORTING
      name =  'IMAGE_URL'
    IMPORTING
      value = lv_image_url ).

  lv_incoming_url = lv_image_url .

  lv_success = zcl_hr_photo_redirect=>get_redirect_url(
    EXPORTING
      incoming_url    = lv_image_url
    IMPORTING
      ex_redirect_url = lv_redirect_url ).

  IF lv_success = abap_true .

    lv_image_url = lv_redirect_url .
    lo_el_photo->set_attribute(
      name =  'IMAGE_URL'
      value = lv_image_url ).

  ENDIF.

ENDMETHOD. 

Below is the class method where actual transformation takes place. Logic is based on the interface provided by SAP around table HTTPURLLOC.

CLASS ZCL_HR_PHOTO_REDIRECT DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

PUBLIC SECTION.

  CLASS-METHODS GET_REDIRECT_URL
    IMPORTING
      !INCOMING_URL TYPE STRING
      !IV_APPLICATION TYPE CSEQUENCE DEFAULT 'ZPHOTO'
    EXPORTING
      !EX_REDIRECT_URL TYPE STRING
    RETURNING
      VALUE(EX_SUCCESS) TYPE BOOLEAN .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.


CLASS ZCL_HR_PHOTO_REDIRECT IMPLEMENTATION.


* <SIGNATURE>----------------------------------------------------------------------------------+
* | Static Public Method ZCL_HR_PHOTO_REDIRECT=>GET_REDIRECT_URL
* +--------------------------------------------------------------------------------------------+
* | [--->] INCOMING_URL                   TYPE        STRING
* | [--->] IV_APPLICATION                 TYPE        CSEQUENCE (default ='ZPHOTO')
* | [<---] EX_REDIRECT_URL                TYPE        STRING
* | [<-()] EX_SUCCESS                     TYPE        BOOLEAN
* +----------------------------------------------------------------------------------</SIGNATURE>
  METHOD get_redirect_url.

    DATA : lt_initial_split TYPE TABLE OF string,
           ls_initial_split TYPE string.

    FIELD-SYMBOLS : <lfs_string> TYPE string .

    DATA in_protocol TYPE string .
    DATA in_domain   TYPE string .
    DATA in_port     TYPE string .

    DATA out_protocol TYPE string .
    DATA out_domain   TYPE string .
    DATA out_port     TYPE string .

    DATA def_host TYPE string .
    DATA url_part            TYPE string.
    DATA host                TYPE string.
    DATA port                TYPE string.

*    FIELD-SYMBOLS :  <ls_urltoken>  TYPE ty_token .

    "by default its false
    ex_success = abap_false .

    IF incoming_url IS INITIAL .
      RETURN .
    ENDIF.

    "split url into fields
    SPLIT incoming_url AT '//' INTO TABLE lt_initial_split .

*   Sanity checks
    IF lt_initial_split IS INITIAL .
      RETURN .
    ENDIF.

    IF lines( lt_initial_split ) < 3 .
      RETURN .
    ENDIF.

*   Get incoming protocol
    READ TABLE lt_initial_split INTO ls_initial_split INDEX 1 .
    in_protocol = ls_initial_split .
    REPLACE ':' IN in_protocol WITH space .
    CONDENSE in_protocol .
    TRANSLATE in_protocol TO UPPER CASE .

*   Get domain and port
    READ TABLE lt_initial_split INTO ls_initial_split INDEX 2 .
    FIND ':' IN ls_initial_split .
    IF sy-subrc = 0 .
      SPLIT ls_initial_split AT ':' INTO in_domain in_port .
      TRANSLATE in_domain TO UPPER CASE .
    ELSE.
      in_domain = ls_initial_split  .
    ENDIF.

    "get default host to indentify missing entry in HTTPURLOC
    url_part = cl_http_server=>get_location(
      IMPORTING
          host                = def_host ).
    TRANSLATE def_host TO UPPER CASE .

    "get mapping
    url_part = cl_http_server=>get_location(
      EXPORTING
        protocol            = in_protocol
        application         = iv_application
        for_domain          = in_domain
      IMPORTING
        host                = host
        port                = port ).

    TRANSLATE host TO UPPER CASE .

    IF def_host = host .
      "missing mapping
      RETURN .
    ENDIF.

    "extract mapped protocol, domain and port
    FIND ':' IN host .
    IF sy-subrc = 0 .
      SPLIT host AT ':' INTO out_protocol out_domain  .
      REPLACE '//' IN out_domain WITH space .
      CONDENSE out_domain .
    ELSE.
      out_protocol = in_protocol .
      out_domain = host .
    ENDIF.
    out_port = port .

    "replace in procotol, domain and port with mapped values
    READ TABLE lt_initial_split ASSIGNING <lfs_string> INDEX 1 .
    REPLACE in_protocol IN <lfs_string> WITH out_protocol IGNORING CASE .
    TRANSLATE <lfs_string> TO LOWER CASE .

    READ TABLE lt_initial_split ASSIGNING <lfs_string> INDEX 2 .
    FIND ':' IN host .
    IF sy-subrc = 0 .
      REPLACE in_domain IN <lfs_string> WITH out_domain IGNORING CASE .
      REPLACE in_port IN <lfs_string> WITH out_port IGNORING CASE .
      TRANSLATE <lfs_string> TO LOWER CASE .
    ELSE.
      REPLACE in_domain IN <lfs_string> WITH out_domain .
      TRANSLATE <lfs_string> TO LOWER CASE .
    ENDIF.

    "create url using mapped values
    CLEAR url_part .
    LOOP AT lt_initial_split INTO ls_initial_split .
      IF url_part IS INITIAL .
        url_part = ls_initial_split .
      ELSE.
        CONCATENATE url_part ls_initial_split INTO url_part SEPARATED BY '//' .
      ENDIF.
    ENDLOOP.

    IF url_part IS NOT INITIAL .
      ex_redirect_url = url_part .
      ex_success = abap_true .
    ENDIF.

  ENDMETHOD.
ENDCLASS. 

For above code to work you need to have this entry in HTTPURLLOC table.

SORT_KEY PROTOCOL APPLICATN FOR_DOMAIN HOST PORT
100 HTTP ZPHOTO MYERPSYS.COM HTTPS://MYWEBDISPACHTHER.COM 442

At this point you will notice in Web Dynpro that picture url will now contain new URL. However for this new URL to actually reach your internal server you would need Forwarding Rule in Web Dispatcher (Basis Consultant).