Skip to Content
Author's profile photo Harini Gunabalan

Adding Business Actions to any CMIS ABAP repository for SAP Document Center

Hi Everyone,

In this blog, we will look into how custom business actions can be enabled in SAP Document Center for SAP DMS Repositories. SAP Document Center provides basic actions to handle documents such as Download, Delete, Rename, Copy, Paste, Check-in, and Checkout. However there could be some actions that are specific to business needs of a customer such as changing the status associated with a document, forwarding documents for approval and so forth. SAP Document Center provides us a possibility to include these custom actions to full fill specific business needs. The custom actions are henceforth referred to as business actions.

In a recent use-case, one of our customers wanted to store architectural images of certain plot designs. They were interested in performing two business actions: first was to open their internal plot management portal from the document, and another action was to approve or reject the document, based on the details in the tool.  They had plans of executing these tasks several thousand times over the next few years. The workflow process of approval/rejection had to be performed in their SAP ERP on-premise systems. So in this blog, let us look into how they can achieve this process from SAP Document Center.

Pre-requisite: A CMIS complaint ABAP Repository that has been integrated as a Corporate Repository with SAP Document Center (Reference guide to set this up). Knowledge on CMIS Workbench, and Secondary types would be beneficial.

From our customer requirements perspective, we classified the business action into the following 2 types:

  • Business action that invokes a URL to open a web page where user can interact with the application to make further actions
  • Business action that triggers any remote backend call to the ABAP repository and show result of the operation in SAP Document Center

This blog is structured into two parts. In the first part, we will look into how our customer enabled a business action that invokes a URL and in the latter part we will show how they implemented a Business action that invokes a backend call. For adding a business action, a secondary CMIS type needs to be created and an extension action should be attached to it. In this blog, the backend repository is in ABAP, hence we implement the business actions in ABAP. A similar implementation can also be performed in Microsoft Sharepoint, Alfresco or any other CMIS repository implementation. We can implement the business actions either at the folder level or at the document level (explained in step 7).

Adding a Business Action to invoke a URL from a repository:

To add this business action we need to add a few lines of code shown in the following steps:

1. Identify the repository where the business actions has to be supported. In SAP Document Center admin UI, we can see the CMIS Repository ID of all the corporate repositories; note down the CMIS Repository ID of the identified repository as shown in the below image.

Go to transaction code SM30 and navigate to the contents of view CMISD_SERVICE. Match the repository id column with the CMIS Repository ID from the admin UI and retrieve the corresponding class name of the repository implementation. If it is a standard SAP delivered class, then a custom Z class should  be created inheriting the original standard class. You may need to add this custom class as another entry in the view in SM30 and also add it in the admin UI to be able to use it in SAP Document Center. If the class was a custom created class as shown in the image below, then proceed to Step 2.

2. Go to SE24 transaction code and edit the custom class to which the business action should be added. This class extends the abstract class, CL_CMIS_ABSTRACT_SERVICE. The custom class is referred as <YOUR_CLASS_NAME> in the code snippets below. Define a global constant with a value, say, “MYBUSINESSACTION”, which is referred to as <YOUR_GLOBAL_CONSTANT> in the code snippets below:

Attribute Level Visibility Typing Associated Type Description Initial Value
GC_BA_ID_FOR_URL Constant Private Type STRING ‘MYBUSINESSACTION’

 

3. Define a private method GET_BUSINESS_ACTIONS_TYPES as per the below signature.  The logic to define the additional properties and corresponding extension definitions for all the business actions are implemented here.

Parameter Type Pass Value Optional Typing Method Associated Type Default Value Description
IV_TYPE_ID Importing Type CMIS_ID
ES_BA_TYPE Exporting Type CMIS_S_TYPE_DEFINITION
METHOD GET_BUSINESS_ACTIONS_TYPES.

    DATA:   ls_prop_def     TYPE cmis_s_property_definition,
            lv_ba_desc      TYPE string,
            action_ext      TYPE cmis_s_extension,
            ext_name        TYPE cmis_s_extension,
            ext_button      TYPE cmis_s_extension,
            ext_position    TYPE cmis_s_extension,
            ext_version     TYPE cmis_s_extension.

* Create a secondary type
      lv_ba_desc = 'MyDescription'.    "Secondary type description

      es_ba_type-id                          = gc_ba_id_for_url.
      es_ba_type-local_name                  = lv_ba_desc.
      es_ba_type-local_namespace             = 'com.sap.sdc'.
      es_ba_type-display_name                = lv_ba_desc.
      es_ba_type-query_name                  = gc_ba_id_for_url.
      es_ba_type-description                 = lv_ba_desc.
      es_ba_type-base_id                     = 
                         cl_cmis_constants=>base_type_id-cmis_secondary.
      es_ba_type-parent_id                   = 
                         cl_cmis_constants=>base_type_id-cmis_secondary.
      es_ba_type-creatable                   = abap_false.
      es_ba_type-fileable                    = abap_false.
      es_ba_type-queryable                   = abap_false.
      es_ba_type-full_text_indexed           = abap_false.
      es_ba_type-included_in_supertype_query = abap_false.
      es_ba_type-controllable_policy         = abap_false.
      es_ba_type-controllable_acl            = abap_false.
      es_ba_type-type_mutability-create      = abap_false.
      es_ba_type-type_mutability-delete      = abap_false.
      es_ba_type-type_mutability-update      = abap_true.

      CLEAR ls_prop_def.

*Create property and extension for corresponding business action
      ls_prop_def-id                   = 'ACTION:OpenURL'.
      ls_prop_def-local_name           = 'ACTION:OpenURL'.
      ls_prop_def-local_namespace      = 'com.sap.dms'.
      ls_prop_def-display_name         = 'OpenURL'. 
      ls_prop_def-query_name           = 'ACTION:OpenURL'.
      ls_prop_def-description          = 'OpenURL'. 

      ls_prop_def-property_type        = 
                           cl_cmis_constants=>property_type-string.
      ls_prop_def-cardinality          = 
                           cl_cmis_constants=>cardinality-single.
      ls_prop_def-updatability         = 
                           cl_cmis_constants=>updatability-read_write.
      ls_prop_def-inherited            = abap_false.
      ls_prop_def-required             = abap_false.
      ls_prop_def-queryable            = abap_false.
      ls_prop_def-orderable            = abap_false.
      ls_prop_def-openchoice           = abap_false.

      action_ext-name                  = 'mcm:action'.
      action_ext-transport_id          = 'mcm:action'.


      ext_name-name                    = 'localizedDisplayName'.
      ext_name-transport_id            = '1'.
      ext_name-transport_parent_id     = action_ext-transport_id.
      ext_name-value                   = 'OpenURL'.

      ext_button-name                  = 'renderAsButton'.
      ext_button-transport_id          = '2'.
      ext_button-transport_parent_id   = action_ext-transport_id.
      ext_button-value                 = 'true'.

      ext_position-name                = 'positionWeight'.
      ext_position-transport_id        = '3'.
      ext_position-transport_parent_id = action_ext-transport_id.
      ext_position-value               = '1'.


      APPEND action_ext   TO ls_prop_def-extensions.
      APPEND ext_name     TO ls_prop_def-extensions.
      APPEND ext_button   TO ls_prop_def-extensions.
      APPEND ext_position TO ls_prop_def-extensions.
      APPEND ls_prop_def  TO es_ba_type-property_definitions.

  ENDMETHOD.


4. Fetch the definition of the secondary type added for business actions while fetching the type definitions defined for the repository by enhancing the implementation of the IF_CMIS_SERVICE~GET_TYPE_DEFINITION method.

 METHOD if_cmis_service~get_type_definition.
  IF iv_type_id = gc_ba_id_for_url.
    CALL METHOD get_business_actions_types
      EXPORTING
        iv_type_id = iv_type_id
      IMPORTING
        es_ba_type = es_type.
  ELSE.
    CALL METHOD super->if_cmis_service~get_type_definition
      EXPORTING
        iv_repository_id = iv_repository_id
        iv_type_id       = iv_type_id
      IMPORTING
        es_type          = es_type.
  ENDIF.
ENDMETHOD.

5. Fetch the definition of the secondary type added for business actions while fetching the type children by enhancing the implementation of the private method GET_SECONDARY_OBJECTTYPES.

METHOD if_cmis_service~get_type_children.
  CALL METHOD super->if_cmis_service~get_type_children
    EXPORTING
      iv_repository_id            = iv_repository_id
      iv_type_id                  = iv_type_id
      iv_include_prop_definitions = iv_include_prop_definitions
      iv_max_items                = iv_max_items
      iv_skip_count               = iv_skip_count
    IMPORTING
      es_types                    = es_types.
 
  IF iv_type_id EQ cl_cmis_constants=>base_type_id-cmis_secondary.
    DATA ls_type TYPE cmis_s_type_definition.
    get_business_actions_types(
      EXPORTING
        iv_type_id = iv_type_id
      IMPORTING
        es_ba_type = ls_type " CMIS Type Definition
      ).
    APPEND ls_type TO es_types-types.
  ENDIF.
ENDMETHOD.

6. The cmis object definition has to be enhanced in order to supply the additional property, which carries the business context and the action to be carried out. Create an additional method ENRICH_CMIS_OBJECT with the below signature.

Parameter Type Pass Value Optional Typing Method Associated Type Default Value Description
CS_CMIS_OBJECT Changing Type CMIS_S_OBJECT
METHOD enrich_cmis_object.
  FIELD-SYMBOLS <ls_property> TYPE cmis_s_property.
 
  READ TABLE cs_cmis_object-properties-properties ASSIGNING <ls_property> WITH KEY id = 'cmis:secondaryObjectTypeIds'.
  IF sy-subrc = 0.
    DATA ls_property_value TYPE cmis_s_property_value.
    ls_property_value-string_value = gc_ba_id_for_url.
    APPEND ls_property_value TO <ls_property>-value.
 
    APPEND INITIAL LINE TO cs_cmis_object-properties-properties ASSIGNING <ls_property>.
    cl_cmis_object_factory=>create_string_prop_single(
      EXPORTING
        iv_property_id = gc_ba_id_for_url && ':OpenURL'
        iv_value       = 'https://www.sap.com'
      RECEIVING
        rs_property    = <ls_property>
    ).
  ENDIF.
ENDMETHOD.

7. Redefine the methods IF_CMIS_SERVICE~GET_OBJECT and IF_CMIS_SERVICE~GET_CHILDREN to call the enrich method. Currently these calls are not based on any condition. But conditions can be introduced for example, the business action should be included only for files and not folders or the business action should be applied to files of a certain type and so forth.

METHOD if_cmis_service~get_object.
  CALL METHOD super->if_cmis_service~get_object
    EXPORTING
      iv_repository_id             = iv_repository_id
      iv_object_id                 = iv_object_id
      iv_include_acl               = iv_include_acl
      iv_filter                    = iv_filter
      iv_include_relationships     = iv_include_relationships
      iv_rendition_filter          = iv_rendition_filter
      iv_include_allowable_actions = iv_include_allowable_actions
    IMPORTING
      es_object                    = es_object.
 
  enrich_cmis_object( changing cs_cmis_object = es_object ).
ENDMETHOD.

 

METHOD if_cmis_service~get_children.
  CALL METHOD super->if_cmis_service~get_children
    EXPORTING
      iv_repository_id             = iv_repository_id
      iv_folder_id                 = iv_folder_id
      iv_max_items                 = iv_max_items
      iv_skip_count                = iv_skip_count
      iv_order_by                  = iv_order_by
      iv_filter                    = iv_filter
      iv_include_relationships     = iv_include_relationships
      iv_rendition_filter          = iv_rendition_filter
      iv_include_allowable_actions = iv_include_allowable_actions
      iv_include_path_segment      = iv_include_path_segment
    IMPORTING
      es_children                  = es_children.
 
  FIELD-SYMBOLS <ls_object_in_folder> TYPE cmis_s_object_in_folder.
 
  LOOP AT es_children-objects_in_folder ASSIGNING <ls_object_in_folder>.
    enrich_cmis_object( changing cs_cmis_object = <ls_object_in_folder>-object ).
  ENDLOOP.
ENDMETHOD.

That’s it. Now our Customer can invoke the business action to open a URL from the corresponding repository in the web-app as shown below:

On clicking OpenURL Button, the link provided in the enrich_cmis_object method will be invoked.

The action can also be seen in the properties of the file as below:

The secondary type, ‘MYBUSINESSACTION’ and the action ”ACTION:OpenURL’ can be found in the CMIS workbench by clicking on the Types button as shown:

Now moving on to the second part, let us see how our customer included another business action to approve the plot design documents. This requires triggering a backend code which could be an RFC, a class method or a function module in the ABAP repository. In the example shown below, we invoke another class method.

Adding a Business Action to trigger ABAP backend code:

8. Add the following lines of code to the GET_BUSINESS_ACTIONS_TYPES method that we created in Step 3.

METHOD GET_BUSINESS_ACTIONS_TYPES.

......................... 
...Code added in Step 3...
.........................

DATA:   lt_localization TYPE cmis_t_map,
        ls_locale       TYPE cmis_s_key_value.

*Create property and attach extension for the second business action
    CLEAR ls_prop_def.

    ls_prop_def-id                = 'ACTION:Approve'.
    ls_prop_def-local_name        = 'ACTION:Approve'.
    ls_prop_def-local_namespace   = 'com.sap.dms'.
    ls_prop_def-display_name      = 'Approve'. 
    ls_prop_def-query_name        = 'ACTION:Approve'.
    ls_prop_def-description       = 'Approve'.
    ls_prop_def-property_type     = 
                          cl_cmis_constants=>property_type-string.
    ls_prop_def-cardinality       = 
                          cl_cmis_constants=>cardinality-single.
    ls_prop_def-updatability      = 
                          cl_cmis_constants=>updatability-read_write.
    ls_prop_def-inherited         = abap_false.
    ls_prop_def-required          = abap_false.
    ls_prop_def-queryable         = abap_true.
    ls_prop_def-orderable         = abap_true.
    ls_prop_def-openchoice        = abap_true.

    action_ext-name               = 'mcm:action'.
    action_ext-transport_id       = 'mcm:action'.

    ext_name-name                 = 'localizedDisplayName'.
    ext_name-transport_id         = '1'.
    ext_name-transport_parent_id  = action_ext-transport_id.

*Business Action name that is displayed on the Web UI can be changed 
*according to the location/language settings
    CLEAR lt_localization.
    CLEAR ls_locale.
    ls_locale-id    = 'default'.
    ls_locale-value = 'Approve'.
    APPEND ls_locale TO lt_localization.

    CLEAR ls_locale.
    ls_locale-id    = 'en'.
    ls_locale-value = 'Approve'.
    APPEND ls_locale TO lt_localization.

    CLEAR ls_locale.
    ls_locale-id    = 'de'.
    ls_locale-value = 'Genehmigen'.
    APPEND ls_locale TO lt_localization.

    ext_name-attributes              = lt_localization.
    ext_name-value                   = 
           '{"default": "Approve", "en":"Approve",' &&
             '"en-US":"Approve","de":"Genehmigen"}'. 
* Note that the default key is mandatory

    ext_button-name                  = 'renderAsButton'.
    ext_button-transport_id          = '2'.
    ext_button-transport_parent_id   = action_ext-transport_id.
    ext_button-value                 = 'true'.

    ext_position-name                = 'positionWeight'.
    ext_position-transport_id        = '3'.
    ext_position-transport_parent_id = action_ext-transport_id.
    ext_position-value               = '2'.

    ext_version-name                 = 'version'.
    ext_version-transport_id         = '4'.
    ext_version-transport_parent_id  = action_ext-transport_id.
    ext_version-value                = '1.0'.


    APPEND action_ext   TO ls_prop_def-extensions.
    APPEND ext_name     TO ls_prop_def-extensions.
    APPEND ext_button   TO ls_prop_def-extensions.
    APPEND ext_position TO ls_prop_def-extensions.
    APPEND ext_version  TO ls_prop_def-extensions.
    APPEND ls_prop_def  TO es_ba_type-property_definitions.
ENDMETHOD.

9. Enhance the ENRICH_CMIS_OBJECT method to support the new property definition. Add a new property.

METHOD enrich_cmis_object.
  FIELD-SYMBOLS <ls_property> TYPE cmis_s_property.
 
  READ TABLE cs_cmis_object-properties-properties ASSIGNING 
         <ls_property> WITH KEY id = 'cmis:secondaryObjectTypeIds'.
  IF sy-subrc = 0.
    DATA ls_property_value TYPE cmis_s_property_value.
    ls_property_value-string_value = gc_ba_id_for_url.
    APPEND ls_property_value TO <ls_property>-value.
 
    APPEND INITIAL LINE TO cs_cmis_object-properties-properties 
                                         ASSIGNING <ls_property>.
    cl_cmis_object_factory=>create_string_prop_single(
      EXPORTING
        iv_property_id = gc_ba_id_for_url && ':OpenURL'
        iv_value       = 'https://www.sap.com'
      RECEIVING
        rs_property    = <ls_property>
    ).
 
*Begin of additional code
    APPEND INITIAL LINE TO cs_cmis_object-properties-properties 
                                         ASSIGNING <ls_property>.
    cl_cmis_object_factory=>create_string_prop_single(
      EXPORTING
        iv_property_id = gc_ba_id_for_url && ':Approve'
        iv_value       = space
      RECEIVING
        rs_property    = <ls_property>
    ).
*End of additional code
  ENDIF.
ENDMETHOD.

10. We will set the DIR status (DIR is a Document Info Record that contains the metadata of a Document in SAP DMS) that backs the file displayed. Create a new method SET_DOCUMENT_STATUS with the following signature. Add the exception type CX_CMIS_NOT_SUPPORTED to the method definition as well.

Parameter Type Pass Value Optional Typing Method Associated Type Default Value Description
IV_DOKAR Importing Type DOKAR
IV_DOKNR Importing Type DOKNR
IV_DOKTL Importing Type DOKTL_D
IV_DOKVR Importing Type DOKVR
IV_DOKST Importing Type DOKST
METHOD set_document_status.
  DATA : ls_bapi_msg   TYPE bapiret2,
         lv_return_msg TYPE string.
 
  CALL FUNCTION 'BAPI_DOCUMENT_SETSTATUS'
    EXPORTING
      documenttype    = iv_dokar
      documentnumber  = iv_doknr
      documentpart    = iv_doktl
      documentversion = iv_dokvr
      statusextern    = iv_dokst
      statusintern    = iv_dokst
*     STATUSLOG       = 'Updated'
    IMPORTING
      return          = ls_bapi_msg.
 
  IF ls_bapi_msg-type EQ 'E' OR ls_bapi_msg-type EQ 'W' 
                  OR ls_bapi_msg-type EQ 'A'.
    CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
    CONCATENATE ls_bapi_msg-type  ls_bapi_msg-message INTO 
                            lv_return_msg SEPARATED BY space.
    RAISE EXCEPTION TYPE cx_cmis_not_supported
      EXPORTING
        message_text = lv_return_msg.
  ELSE.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
  ENDIF.
ENDMETHOD.
11. Introduce the call to the method in the IF_CMIS_SERVICE~UPDATE_PROPERTIES method to perform the call.
METHOD if_cmis_service~update_properties.
  FIELD-SYMBOLS <ls_property> TYPE cmis_s_property.
 
  READ TABLE is_properties-properties WITH KEY id = 'ACTION:Approve' 
             ASSIGNING <ls_property>.
 
  IF sy-subrc = 0.
    " Sample backend code invocation
    DATA ls_file_id    TYPE doc_s_file_id.
    DATA lv_doc_status TYPE bapi_doc_draw-statusintern.
    DATA lv_len        TYPE i.
 
    lv_len = strlen( iv_object_id ).
 
    IF lv_len > 33.
      ls_file_id = iv_object_id.
      lv_doc_status ='Z4'. "update the document status to approved.
 
      CALL METHOD me->set_document_status
        EXPORTING
          iv_dokar = ls_file_id-dokar
          iv_doknr = ls_file_id-doknr
          iv_doktl = ls_file_id-doktl
          iv_dokvr = ls_file_id-dokvr
          iv_dokst = lv_doc_status.
    ENDIF.
 
    RETURN.
  ENDIF.
 
  CALL METHOD super->if_cmis_service~update_properties
    EXPORTING
      iv_repository_id = iv_repository_id
      iv_object_id     = iv_object_id
      is_properties    = is_properties
      iv_change_token  = iv_change_token
    IMPORTING
      ev_change_token  = ev_change_token
      ev_object_id     = ev_object_id.
ENDMETHOD.

Yes, that is it. Similarly another business action, perhaps to reject a document, could be added by repeating steps 8 to 11. Now our customer can open a URL or approve a document based on the backend functionality as shown below:

Again by clicking on “Types” button in the CMIS workbench we can view the second action for Approve appended to the same “MYBUSINESSACTION” secondary type.

By clicking on “Save Type Definition” button on the top we can save the JSON schema as shown.

"ACTION:OpenURL":{  
     "maxLength":0,
     "id":"ACTION:OpenURL",
     "localName":"ACTION:OpenURL",
     "localNamespace":"com.sap.dms",
     "displayName":"Open a URL",
     "queryName":"ACTION:OpenURL",
     "description":"Open a URL",
     "propertyType":"string",
     "cardinality":"single",
     "updatability":"readwrite",
     "inherited":false,
     "required":false,
     "queryable":false,
     "orderable":false,
     "openChoice":false,
     "mcm:action":{  
        "localizedDisplayName":"OpenURL",
        "renderAsButton":"true",
        "positionWeight":"1"
     }
  },
  "ACTION:Approve":{  
     "maxLength":0,
     "id":"ACTION:Approve",
     "localName":"ACTION:Approve",
     "localNamespace":"com.sap.dms",
     "displayName":"Approve",
     "queryName":"ACTION:Approve",
     "description":"Approve",
     "propertyType":"string",
     "cardinality":"single",
     "updatability":"readwrite",
     "inherited":false,
     "required":false,
     "queryable":true,
     "orderable":true,
     "openChoice":true,
     "mcm:action":{  
        "localizedDisplayName":"{\"en\":\"Approve\",\"en-US\":\"Approve\",\"de\":\"Genehmigen\"}",
        "renderAsButton":"true",
        "positionWeight":"2",
        "version":"1.0"
     }
  }

If this implementation needs to be performed on another repository such as MS Sharepoint or Alfresco, corresponding implementation to generate this JSON schema must be added. SAP Document Center allows us to handle business actions on any backend repository, be it in the cloud or on-premise. This is quite unique and powerful because of the flexibility to support business actions on documents or folders of any repository.

Hope this blog helps you to add your necessary business actions. If you come across interesting insights, challenges or tips feel free to post your comments below. All the best and happy coding.

 

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Stefan Riedel-Seifert
      Stefan Riedel-Seifert

      I have created a repository by sap document service, built the neccessary document bridge and are able to connect via cmis workbench. But i am still wondering, that i am not able to create/delete/update types.

       

      Author's profile photo kiran kumar kandakatla
      kiran kumar kandakatla

      Harini,

      Very informative and detailed blog.

      How to enhance "Shared folder" or "My Documents" folder with user actions, like opening an url? where do we write the code to enhance?