CLASS zcl_abapgit_git_transport DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
* remote to local
    CLASS-METHODS upload_pack
      IMPORTING iv_url         TYPE string
                iv_branch_name TYPE string
                iv_deepen      TYPE abap_bool DEFAULT abap_true
                it_branches    TYPE zif_abapgit_definitions=>ty_git_branch_list_tt OPTIONAL
      EXPORTING et_objects     TYPE zif_abapgit_definitions=>ty_objects_tt
                ev_branch      TYPE zif_abapgit_definitions=>ty_sha1
      RAISING   zcx_abapgit_exception.

* local to remote
    CLASS-METHODS receive_pack
      IMPORTING iv_url         TYPE string
                iv_old         TYPE zif_abapgit_definitions=>ty_sha1
                iv_new         TYPE zif_abapgit_definitions=>ty_sha1
                iv_branch_name TYPE string
                iv_pack        TYPE xstring
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS branches
      IMPORTING iv_url                TYPE string
      RETURNING VALUE(ro_branch_list) TYPE REF TO zcl_abapgit_git_branch_list
      RAISING   zcx_abapgit_exception.
  PRIVATE SECTION.
    CONSTANTS: BEGIN OF c_service,
                 receive TYPE string VALUE 'receive',       "#EC NOTEXT
                 upload  TYPE string VALUE 'upload',        "#EC NOTEXT
               END OF c_service.

    CLASS-METHODS branch_list
      IMPORTING iv_url         TYPE string
                iv_service     TYPE string
      EXPORTING eo_client      TYPE REF TO zcl_abapgit_http_client
                eo_branch_list TYPE REF TO zcl_abapgit_git_branch_list
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS find_branch
      IMPORTING iv_url         TYPE string
                iv_service     TYPE string
                iv_branch_name TYPE string
      EXPORTING eo_client      TYPE REF TO zcl_abapgit_http_client
                ev_branch      TYPE zif_abapgit_definitions=>ty_sha1
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS parse
      EXPORTING ev_pack TYPE xstring
      CHANGING  cv_data TYPE xstring
      RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_GIT_TRANSPORT IMPLEMENTATION.

  METHOD branches.

    DATA: lo_client TYPE REF TO zcl_abapgit_http_client.

    branch_list(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-upload
      IMPORTING
        eo_client      = lo_client
        eo_branch_list = ro_branch_list ).

    lo_client->close( ).

  ENDMETHOD.                    "branches

  METHOD branch_list.

    DATA: lv_data TYPE string.

    eo_client = zcl_abapgit_http=>create_by_url(
      iv_url     = iv_url
      iv_service = iv_service ).

    lv_data = eo_client->get_cdata( ).

    CREATE OBJECT eo_branch_list
      EXPORTING
        iv_data = lv_data.

  ENDMETHOD.                    "branch_list

  METHOD find_branch.

    DATA: lo_branch_list TYPE REF TO zcl_abapgit_git_branch_list.

    branch_list(
      EXPORTING
        iv_url          = iv_url
        iv_service      = iv_service
      IMPORTING
        eo_client       = eo_client
        eo_branch_list  = lo_branch_list ).

    IF ev_branch IS SUPPLIED.
      ev_branch = lo_branch_list->find_by_name( iv_branch_name )-sha1.
    ENDIF.

  ENDMETHOD.                    "find_branch

  METHOD parse.

    CONSTANTS: lc_band1 TYPE x VALUE '01'.

    DATA: lv_len      TYPE i,
          lv_contents TYPE xstring,
          lv_pack     TYPE xstring.

    WHILE xstrlen( cv_data ) >= 4.
      lv_len = zcl_abapgit_git_utils=>length_utf8_hex( cv_data ).

      IF lv_len > xstrlen( cv_data ).
        zcx_abapgit_exception=>raise( 'parse, string length too large' ).
      ENDIF.

      lv_contents = cv_data(lv_len).
      IF lv_len = 0.
        cv_data = cv_data+4.
        CONTINUE.
      ELSE.
        cv_data = cv_data+lv_len.
      ENDIF.

      lv_contents = lv_contents+4.

      IF xstrlen( lv_contents ) > 1 AND lv_contents(1) = lc_band1.
        CONCATENATE lv_pack lv_contents+1 INTO lv_pack IN BYTE MODE.
      ENDIF.

    ENDWHILE.

    ev_pack = lv_pack.

  ENDMETHOD.                    "parse

  METHOD receive_pack.

    DATA: lo_client   TYPE REF TO zcl_abapgit_http_client,
          lv_cmd_pkt  TYPE string,
          lv_line     TYPE string,
          lv_tmp      TYPE xstring,
          lv_xstring  TYPE xstring,
          lv_string   TYPE string,
          lv_cap_list TYPE string,
          lv_buffer   TYPE string.

    find_branch(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-receive
        iv_branch_name = iv_branch_name
      IMPORTING
        eo_client      = lo_client ).

    lo_client->set_headers(
      iv_url     = iv_url
      iv_service = c_service-receive ).

    lv_cap_list = 'report-status agent=' && zcl_abapgit_http=>get_agent( ) ##NO_TEXT.

    lv_line = iv_old &&
              ` ` &&
              iv_new &&
              ` ` &&
              iv_branch_name &&
              zcl_abapgit_git_utils=>get_null( ) &&
              ` ` &&
              lv_cap_list &&
              zif_abapgit_definitions=>gc_newline.          "#EC NOTEXT
    lv_cmd_pkt = zcl_abapgit_git_utils=>pkt_string( lv_line ).

    lv_buffer = lv_cmd_pkt && '0000'.
    lv_tmp = zcl_abapgit_convert=>string_to_xstring_utf8( lv_buffer ).

    CONCATENATE lv_tmp iv_pack INTO lv_xstring IN BYTE MODE.

    lv_xstring = lo_client->send_receive_close( lv_xstring ).

    lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xstring ).
    IF NOT lv_string CP '*unpack ok*'.
      zcx_abapgit_exception=>raise( 'unpack not ok' ).
    ELSEIF lv_string CP '*pre-receive hook declined*'.
      zcx_abapgit_exception=>raise( 'pre-receive hook declined' ).
    ELSEIF lv_string CP '*funny refname*'.
      zcx_abapgit_exception=>raise( 'funny refname' ).
    ELSEIF lv_string CP '*failed to update ref*'.
      zcx_abapgit_exception=>raise( 'failed to update ref' ).
    ENDIF.

  ENDMETHOD.                    "receive_pack

  METHOD upload_pack.

    DATA: lo_client   TYPE REF TO zcl_abapgit_http_client,
          lv_buffer   TYPE string,
          lv_xstring  TYPE xstring,
          lv_line     TYPE string,
          lv_pack     TYPE xstring,
          lt_branches TYPE zif_abapgit_definitions=>ty_git_branch_list_tt,
          lv_capa     TYPE string.

    FIELD-SYMBOLS: <ls_branch> LIKE LINE OF lt_branches.

    CLEAR et_objects.

    find_branch(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-upload
        iv_branch_name = iv_branch_name
      IMPORTING
        eo_client      = lo_client
        ev_branch      = ev_branch ).

    IF it_branches IS INITIAL.
      APPEND INITIAL LINE TO lt_branches ASSIGNING <ls_branch>.
      <ls_branch>-sha1 = ev_branch.
    ELSE.
      lt_branches = it_branches.
    ENDIF.

    lo_client->set_headers( iv_url     = iv_url
                            iv_service = c_service-upload ).

    LOOP AT lt_branches FROM 1 ASSIGNING <ls_branch>.
      IF sy-tabix = 1.
        lv_capa = 'side-band-64k no-progress multi_ack agent='
          && zcl_abapgit_http=>get_agent( ) ##NO_TEXT.
        lv_line = 'want' && ` ` && <ls_branch>-sha1
          && ` ` && lv_capa && zif_abapgit_definitions=>gc_newline. "#EC NOTEXT
      ELSE.
        lv_line = 'want' && ` ` && <ls_branch>-sha1
          && zif_abapgit_definitions=>gc_newline.           "#EC NOTEXT
      ENDIF.
      lv_buffer = lv_buffer && zcl_abapgit_git_utils=>pkt_string( lv_line ).
    ENDLOOP.

    IF iv_deepen = abap_true.
      lv_buffer = lv_buffer && zcl_abapgit_git_utils=>pkt_string( 'deepen 1'
        && zif_abapgit_definitions=>gc_newline ).           "#EC NOTEXT
    ENDIF.

    lv_buffer = lv_buffer
             && '0000'
             && '0009done' && zif_abapgit_definitions=>gc_newline.

    lv_xstring = lo_client->send_receive_close(
      zcl_abapgit_convert=>string_to_xstring_utf8( lv_buffer ) ).

    parse( IMPORTING ev_pack = lv_pack
           CHANGING cv_data = lv_xstring ).

    IF lv_pack IS INITIAL.
      zcx_abapgit_exception=>raise( 'empty pack' ).
    ENDIF.

    et_objects = zcl_abapgit_git_pack=>decode( lv_pack ).

  ENDMETHOD.                    "upload_pack
ENDCLASS.

CLASS zcl_abapgit_git_transport DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
* remote to local
    CLASS-METHODS upload_pack
      IMPORTING iv_url         TYPE string
                iv_branch_name TYPE string
                iv_deepen      TYPE abap_bool DEFAULT abap_true
                it_branches    TYPE zif_abapgit_definitions=>ty_git_branch_list_tt OPTIONAL
      EXPORTING et_objects     TYPE zif_abapgit_definitions=>ty_objects_tt
                ev_branch      TYPE zif_abapgit_definitions=>ty_sha1
      RAISING   zcx_abapgit_exception.

* local to remote
    CLASS-METHODS receive_pack
      IMPORTING iv_url         TYPE string
                iv_old         TYPE zif_abapgit_definitions=>ty_sha1
                iv_new         TYPE zif_abapgit_definitions=>ty_sha1
                iv_branch_name TYPE string
                iv_pack        TYPE xstring
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS branches
      IMPORTING iv_url                TYPE string
      RETURNING VALUE(ro_branch_list) TYPE REF TO zcl_abapgit_git_branch_list
      RAISING   zcx_abapgit_exception.
  PRIVATE SECTION.
    CONSTANTS: BEGIN OF c_service,
                 receive TYPE string VALUE 'receive',       "#EC NOTEXT
                 upload  TYPE string VALUE 'upload',        "#EC NOTEXT
               END OF c_service.

    CLASS-METHODS branch_list
      IMPORTING iv_url         TYPE string
                iv_service     TYPE string
      EXPORTING eo_client      TYPE REF TO zcl_abapgit_http_client
                eo_branch_list TYPE REF TO zcl_abapgit_git_branch_list
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS find_branch
      IMPORTING iv_url         TYPE string
                iv_service     TYPE string
                iv_branch_name TYPE string
      EXPORTING eo_client      TYPE REF TO zcl_abapgit_http_client
                ev_branch      TYPE zif_abapgit_definitions=>ty_sha1
      RAISING   zcx_abapgit_exception.

    CLASS-METHODS parse
      EXPORTING ev_pack TYPE xstring
      CHANGING  cv_data TYPE xstring
      RAISING   zcx_abapgit_exception.

ENDCLASS.

CLASS ZCL_ABAPGIT_GIT_TRANSPORT IMPLEMENTATION.

  METHOD branches.

    DATA: lo_client TYPE REF TO zcl_abapgit_http_client.

    branch_list(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-upload
      IMPORTING
        eo_client      = lo_client
        eo_branch_list = ro_branch_list ).

    lo_client->close( ).

  ENDMETHOD.                    "branches

  METHOD branch_list.

    DATA: lv_data TYPE string.

    eo_client = zcl_abapgit_http=>create_by_url(
      iv_url     = iv_url
      iv_service = iv_service ).

    lv_data = eo_client->get_cdata( ).

    CREATE OBJECT eo_branch_list
      EXPORTING
        iv_data = lv_data.

  ENDMETHOD.                    "branch_list

  METHOD find_branch.

    DATA: lo_branch_list TYPE REF TO zcl_abapgit_git_branch_list.

    branch_list(
      EXPORTING
        iv_url          = iv_url
        iv_service      = iv_service
      IMPORTING
        eo_client       = eo_client
        eo_branch_list  = lo_branch_list ).

    IF ev_branch IS SUPPLIED.
      ev_branch = lo_branch_list->find_by_name( iv_branch_name )-sha1.
    ENDIF.

  ENDMETHOD.                    "find_branch

  METHOD parse.

    CONSTANTS: lc_band1 TYPE x VALUE '01'.

    DATA: lv_len      TYPE i,
          lv_contents TYPE xstring,
          lv_pack     TYPE xstring.

    WHILE xstrlen( cv_data ) >= 4.
      lv_len = zcl_abapgit_git_utils=>length_utf8_hex( cv_data ).

      IF lv_len > xstrlen( cv_data ).
        zcx_abapgit_exception=>raise( 'parse, string length too large' ).
      ENDIF.

      lv_contents = cv_data(lv_len).
      IF lv_len = 0.
        cv_data = cv_data+4.
        CONTINUE.
      ELSE.
        cv_data = cv_data+lv_len.
      ENDIF.

      lv_contents = lv_contents+4.

      IF xstrlen( lv_contents ) > 1 AND lv_contents(1) = lc_band1.
        CONCATENATE lv_pack lv_contents+1 INTO lv_pack IN BYTE MODE.
      ENDIF.

    ENDWHILE.

    ev_pack = lv_pack.

  ENDMETHOD.                    "parse

  METHOD receive_pack.

    DATA: lo_client   TYPE REF TO zcl_abapgit_http_client,
          lv_cmd_pkt  TYPE string,
          lv_line     TYPE string,
          lv_tmp      TYPE xstring,
          lv_xstring  TYPE xstring,
          lv_string   TYPE string,
          lv_cap_list TYPE string,
          lv_buffer   TYPE string.

    find_branch(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-receive
        iv_branch_name = iv_branch_name
      IMPORTING
        eo_client      = lo_client ).

    lo_client->set_headers(
      iv_url     = iv_url
      iv_service = c_service-receive ).

    lv_cap_list = 'report-status' ##NO_TEXT.

    lv_line = iv_old &&
              ` ` &&
              iv_new &&
              ` ` &&
              iv_branch_name &&
              zcl_abapgit_git_utils=>get_null( ) &&
              ` ` &&
              lv_cap_list &&
              zif_abapgit_definitions=>gc_newline.          "#EC NOTEXT
    lv_cmd_pkt = zcl_abapgit_git_utils=>pkt_string( lv_line ).

    lv_buffer = lv_cmd_pkt && '0000'.
    lv_tmp = zcl_abapgit_convert=>string_to_xstring_utf8( lv_buffer ).

    CONCATENATE lv_tmp iv_pack INTO lv_xstring IN BYTE MODE.

    lv_xstring = lo_client->send_receive_close( lv_xstring ).

    lv_string = zcl_abapgit_convert=>xstring_to_string_utf8( lv_xstring ).
    IF NOT lv_string CP '*unpack ok*'.
      zcx_abapgit_exception=>raise( 'unpack not ok' ).
    ELSEIF lv_string CP '*pre-receive hook declined*'.
      zcx_abapgit_exception=>raise( 'pre-receive hook declined' ).
    ELSEIF lv_string CP '*funny refname*'.
      zcx_abapgit_exception=>raise( 'funny refname' ).
    ELSEIF lv_string CP '*failed to update ref*'.
      zcx_abapgit_exception=>raise( 'failed to update ref' ).
    ENDIF.

  ENDMETHOD.                    "receive_pack

  METHOD upload_pack.

    DATA: lo_client   TYPE REF TO zcl_abapgit_http_client,
          lv_buffer   TYPE string,
          lv_xstring  TYPE xstring,
          lv_line     TYPE string,
          lv_pack     TYPE xstring,
          lt_branches TYPE zif_abapgit_definitions=>ty_git_branch_list_tt,
          lv_capa     TYPE string.

    FIELD-SYMBOLS: <ls_branch> LIKE LINE OF lt_branches.

    CLEAR et_objects.

    find_branch(
      EXPORTING
        iv_url         = iv_url
        iv_service     = c_service-upload
        iv_branch_name = iv_branch_name
      IMPORTING
        eo_client      = lo_client
        ev_branch      = ev_branch ).

    IF it_branches IS INITIAL.
      APPEND INITIAL LINE TO lt_branches ASSIGNING <ls_branch>.
      <ls_branch>-sha1 = ev_branch.
    ELSE.
      lt_branches = it_branches.
    ENDIF.

    lo_client->set_headers( iv_url     = iv_url
                            iv_service = c_service-upload ).

    LOOP AT lt_branches FROM 1 ASSIGNING <ls_branch>.
      IF sy-tabix = 1.
        lv_capa = 'side-band-64k no-progress multi_ack' ##NO_TEXT.
        lv_line = 'want' && ` ` && <ls_branch>-sha1
          && ` ` && lv_capa && zif_abapgit_definitions=>gc_newline. "#EC NOTEXT
      ELSE.
        lv_line = 'want' && ` ` && <ls_branch>-sha1
          && zif_abapgit_definitions=>gc_newline.           "#EC NOTEXT
      ENDIF.
      lv_buffer = lv_buffer && zcl_abapgit_git_utils=>pkt_string( lv_line ).
    ENDLOOP.

    IF iv_deepen = abap_true.
      lv_buffer = lv_buffer && zcl_abapgit_git_utils=>pkt_string( 'deepen 1'
        && zif_abapgit_definitions=>gc_newline ).           "#EC NOTEXT
    ENDIF.

    lv_buffer = lv_buffer
             && '0000'
             && '0009done' && zif_abapgit_definitions=>gc_newline.

    lv_xstring = lo_client->send_receive_close(
      zcl_abapgit_convert=>string_to_xstring_utf8( lv_buffer ) ).

    parse( IMPORTING ev_pack = lv_pack
           CHANGING cv_data = lv_xstring ).

    IF lv_pack IS INITIAL.
      zcx_abapgit_exception=>raise( 'empty pack' ).
    ENDIF.

    et_objects = zcl_abapgit_git_pack=>decode( lv_pack ).

  ENDMETHOD.                    "upload_pack
ENDCLASS.

fix for AWS CodeCommit (#1266)

fix for AWS CodeCommit (#1266)

* fix for AWS CodeCommit

* and also fix push


CLASS zcl_abapgit_gui_page_db DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor
      RAISING zcx_abapgit_exception.

    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .
  PROTECTED SECTION.

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    CONSTANTS:
      BEGIN OF c_action,
        delete TYPE string VALUE 'delete',
      END OF c_action .

    CLASS-METHODS delete
      IMPORTING
        !is_key TYPE zif_abapgit_persistence=>ty_content
      RAISING
        zcx_abapgit_exception.
    METHODS explain_content
      IMPORTING
        !is_data       TYPE zif_abapgit_persistence=>ty_content
      RETURNING
        VALUE(rv_text) TYPE string
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_PAGE_DB IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'Database Utility'.
  ENDMETHOD.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.

    lv_answer = zcl_abapgit_ui_factory=>get_popups( )->popup_to_confirm(
      iv_titlebar              = 'Warning'
      iv_text_question         = 'Delete?'
      iv_text_button_1         = 'Ok'
      iv_icon_button_1         = 'ICON_DELETE'
      iv_text_button_2         = 'Cancel'
      iv_icon_button_2         = 'ICON_CANCEL'
      iv_default_button        = '2'
      iv_display_cancel_button = abap_false ).

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE zcx_abapgit_cancel.
    ENDIF.

    zcl_abapgit_persistence_db=>get_instance( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.

  METHOD explain_content.

    DATA: ls_result TYPE match_result,
          ls_match  TYPE submatch_result,
          lv_cnt    TYPE i.

    CASE is_data-type.
      WHEN 'REPO'.
        FIND FIRST OCCURRENCE OF REGEX '<url>(.*)</url>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS INITIAL.
          rv_text = is_data-data_str+ls_match-offset(ls_match-length).
        ENDIF.

        FIND FIRST OCCURRENCE OF REGEX '<OFFLINE/>'
          IN is_data-data_str IGNORING CASE MATCH COUNT lv_cnt.
        IF lv_cnt > 0.
          rv_text = |On-line, Name: {
                    zcl_abapgit_url=>name( rv_text ) }|.
        ELSE.
          rv_text = |Off-line, Name: { rv_text }|.
        ENDIF.

      WHEN 'BACKGROUND'.
        FIND FIRST OCCURRENCE OF REGEX '<method>(.*)</method>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
        rv_text = |Method: { is_data-data_str+ls_match-offset(ls_match-length) }, |
               && |Repository: { zcl_abapgit_repo_srv=>get_instance( )->get( is_data-value )->get_name( ) }|.

      WHEN 'USER'.
        rv_text = '-'. " No additional explanation for user
      WHEN 'SETTINGS'.
        rv_text = '-'.
      WHEN OTHERS.
        IF strlen( is_data-data_str ) >= 250.
          rv_text = is_data-data_str(250).
        ELSE.
          rv_text = is_data-data_str.
        ENDIF.
        rv_text = escape( val    = rv_text
                          format = cl_abap_format=>e_html_attr ).
        rv_text = |
{ rv_text }
|.
    ENDCASE.
  ENDMETHOD.

  METHOD render_content.

    DATA: lt_data    TYPE zif_abapgit_persistence=>ty_contents,
          lv_action  TYPE string,
          lv_trclass TYPE string,
          lo_toolbar TYPE REF TO zcl_abapgit_html_toolbar.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF lt_data.

    lt_data = zcl_abapgit_persistence_db=>get_instance( )->list( ).

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.

    ri_html->add( '
' ).
    ri_html->add( '
' ).

    " Header
    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( 'Type	' ).
    ri_html->add( 'Key	' ).
    ri_html->add( 'Data	' ).
    ri_html->add( '	' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).

    " Lines
    LOOP AT lt_data ASSIGNING <ls_data>.
      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"'.
      ENDIF.

      lv_action  = zcl_abapgit_html_action_utils=>dbkey_encode( <ls_data> ).

      CREATE OBJECT lo_toolbar.
      lo_toolbar->add( iv_txt = 'Display'
                       iv_act = |{ zif_abapgit_definitions=>c_action-db_display }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Edit'
                       iv_act = |{ zif_abapgit_definitions=>c_action-db_edit }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Delete'
                       iv_act = |{ c_action-delete }?{ lv_action }| ).

      ri_html->add( |
| ).
      ri_html->add( |{ <ls_data>-type }	| ).
      ri_html->add( |{ <ls_data>-value }	| ).
      ri_html->add( |{ explain_content( <ls_data> ) }	| ).
      ri_html->add( '' ).
      ri_html->add( lo_toolbar->render( ) ).
      ri_html->add( '	' ).
      ri_html->add( '
' ).
    ENDLOOP.

    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    DATA ls_db TYPE zif_abapgit_persistence=>ty_content.
    DATA lo_query TYPE REF TO zcl_abapgit_string_map.

    lo_query = ii_event->query( ).
    CASE ii_event->mv_action.
      WHEN c_action-delete.
        lo_query->to_abap( CHANGING cs_container = ls_db ).
        delete( ls_db ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN OTHERS.
        rs_handled = super->zif_abapgit_gui_event_handler~on_event( ii_event ).
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_gui_page_db DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor
      RAISING zcx_abapgit_exception.

    METHODS zif_abapgit_gui_event_handler~on_event
        REDEFINITION .
  PROTECTED SECTION.

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    CONSTANTS:
      BEGIN OF c_action,
        delete  TYPE string VALUE 'delete',
        backup  TYPE string VALUE 'backup',
        restore TYPE string VALUE 'restore',
      END OF c_action .

    CLASS-METHODS backup
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS delete
      IMPORTING
        !is_key TYPE zif_abapgit_persistence=>ty_content
      RAISING
        zcx_abapgit_exception .
    CLASS-METHODS restore
      RAISING
        zcx_abapgit_exception .
    METHODS explain_content
      IMPORTING
        !is_data       TYPE zif_abapgit_persistence=>ty_content
      RETURNING
        VALUE(rv_text) TYPE string
      RAISING
        zcx_abapgit_exception .
    METHODS build_menu
      RETURNING
        VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar .
ENDCLASS.

CLASS zcl_abapgit_gui_page_db IMPLEMENTATION.

  METHOD backup.

    DATA:
      lt_data     TYPE zif_abapgit_persistence=>ty_contents,
      lo_zip      TYPE REF TO cl_abap_zip,
      lv_zip      TYPE xstring,
      lv_path     TYPE string,
      lv_filename TYPE string,
      li_fe_serv  TYPE REF TO zif_abapgit_frontend_services.

    FIELD-SYMBOLS:
      <ls_data> LIKE LINE OF lt_data.

    lt_data = zcl_abapgit_persistence_db=>get_instance( )->list( ).

    CREATE OBJECT lo_zip.

    LOOP AT lt_data ASSIGNING <ls_data>.
      CONCATENATE <ls_data>-type '_' <ls_data>-value '.xml' INTO lv_filename.
      lo_zip->add( name    = lv_filename
                   content = zcl_abapgit_convert=>string_to_xstring_utf8( <ls_data>-data_str ) ).
    ENDLOOP.

    lv_zip = lo_zip->save( ).

    CONCATENATE 'abapGit_Backup_' sy-datlo '_' sy-timlo INTO lv_filename.

    li_fe_serv = zcl_abapgit_ui_factory=>get_frontend_services( ).

    lv_path = li_fe_serv->show_file_save_dialog(
      iv_title            = 'abapGit Backup'
      iv_extension        = 'zip'
      iv_default_filename = lv_filename ).

    li_fe_serv->file_download(
      iv_path = lv_path
      iv_xstr = lv_zip ).

    MESSAGE 'abapGit Backup successfully saved' TYPE 'S'.

  ENDMETHOD.

  METHOD build_menu.

    CREATE OBJECT ro_menu.

    ro_menu->add( iv_txt = 'Backup'
                  iv_act = c_action-backup ).

    ro_menu->add( iv_txt = 'Restore'
                  iv_act = c_action-restore ).

  ENDMETHOD.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'Database Utility'.
    ms_control-page_menu  = build_menu( ).
  ENDMETHOD.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.

    lv_answer = zcl_abapgit_ui_factory=>get_popups( )->popup_to_confirm(
      iv_titlebar              = 'Warning'
      iv_text_question         = 'Delete?'
      iv_text_button_1         = 'Ok'
      iv_icon_button_1         = 'ICON_DELETE'
      iv_text_button_2         = 'Cancel'
      iv_icon_button_2         = 'ICON_CANCEL'
      iv_default_button        = '2'
      iv_display_cancel_button = abap_false ).

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE zcx_abapgit_cancel.
    ENDIF.

    zcl_abapgit_persistence_db=>get_instance( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.

  METHOD explain_content.

    DATA: ls_result TYPE match_result,
          ls_match  TYPE submatch_result,
          lv_cnt    TYPE i.

    CASE is_data-type.
      WHEN 'REPO'.
        FIND FIRST OCCURRENCE OF REGEX '<url>(.*)</url>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS INITIAL.
          rv_text = is_data-data_str+ls_match-offset(ls_match-length).
        ENDIF.

        FIND FIRST OCCURRENCE OF REGEX '<OFFLINE/>'
          IN is_data-data_str IGNORING CASE MATCH COUNT lv_cnt.
        IF lv_cnt > 0.
          rv_text = |On-line, Name: {
                    zcl_abapgit_url=>name( rv_text ) }|.
        ELSE.
          rv_text = |Off-line, Name: { rv_text }|.
        ENDIF.

      WHEN 'BACKGROUND'.
        FIND FIRST OCCURRENCE OF REGEX '<method>(.*)</method>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
        rv_text = |Method: { is_data-data_str+ls_match-offset(ls_match-length) }, |
               && |Repository: { zcl_abapgit_repo_srv=>get_instance( )->get( is_data-value )->get_name( ) }|.

      WHEN 'USER'.
        rv_text = '-'. " No additional explanation for user
      WHEN 'SETTINGS'.
        rv_text = '-'.
      WHEN OTHERS.
        IF strlen( is_data-data_str ) >= 250.
          rv_text = is_data-data_str(250).
        ELSE.
          rv_text = is_data-data_str.
        ENDIF.
        rv_text = escape( val    = rv_text
                          format = cl_abap_format=>e_html_attr ).
        rv_text = |
{ rv_text }
|.
    ENDCASE.
  ENDMETHOD.

  METHOD render_content.

    DATA: lt_data    TYPE zif_abapgit_persistence=>ty_contents,
          lv_action  TYPE string,
          lv_trclass TYPE string,
          lo_toolbar TYPE REF TO zcl_abapgit_html_toolbar.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF lt_data.

    lt_data = zcl_abapgit_persistence_db=>get_instance( )->list( ).

    CREATE OBJECT ri_html TYPE zcl_abapgit_html.

    ri_html->add( '
' ).
    ri_html->add( '
' ).

    " Header
    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( 'Type	' ).
    ri_html->add( 'Key	' ).
    ri_html->add( 'Data	' ).
    ri_html->add( '	' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).

    " Lines
    LOOP AT lt_data ASSIGNING <ls_data>.
      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"'.
      ENDIF.

      lv_action  = zcl_abapgit_html_action_utils=>dbkey_encode( <ls_data> ).

      CREATE OBJECT lo_toolbar.
      lo_toolbar->add( iv_txt = 'Display'
                       iv_act = |{ zif_abapgit_definitions=>c_action-db_display }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Edit'
                       iv_act = |{ zif_abapgit_definitions=>c_action-db_edit }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Delete'
                       iv_act = |{ c_action-delete }?{ lv_action }| ).

      ri_html->add( |
| ).
      ri_html->add( |{ <ls_data>-type }	| ).
      ri_html->add( |{ <ls_data>-value }	| ).
      ri_html->add( |{ explain_content( <ls_data> ) }	| ).
      ri_html->add( '' ).
      ri_html->add( lo_toolbar->render( ) ).
      ri_html->add( '	' ).
      ri_html->add( '
' ).
    ENDLOOP.

    ri_html->add( '
' ).
    ri_html->add( '
' ).
    ri_html->add( '
' ).

  ENDMETHOD.

  METHOD restore.

    DATA:
      lv_answer   TYPE c LENGTH 1,
      lo_zip      TYPE REF TO cl_abap_zip,
      lv_zip      TYPE xstring,
      lv_path     TYPE string,
      lv_filename TYPE string,
      lv_data     TYPE xstring,
      ls_data     TYPE zif_abapgit_persistence=>ty_content,
      lt_data     TYPE zif_abapgit_persistence=>ty_contents,
      lt_data_old TYPE zif_abapgit_persistence=>ty_contents,
      li_fe_serv  TYPE REF TO zif_abapgit_frontend_services.

    FIELD-SYMBOLS:
      <ls_zipfile> LIKE LINE OF lo_zip->files.

    li_fe_serv = zcl_abapgit_ui_factory=>get_frontend_services( ).

    lv_path = li_fe_serv->show_file_open_dialog(
      iv_title            = 'Restore abapGit Backup'
      iv_extension        = 'zip'
      iv_default_filename = 'abapGit_Backup_*.zip' ).

    lv_zip = li_fe_serv->file_upload( lv_path ).

    CREATE OBJECT lo_zip.

    lo_zip->load(
      EXPORTING
        zip             = lv_zip
      EXCEPTIONS
        zip_parse_error = 1
        OTHERS          = 2 ).
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'Error loading ZIP file' ).
    ENDIF.

    LOOP AT lo_zip->files ASSIGNING <ls_zipfile>.
      CLEAR ls_data.
      lv_filename = <ls_zipfile>-name.
      REPLACE '.xml' IN lv_filename WITH ''.
      SPLIT lv_filename AT '_' INTO ls_data-type ls_data-value.

      " Validate DB key
      IF ls_data-type <> 'REPO' AND ls_data-type <> 'USER' AND
          ls_data-type <> 'SETTINGS' AND ls_data-type <> 'BACKGROUND'.
        zcx_abapgit_exception=>raise( |Invalid DB key. This is not an abapGit Backup| ).
      ENDIF.

      lo_zip->get(
        EXPORTING
          name                    = <ls_zipfile>-name
        IMPORTING
          content                 = lv_data
        EXCEPTIONS
          zip_index_error         = 1
          zip_decompression_error = 2
          OTHERS                  = 3 ).
      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise( |Error getting file { <ls_zipfile>-name } from ZIP| ).
      ENDIF.

      ls_data-data_str = zcl_abapgit_convert=>xstring_to_string_utf8( lv_data ).
      INSERT ls_data INTO TABLE lt_data.
    ENDLOOP.

    lv_answer = zcl_abapgit_ui_factory=>get_popups( )->popup_to_confirm(
      iv_titlebar              = 'Warning'
      iv_text_question         = 'All existing repositories and settings will be deleted and overwritten! Continue?'
      iv_text_button_1         = 'Restore'
      iv_icon_button_1         = 'ICON_IMPORT'
      iv_text_button_2         = 'Cancel'
      iv_icon_button_2         = 'ICON_CANCEL'
      iv_default_button        = '2'
      iv_display_cancel_button = abap_false ).

    IF lv_answer <> '1'.
      RAISE EXCEPTION TYPE zcx_abapgit_cancel.
    ENDIF.

    lt_data_old = zcl_abapgit_persistence_db=>get_instance( )->list( ).
    LOOP AT lt_data_old INTO ls_data.
      zcl_abapgit_persistence_db=>get_instance( )->delete(
        iv_type  = ls_data-type
        iv_value = ls_data-value ).
    ENDLOOP.

    COMMIT WORK AND WAIT.

    LOOP AT lt_data INTO ls_data.
      zcl_abapgit_persistence_db=>get_instance( )->add(
        iv_type  = ls_data-type
        iv_value = ls_data-value
        iv_data  = ls_data-data_str ).
    ENDLOOP.

    COMMIT WORK AND WAIT.

    MESSAGE 'abapGit Backup successfully restored' TYPE 'S'.

  ENDMETHOD.

  METHOD zif_abapgit_gui_event_handler~on_event.

    DATA ls_db TYPE zif_abapgit_persistence=>ty_content.
    DATA lo_query TYPE REF TO zcl_abapgit_string_map.

    lo_query = ii_event->query( ).
    CASE ii_event->mv_action.
      WHEN c_action-delete.
        lo_query->to_abap( CHANGING cs_container = ls_db ).
        delete( ls_db ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_action-backup.
        backup( ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN c_action-restore.
        restore( ).
        rs_handled-state = zcl_abapgit_gui=>c_event_state-re_render.
      WHEN OTHERS.
        rs_handled = super->zif_abapgit_gui_event_handler~on_event( ii_event ).
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

Backup and restore for abapGit persistency (#4137)

* Backup and restore for abapGit persistency

Backup complete content of abapGit persistency to a ZIP file and restore when necessary.

* Messages

* 702

Co-authored-by: Lars Hvam <721d1b171982cd84b755f6b25130d2b88cd1f696@hotmail.com>


CLASS zcl_abapgit_gui_page_db DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor .

    METHODS zif_abapgit_gui_page~on_event
        REDEFINITION .
  PROTECTED SECTION.

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    CONSTANTS:
      BEGIN OF gc_action,
        delete TYPE string VALUE 'delete',
      END OF gc_action .

    CLASS-METHODS delete
      IMPORTING
        !is_key TYPE zif_abapgit_persistence=>ty_content
      RAISING
        zcx_abapgit_exception
        zcx_abapgit_cancel .
    METHODS explain_content
      IMPORTING
        !is_data       TYPE zif_abapgit_persistence=>ty_content
      RETURNING
        VALUE(rv_text) TYPE string
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_PAGE_DB IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'DATABASE PERSISTENCY'.
  ENDMETHOD.  " constructor.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.
    ASSERT is_key-value IS NOT INITIAL.

    lv_answer = zcl_abapgit_popups=>popup_to_confirm(
      titlebar              = 'Warning'
      text_question         = 'Delete?'
      text_button_1         = 'Ok'
      icon_button_1         = 'ICON_DELETE'
      text_button_2         = 'Cancel'
      icon_button_2         = 'ICON_CANCEL'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE zcx_abapgit_cancel.
    ENDIF.

    zcl_abapgit_persistence_db=>get_instance( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.  " delete

  METHOD explain_content.

    DATA: ls_result TYPE match_result,
          ls_match  TYPE submatch_result,
          lv_cnt    TYPE i.

    CASE is_data-type.
      WHEN 'REPO'.
        FIND FIRST OCCURRENCE OF REGEX '<url>(.*)</url>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS INITIAL.
          rv_text = is_data-data_str+ls_match-offset(ls_match-length).
        ENDIF.

        FIND FIRST OCCURRENCE OF REGEX '<OFFLINE/>'
          IN is_data-data_str IGNORING CASE MATCH COUNT lv_cnt.
        IF lv_cnt > 0.
          rv_text = |On-line, Name: {
                    zcl_abapgit_url=>name( rv_text ) }|.
        ELSE.
          rv_text = |Off-line, Name: { rv_text }|.
        ENDIF.

      WHEN 'BACKGROUND'.
        FIND FIRST OCCURRENCE OF REGEX '<method>(.*)</method>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
        rv_text = |Method: { is_data-data_str+ls_match-offset(ls_match-length) }, |
               && |Repository: { zcl_abapgit_repo_srv=>get_instance( )->get( is_data-value )->get_name( ) }|.

      WHEN 'USER'.
        rv_text = '-'. " No additional explanation for user
      WHEN 'SETTINGS'.
        rv_text = '-'.
      WHEN OTHERS.
        IF strlen( is_data-data_str ) >= 250.
          rv_text = is_data-data_str(250).
        ELSE.
          rv_text = is_data-data_str.
        ENDIF.
        rv_text = escape( val    = rv_text
                          format = cl_abap_format=>e_html_attr ).
        rv_text = |
{ rv_text }
|.
    ENDCASE.
  ENDMETHOD.  "explain_content

  METHOD render_content.

    DATA: lt_data    TYPE zif_abapgit_persistence=>tt_content,
          lv_action  TYPE string,
          lv_trclass TYPE string,
          lo_toolbar TYPE REF TO zcl_abapgit_html_toolbar.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF lt_data.

    lt_data = zcl_abapgit_persistence_db=>get_instance( )->list( ).

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( '
' ).

    " Header
    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( 'Type	' ).
    ro_html->add( 'Key	' ).
    ro_html->add( 'Data	' ).
    ro_html->add( '	' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).

    " Lines
    LOOP AT lt_data ASSIGNING <ls_data>.
      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"' ##NO_TEXT.
      ENDIF.

      lv_action  = zcl_abapgit_html_action_utils=>dbkey_encode( <ls_data> ).

      CREATE OBJECT lo_toolbar.
      lo_toolbar->add( iv_txt = 'Display' iv_act = |{ zif_abapgit_definitions=>gc_action-db_display }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Edit'    iv_act = |{ zif_abapgit_definitions=>gc_action-db_edit }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Delete'  iv_act = |{ gc_action-delete }?{ lv_action }| ).

      ro_html->add( |
| ).
      ro_html->add( |{ <ls_data>-type }	| ).
      ro_html->add( |{ <ls_data>-value }	| ).
      ro_html->add( |{ explain_content( <ls_data> ) }	| ).
      ro_html->add( '' ).
      ro_html->add( lo_toolbar->render( ) ).
      ro_html->add( '	' ).
      ro_html->add( '
' ).
    ENDLOOP.

    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).

  ENDMETHOD.            "render_content

  METHOD zif_abapgit_gui_page~on_event.

    DATA: ls_db TYPE zif_abapgit_persistence=>ty_content.

    CASE iv_action.
      WHEN gc_action-delete.
        ls_db = zcl_abapgit_html_action_utils=>dbkey_decode( iv_getdata ).
        delete( ls_db ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_gui_page_db DEFINITION
  PUBLIC
  INHERITING FROM zcl_abapgit_gui_page
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    METHODS constructor .

    METHODS zif_abapgit_gui_page~on_event
        REDEFINITION .
  PROTECTED SECTION.

    METHODS render_content
        REDEFINITION .
  PRIVATE SECTION.

    CONSTANTS:
      BEGIN OF gc_action,
        delete TYPE string VALUE 'delete',
      END OF gc_action .

    CLASS-METHODS delete
      IMPORTING
        !is_key TYPE zif_abapgit_persistence=>ty_content
      RAISING
        zcx_abapgit_exception
        zcx_abapgit_cancel .
    METHODS explain_content
      IMPORTING
        !is_data       TYPE zif_abapgit_persistence=>ty_content
      RETURNING
        VALUE(rv_text) TYPE string
      RAISING
        zcx_abapgit_exception .
ENDCLASS.

CLASS ZCL_ABAPGIT_GUI_PAGE_DB IMPLEMENTATION.

  METHOD constructor.
    super->constructor( ).
    ms_control-page_title = 'DATABASE PERSISTENCY'.
  ENDMETHOD.  " constructor.

  METHOD delete.

    DATA: lv_answer TYPE c LENGTH 1.

    ASSERT is_key-type IS NOT INITIAL.

    lv_answer = zcl_abapgit_popups=>popup_to_confirm(
      titlebar              = 'Warning'
      text_question         = 'Delete?'
      text_button_1         = 'Ok'
      icon_button_1         = 'ICON_DELETE'
      text_button_2         = 'Cancel'
      icon_button_2         = 'ICON_CANCEL'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer = '2'.
      RAISE EXCEPTION TYPE zcx_abapgit_cancel.
    ENDIF.

    zcl_abapgit_persistence_db=>get_instance( )->delete(
      iv_type  = is_key-type
      iv_value = is_key-value ).

    COMMIT WORK.

  ENDMETHOD.  " delete

  METHOD explain_content.

    DATA: ls_result TYPE match_result,
          ls_match  TYPE submatch_result,
          lv_cnt    TYPE i.

    CASE is_data-type.
      WHEN 'REPO'.
        FIND FIRST OCCURRENCE OF REGEX '<url>(.*)</url>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS INITIAL.
          rv_text = is_data-data_str+ls_match-offset(ls_match-length).
        ENDIF.

        FIND FIRST OCCURRENCE OF REGEX '<OFFLINE/>'
          IN is_data-data_str IGNORING CASE MATCH COUNT lv_cnt.
        IF lv_cnt > 0.
          rv_text = |On-line, Name: {
                    zcl_abapgit_url=>name( rv_text ) }|.
        ELSE.
          rv_text = |Off-line, Name: { rv_text }|.
        ENDIF.

      WHEN 'BACKGROUND'.
        FIND FIRST OCCURRENCE OF REGEX '<method>(.*)</method>'
          IN is_data-data_str IGNORING CASE RESULTS ls_result.
        READ TABLE ls_result-submatches INTO ls_match INDEX 1.
        IF sy-subrc IS NOT INITIAL.
          RETURN.
        ENDIF.
        rv_text = |Method: { is_data-data_str+ls_match-offset(ls_match-length) }, |
               && |Repository: { zcl_abapgit_repo_srv=>get_instance( )->get( is_data-value )->get_name( ) }|.

      WHEN 'USER'.
        rv_text = '-'. " No additional explanation for user
      WHEN 'SETTINGS'.
        rv_text = '-'.
      WHEN OTHERS.
        IF strlen( is_data-data_str ) >= 250.
          rv_text = is_data-data_str(250).
        ELSE.
          rv_text = is_data-data_str.
        ENDIF.
        rv_text = escape( val    = rv_text
                          format = cl_abap_format=>e_html_attr ).
        rv_text = |
{ rv_text }
|.
    ENDCASE.
  ENDMETHOD.  "explain_content

  METHOD render_content.

    DATA: lt_data    TYPE zif_abapgit_persistence=>tt_content,
          lv_action  TYPE string,
          lv_trclass TYPE string,
          lo_toolbar TYPE REF TO zcl_abapgit_html_toolbar.

    FIELD-SYMBOLS: <ls_data> LIKE LINE OF lt_data.

    lt_data = zcl_abapgit_persistence_db=>get_instance( )->list( ).

    CREATE OBJECT ro_html.

    ro_html->add( '
' ).
    ro_html->add( '
' ).

    " Header
    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( 'Type	' ).
    ro_html->add( 'Key	' ).
    ro_html->add( 'Data	' ).
    ro_html->add( '	' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).

    " Lines
    LOOP AT lt_data ASSIGNING <ls_data>.
      CLEAR lv_trclass.
      IF sy-tabix = 1.
        lv_trclass = ' class="firstrow"' ##NO_TEXT.
      ENDIF.

      lv_action  = zcl_abapgit_html_action_utils=>dbkey_encode( <ls_data> ).

      CREATE OBJECT lo_toolbar.
      lo_toolbar->add( iv_txt = 'Display' iv_act = |{ zif_abapgit_definitions=>gc_action-db_display }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Edit'    iv_act = |{ zif_abapgit_definitions=>gc_action-db_edit }?{ lv_action }| ).
      lo_toolbar->add( iv_txt = 'Delete'  iv_act = |{ gc_action-delete }?{ lv_action }| ).

      ro_html->add( |
| ).
      ro_html->add( |{ <ls_data>-type }	| ).
      ro_html->add( |{ <ls_data>-value }	| ).
      ro_html->add( |{ explain_content( <ls_data> ) }	| ).
      ro_html->add( '' ).
      ro_html->add( lo_toolbar->render( ) ).
      ro_html->add( '	' ).
      ro_html->add( '
' ).
    ENDLOOP.

    ro_html->add( '
' ).
    ro_html->add( '
' ).
    ro_html->add( '
' ).

  ENDMETHOD.            "render_content

  METHOD zif_abapgit_gui_page~on_event.

    DATA: ls_db TYPE zif_abapgit_persistence=>ty_content.

    CASE iv_action.
      WHEN gc_action-delete.
        ls_db = zcl_abapgit_html_action_utils=>dbkey_decode( iv_getdata ).
        delete( ls_db ).
        ev_state = zif_abapgit_definitions=>gc_event_state-re_render.
    ENDCASE.

  ENDMETHOD.
ENDCLASS.

Fix database util: Cannot delete SETTINGS


CLASS zcl_abapgit_http_client DEFINITION PUBLIC CREATE PUBLIC.

  PUBLIC SECTION.

    METHODS:
      constructor
        IMPORTING ii_client TYPE REF TO if_http_client,
      close,
      set_digest
        IMPORTING io_digest TYPE REF TO zcl_abapgit_http_digest,
      send_receive_close
        IMPORTING iv_data        TYPE xstring
        RETURNING VALUE(rv_data) TYPE xstring
        RAISING   zcx_abapgit_exception,
      get_cdata
        RETURNING VALUE(rv_value) TYPE string,
      check_http_200
        RAISING zcx_abapgit_exception,
      check_smart_response
        IMPORTING iv_expected_content_type TYPE string
                  iv_content_regex         TYPE string
        RAISING   zcx_abapgit_exception,
      send_receive
        RAISING zcx_abapgit_exception,
      set_headers
        IMPORTING iv_url     TYPE string
                  iv_service TYPE string
        RAISING   zcx_abapgit_exception.

  PRIVATE SECTION.
    DATA: mi_client TYPE REF TO if_http_client,
          mo_digest TYPE REF TO zcl_abapgit_http_digest.

ENDCLASS.

CLASS zcl_abapgit_http_client IMPLEMENTATION.

  METHOD check_http_200.

    DATA: lv_code TYPE i,
          lv_text TYPE string.

    mi_client->response->get_status( IMPORTING code = lv_code ).
    CASE lv_code.
      WHEN 200.
        RETURN. " Success, OK
      WHEN 302.
        zcx_abapgit_exception=>raise( 'Resource access temporarily redirected. Check the URL (HTTP 302)' ).
      WHEN 401.
        zcx_abapgit_exception=>raise( 'Unauthorized access to resource. Check your credentials (HTTP 401)' ).
      WHEN 403.
        zcx_abapgit_exception=>raise( 'Access to resource forbidden (HTTP 403)' ).
      WHEN 404.
        zcx_abapgit_exception=>raise( 'Resource not found. Check the URL (HTTP 404)' ).
      WHEN 407.
        zcx_abapgit_exception=>raise( 'Proxy authentication required. Check your credentials (HTTP 407)' ).
      WHEN 408.
        zcx_abapgit_exception=>raise( 'Request timeout (HTTP 408)' ).
      WHEN 415.
        zcx_abapgit_exception=>raise( 'Unsupported media type (HTTP 415)' ).
      WHEN OTHERS.
        lv_text = mi_client->response->get_cdata( ).
        zcx_abapgit_exception=>raise( |{ lv_text } (HTTP { lv_code })| ).
    ENDCASE.

  ENDMETHOD.

  METHOD check_smart_response.

    DATA: lv_content_type TYPE string.
    DATA: lv_data         TYPE string.

    IF iv_expected_content_type IS NOT INITIAL.
      lv_content_type = mi_client->response->get_content_type( ).
      IF lv_content_type <> iv_expected_content_type.
        zcx_abapgit_exception=>raise( 'Wrong Content-Type sent by server - no fallback to the dumb protocol!' ).
      ENDIF.
    ENDIF.

    IF iv_content_regex IS NOT INITIAL.
      lv_data = mi_client->response->get_cdata( ).
      FIND REGEX iv_content_regex IN lv_data.
      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise( 'Wrong Content sent by server' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD close.
    mi_client->close( ).
  ENDMETHOD.

  METHOD constructor.
    mi_client = ii_client.
  ENDMETHOD.

  METHOD get_cdata.
    rv_value = mi_client->response->get_cdata( ).
  ENDMETHOD.

  METHOD send_receive.

    DATA: lv_text    TYPE string,
          lv_code    TYPE i,
          lv_message TYPE string.

    mi_client->send( ).
    mi_client->receive(
      EXCEPTIONS
        http_communication_failure = 1
        http_invalid_state         = 2
        http_processing_failed     = 3
        OTHERS                     = 4 ).

    IF sy-subrc <> 0.
      " in case of HTTP_COMMUNICATION_FAILURE
      " make sure:
      " a) SSL is setup properly in STRUST
      " b) no firewalls
      " check trace file in transaction SMICM

      mi_client->get_last_error(
        IMPORTING
          code    = lv_code
          message = lv_message ).

      lv_text = |HTTP error { lv_code } occured: { lv_message }|.

      zcx_abapgit_exception=>raise( lv_text ).
    ENDIF.

  ENDMETHOD.

  METHOD send_receive_close.

* do not use set_cdata as it modifies the Content-Type header field
    mi_client->request->set_data( iv_data ).
    send_receive( ).
    check_http_200( ).
    rv_data = mi_client->response->get_data( ).
    mi_client->close( ).

  ENDMETHOD.

  METHOD set_digest.
    mo_digest = io_digest.
  ENDMETHOD.

  METHOD set_headers.

    DATA: lv_value TYPE string.

    mi_client->request->set_header_field(
        name  = '~request_method'
        value = 'POST' ).

    lv_value = zcl_abapgit_url=>path_name( iv_url ) &&
      '/git-' &&
      iv_service &&
      '-pack'.
    mi_client->request->set_header_field(
        name  = '~request_uri'
        value = lv_value ).

    lv_value = 'application/x-git-'
                  && iv_service && '-pack-request'.
    mi_client->request->set_header_field(
        name  = 'Content-Type'
        value = lv_value ).

    lv_value = 'application/x-git-'
                  && iv_service && '-pack-result'.
    mi_client->request->set_header_field(
        name  = 'Accept'
        value = lv_value ).

    IF mo_digest IS BOUND.
      mo_digest->run( mi_client ).
    ENDIF.

  ENDMETHOD.
ENDCLASS.

CLASS zcl_abapgit_http_client DEFINITION PUBLIC CREATE PUBLIC.

  PUBLIC SECTION.

    METHODS:
      constructor
        IMPORTING ii_client TYPE REF TO if_http_client,
      close,
      set_digest
        IMPORTING io_digest TYPE REF TO zcl_abapgit_http_digest,
      send_receive_close
        IMPORTING iv_data        TYPE xstring
        RETURNING VALUE(rv_data) TYPE xstring
        RAISING   zcx_abapgit_exception,
      get_cdata
        RETURNING VALUE(rv_value) TYPE string,
      check_http_200
        RAISING zcx_abapgit_exception,
      check_smart_response
        IMPORTING iv_expected_content_type TYPE string
                  iv_content_regex         TYPE string
        RAISING   zcx_abapgit_exception,
      send_receive
        RAISING zcx_abapgit_exception,
      set_headers
        IMPORTING iv_url     TYPE string
                  iv_service TYPE string
        RAISING   zcx_abapgit_exception.

  PRIVATE SECTION.
    DATA: mi_client TYPE REF TO if_http_client,
          mo_digest TYPE REF TO zcl_abapgit_http_digest.

ENDCLASS.

CLASS zcl_abapgit_http_client IMPLEMENTATION.

  METHOD check_http_200.

    DATA: lv_code TYPE i,
          lv_text TYPE string.

    mi_client->response->get_status( IMPORTING code = lv_code ).
    CASE lv_code.
      WHEN 200.
        RETURN. " Success, OK
      WHEN 302.
        zcx_abapgit_exception=>raise( 'Resource access temporarily redirected (HTTP 302). Check the URL' ).
      WHEN 401.
        zcx_abapgit_exception=>raise( 'Unauthorized access to resource (HTTP 401). Check your credentials' ).
      WHEN 403.
        zcx_abapgit_exception=>raise( 'Access to resource forbidden (HTTP 403)' ).
      WHEN 404.
        zcx_abapgit_exception=>raise( 'Resource not found (HTTP 404). Check the URL' ).
      WHEN 407.
        zcx_abapgit_exception=>raise( 'Proxy authentication required (HTTP 407). Check your credentials' ).
      WHEN 408.
        zcx_abapgit_exception=>raise( 'Request timeout (HTTP 408)' ).
      WHEN 415.
        zcx_abapgit_exception=>raise( 'Unsupported media type (HTTP 415)' ).
      WHEN 422.
        zcx_abapgit_exception=>raise( 'Unprocessable entity (HTTP 422). Check, if URL has to end with ".git"' ).
      WHEN OTHERS.
        lv_text = mi_client->response->get_cdata( ).
        zcx_abapgit_exception=>raise( |(HTTP { lv_code }) { lv_text }| ).
    ENDCASE.

  ENDMETHOD.

  METHOD check_smart_response.

    DATA: lv_content_type TYPE string.
    DATA: lv_data         TYPE string.

    IF iv_expected_content_type IS NOT INITIAL.
      lv_content_type = mi_client->response->get_content_type( ).
      IF lv_content_type <> iv_expected_content_type.
        zcx_abapgit_exception=>raise( 'Wrong Content-Type sent by server - no fallback to the dumb protocol!' ).
      ENDIF.
    ENDIF.

    IF iv_content_regex IS NOT INITIAL.
      lv_data = mi_client->response->get_cdata( ).
      FIND REGEX iv_content_regex IN lv_data.
      IF sy-subrc <> 0.
        zcx_abapgit_exception=>raise( 'Wrong Content sent by server' ).
      ENDIF.
    ENDIF.

  ENDMETHOD.

  METHOD close.
    mi_client->close( ).
  ENDMETHOD.

  METHOD constructor.
    mi_client = ii_client.
  ENDMETHOD.

  METHOD get_cdata.
    rv_value = mi_client->response->get_cdata( ).
  ENDMETHOD.

  METHOD send_receive.

    DATA: lv_text    TYPE string,
          lv_code    TYPE i,
          lv_message TYPE string.

    mi_client->send( ).
    mi_client->receive(
      EXCEPTIONS
        http_communication_failure = 1
        http_invalid_state         = 2
        http_processing_failed     = 3
        OTHERS                     = 4 ).

    IF sy-subrc <> 0.
      " in case of HTTP_COMMUNICATION_FAILURE
      " make sure:
      " a) SSL is setup properly in STRUST
      " b) no firewalls
      " check trace file in transaction SMICM

      mi_client->get_last_error(
        IMPORTING
          code    = lv_code
          message = lv_message ).

      lv_text = |HTTP error { lv_code } occured: { lv_message }|.

      zcx_abapgit_exception=>raise( lv_text ).
    ENDIF.

  ENDMETHOD.

  METHOD send_receive_close.

* do not use set_cdata as it modifies the Content-Type header field
    mi_client->request->set_data( iv_data ).
    send_receive( ).
    check_http_200( ).
    rv_data = mi_client->response->get_data( ).
    mi_client->close( ).

  ENDMETHOD.

  METHOD set_digest.
    mo_digest = io_digest.
  ENDMETHOD.

  METHOD set_headers.

    DATA: lv_value TYPE string.

    mi_client->request->set_header_field(
        name  = '~request_method'
        value = 'POST' ).

    lv_value = zcl_abapgit_url=>path_name( iv_url ) &&
      '/git-' &&
      iv_service &&
      '-pack'.
    mi_client->request->set_header_field(
        name  = '~request_uri'
        value = lv_value ).

    lv_value = 'application/x-git-'
                  && iv_service && '-pack-request'.
    mi_client->request->set_header_field(
        name  = 'Content-Type'
        value = lv_value ).

    lv_value = 'application/x-git-'
                  && iv_service && '-pack-result'.
    mi_client->request->set_header_field(
        name  = 'Accept'
        value = lv_value ).

    IF mo_digest IS BOUND.
      mo_digest->run( mi_client ).
    ENDIF.

  ENDMETHOD.
ENDCLASS.

Tweak HTTP error messages (#4511)

Tweak HTTP error messages (#4511)

* Tweak HTTP error messages

- Minor changes to error texts
- Add HTTP 422 which can happen on gitlab if you ommit ".git" in the URL

* Lint

Co-authored-by: Lars Hvam <721d1b171982cd84b755f6b25130d2b88cd1f696@hotmail.com>


*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_SERVICES_ABAPGIT
*&---------------------------------------------------------------------*

CLASS lcl_services_abapgit DEFINITION FINAL.
  PUBLIC SECTION.

    CONSTANTS c_abapgit_homepage TYPE string
      VALUE 'http://www.abapgit.org' ##NO_TEXT.
    CONSTANTS c_abapgit_wikipage TYPE string
      VALUE 'https://github.com/larshp/abapGit/wiki'.
    CONSTANTS c_package_abapgit  TYPE devclass
      VALUE '$ABAPGIT'.
    CONSTANTS c_package_plugins  TYPE devclass
      VALUE '$ABAPGIT_PLUGINS'.
    CONSTANTS c_abapgit_url      TYPE string
      VALUE 'https://github.com/larshp/abapGit.git'.
    CONSTANTS c_plugins_url      TYPE string
      VALUE 'https://github.com/larshp/abapGit-plugins.git'.

    CLASS-METHODS open_abapgit_homepage
      RAISING lcx_exception.

    CLASS-METHODS open_abapgit_wikipage
      RAISING lcx_exception.

    CLASS-METHODS install_abapgit
      RAISING lcx_exception lcx_cancel.

    CLASS-METHODS install_abapgit_pi
      RAISING lcx_exception lcx_cancel.

    CLASS-METHODS is_installed
      RETURNING VALUE(rv_installed) TYPE abap_bool.

    CLASS-METHODS is_installed_pi
      RETURNING VALUE(rv_installed) TYPE abap_bool.

  PRIVATE SECTION.

    CLASS-METHODS do_install
      IMPORTING iv_title   TYPE c
                iv_text    TYPE c
                iv_url     TYPE string
                iv_package TYPE devclass
      RAISING   lcx_exception.

ENDCLASS. "lcl_services_abapgit

CLASS lcl_services_abapgit IMPLEMENTATION.

  METHOD open_abapgit_homepage.

    cl_gui_frontend_services=>execute(
      EXPORTING document = c_abapgit_homepage
      EXCEPTIONS OTHERS = 1 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'Opening page in external browser failed.' ).
    ENDIF.

  ENDMETHOD.  "open_abapgit_homepage

  METHOD open_abapgit_wikipage.

    cl_gui_frontend_services=>execute(
      EXPORTING document = c_abapgit_wikipage
      EXCEPTIONS OTHERS = 1 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'Opening page in external browser failed.' ).
    ENDIF.

  ENDMETHOD.  "open_abapgit_wikipage

  METHOD install_abapgit.

    CONSTANTS lc_title TYPE c LENGTH 40 VALUE 'Install abapGit'.
    DATA lv_text       TYPE c LENGTH 100.

    IF is_installed( ) = abap_true.
      lv_text = 'Seems like abapGit package is already installed. No changes to be done'.
      lcl_popups=>popup_to_inform(
        titlebar              = lc_title
        text_message          = lv_text ).
      RETURN.
    ENDIF.

    lv_text = |Confirm to install current version of abapGit to package { c_package_abapgit }|.

    do_install( iv_title   = lc_title
                iv_text    = lv_text
                iv_url     = c_abapgit_url
                iv_package = c_package_abapgit ).

  ENDMETHOD.  "install_abapgit

  METHOD install_abapgit_pi.

    CONSTANTS lc_title TYPE c LENGTH 40 VALUE 'Install abapGit plugins'.
    DATA lv_text       TYPE c LENGTH 100.

    IF is_installed_pi( ) = abap_true.
      lv_text = 'Seems like abapGit plugins package is already installed. No changes to be done'.
      lcl_popups=>popup_to_inform(
        titlebar              = lc_title
        text_message          = lv_text ).
      RETURN.
    ENDIF.

    lv_text = |Confirm to install current version abapGit plugins to package {
               c_package_plugins }|.

    do_install( iv_title   = lc_title
                iv_text    = lv_text
                iv_url     = c_plugins_url
                iv_package = c_package_plugins ).

  ENDMETHOD.  "install_abapgit_pi

  METHOD do_install.

    DATA lo_repo            TYPE REF TO lcl_repo_online.
    DATA lv_answer          TYPE c LENGTH 1.

    lv_answer = lcl_popups=>popup_to_confirm(
      titlebar              = iv_title
      text_question         = iv_text
      text_button_1         = 'Continue'
      text_button_2         = 'Cancel'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer <> '1'.
      RETURN.
    ENDIF.

    IF abap_false = lcl_app=>repo_srv( )->is_repo_installed(
        iv_url              = iv_url
        iv_target_package   = iv_package ).

      lcl_sap_package=>create_local( iv_package ).

      lo_repo = lcl_app=>repo_srv( )->new_online(
        iv_url         = iv_url
        iv_branch_name = 'refs/heads/master'
        iv_package     = iv_package ) ##NO_TEXT.

      lo_repo->status( ). " check for errors
      lo_repo->deserialize( ).
      lcl_services_repo=>toggle_favorite( lo_repo->get_key( ) ).
    ENDIF.

    COMMIT WORK.

  ENDMETHOD.  " do_install.

  METHOD is_installed.

    TRY.
        rv_installed = lcl_app=>repo_srv( )->is_repo_installed( c_abapgit_url ).
        " TODO, alternative checks for presence in the system
      CATCH lcx_exception.
        " cannot be installed anyway in this case, e.g. no connection
        rv_installed = abap_false.
    ENDTRY.

  ENDMETHOD.                    "is_installed

  METHOD is_installed_pi.

    TRY.
        rv_installed = lcl_app=>repo_srv( )->is_repo_installed( c_plugins_url ).
        " TODO, alternative checks for presence in the system
      CATCH lcx_exception.
        " cannot be installed anyway in this case, e.g. no connection
        rv_installed = abap_false.
    ENDTRY.

  ENDMETHOD.                    "is_installed_pi

ENDCLASS. "lcl_services_abapgit

*&---------------------------------------------------------------------*
*&  Include           ZABAPGIT_SERVICES_ABAPGIT
*&---------------------------------------------------------------------*

CLASS lcl_services_abapgit DEFINITION FINAL.
  PUBLIC SECTION.

    CONSTANTS c_abapgit_homepage TYPE string
      VALUE 'http://www.abapgit.org' ##NO_TEXT.
    CONSTANTS c_abapgit_wikipage TYPE string
      VALUE 'http://larshp.github.io/abapGit'.
    CONSTANTS c_package_abapgit  TYPE devclass
      VALUE '$ABAPGIT'.
    CONSTANTS c_package_plugins  TYPE devclass
      VALUE '$ABAPGIT_PLUGINS'.
    CONSTANTS c_abapgit_url      TYPE string
      VALUE 'https://github.com/larshp/abapGit.git'.
    CONSTANTS c_plugins_url      TYPE string
      VALUE 'https://github.com/larshp/abapGit-plugins.git'.

    CLASS-METHODS open_abapgit_homepage
      RAISING lcx_exception.

    CLASS-METHODS open_abapgit_wikipage
      RAISING lcx_exception.

    CLASS-METHODS install_abapgit
      RAISING lcx_exception lcx_cancel.

    CLASS-METHODS install_abapgit_pi
      RAISING lcx_exception lcx_cancel.

    CLASS-METHODS is_installed
      RETURNING VALUE(rv_installed) TYPE abap_bool.

    CLASS-METHODS is_installed_pi
      RETURNING VALUE(rv_installed) TYPE abap_bool.

  PRIVATE SECTION.

    CLASS-METHODS do_install
      IMPORTING iv_title   TYPE c
                iv_text    TYPE c
                iv_url     TYPE string
                iv_package TYPE devclass
      RAISING   lcx_exception.

ENDCLASS. "lcl_services_abapgit

CLASS lcl_services_abapgit IMPLEMENTATION.

  METHOD open_abapgit_homepage.

    cl_gui_frontend_services=>execute(
      EXPORTING document = c_abapgit_homepage
      EXCEPTIONS OTHERS = 1 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'Opening page in external browser failed.' ).
    ENDIF.

  ENDMETHOD.  "open_abapgit_homepage

  METHOD open_abapgit_wikipage.

    cl_gui_frontend_services=>execute(
      EXPORTING document = c_abapgit_wikipage
      EXCEPTIONS OTHERS = 1 ).
    IF sy-subrc <> 0.
      lcx_exception=>raise( 'Opening page in external browser failed.' ).
    ENDIF.

  ENDMETHOD.  "open_abapgit_wikipage

  METHOD install_abapgit.

    CONSTANTS lc_title TYPE c LENGTH 40 VALUE 'Install abapGit'.
    DATA lv_text       TYPE c LENGTH 100.

    IF is_installed( ) = abap_true.
      lv_text = 'Seems like abapGit package is already installed. No changes to be done'.
      lcl_popups=>popup_to_inform(
        titlebar              = lc_title
        text_message          = lv_text ).
      RETURN.
    ENDIF.

    lv_text = |Confirm to install current version of abapGit to package { c_package_abapgit }|.

    do_install( iv_title   = lc_title
                iv_text    = lv_text
                iv_url     = c_abapgit_url
                iv_package = c_package_abapgit ).

  ENDMETHOD.  "install_abapgit

  METHOD install_abapgit_pi.

    CONSTANTS lc_title TYPE c LENGTH 40 VALUE 'Install abapGit plugins'.
    DATA lv_text       TYPE c LENGTH 100.

    IF is_installed_pi( ) = abap_true.
      lv_text = 'Seems like abapGit plugins package is already installed. No changes to be done'.
      lcl_popups=>popup_to_inform(
        titlebar              = lc_title
        text_message          = lv_text ).
      RETURN.
    ENDIF.

    lv_text = |Confirm to install current version abapGit plugins to package {
               c_package_plugins }|.

    do_install( iv_title   = lc_title
                iv_text    = lv_text
                iv_url     = c_plugins_url
                iv_package = c_package_plugins ).

  ENDMETHOD.  "install_abapgit_pi

  METHOD do_install.

    DATA lo_repo            TYPE REF TO lcl_repo_online.
    DATA lv_answer          TYPE c LENGTH 1.

    lv_answer = lcl_popups=>popup_to_confirm(
      titlebar              = iv_title
      text_question         = iv_text
      text_button_1         = 'Continue'
      text_button_2         = 'Cancel'
      default_button        = '2'
      display_cancel_button = abap_false ).                 "#EC NOTEXT

    IF lv_answer <> '1'.
      RETURN.
    ENDIF.

    IF abap_false = lcl_app=>repo_srv( )->is_repo_installed(
        iv_url              = iv_url
        iv_target_package   = iv_package ).

      lcl_sap_package=>create_local( iv_package ).

      lo_repo = lcl_app=>repo_srv( )->new_online(
        iv_url         = iv_url
        iv_branch_name = 'refs/heads/master'
        iv_package     = iv_package ) ##NO_TEXT.

      lo_repo->status( ). " check for errors
      lo_repo->deserialize( ).
      lcl_services_repo=>toggle_favorite( lo_repo->get_key( ) ).
    ENDIF.

    COMMIT WORK.

  ENDMETHOD.  " do_install.

  METHOD is_installed.

    TRY.
        rv_installed = lcl_app=>repo_srv( )->is_repo_installed( c_abapgit_url ).
        " TODO, alternative checks for presence in the system
      CATCH lcx_exception.
        " cannot be installed anyway in this case, e.g. no connection
        rv_installed = abap_false.
    ENDTRY.

  ENDMETHOD.                    "is_installed

  METHOD is_installed_pi.

    TRY.
        rv_installed = lcl_app=>repo_srv( )->is_repo_installed( c_plugins_url ).
        " TODO, alternative checks for presence in the system
      CATCH lcx_exception.
        " cannot be installed anyway in this case, e.g. no connection
        rv_installed = abap_false.
    ENDTRY.

  ENDMETHOD.                    "is_installed_pi

ENDCLASS. "lcl_services_abapgit

update link to documentation

update link to documentation


CLASS zcl_ags_pack DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    TYPES:
      BEGIN OF ty_object,
        sha1 TYPE zags_sha1,
        type TYPE zags_type,
        data TYPE xstring,
      END OF ty_object .
    TYPES:
      ty_objects_tt TYPE STANDARD TABLE OF ty_object WITH DEFAULT KEY .
    TYPES:
      ty_adler32 TYPE x LENGTH 4 .

    CLASS-METHODS to_object
      IMPORTING
        !ii_object       TYPE REF TO zif_ags_object
      RETURNING
        VALUE(rs_object) TYPE ty_object
      RAISING
        zcx_ags_error .
    CLASS-METHODS encode
      IMPORTING
        !it_objects    TYPE ty_objects_tt
      RETURNING
        VALUE(rv_data) TYPE xstring .
    CLASS-METHODS decode
      IMPORTING
        !iv_data          TYPE xstring
      RETURNING
        VALUE(rt_objects) TYPE ty_objects_tt .
    CLASS-METHODS save
      IMPORTING
        !iv_repo    TYPE zags_repos-repo
        !it_objects TYPE ty_objects_tt
      RAISING
        zcx_ags_error .
    CLASS-METHODS explode
      IMPORTING
        !iv_repo          TYPE zags_repos-repo
        !ii_object        TYPE REF TO zif_ags_object
        !iv_deepen        TYPE i DEFAULT 0
      RETURNING
        VALUE(rt_objects) TYPE ty_objects_tt
      RAISING
        zcx_ags_error .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_AGS_PACK IMPLEMENTATION.

  METHOD decode.

    CALL METHOD ('\PROGRAM=ZABAPGIT\CLASS=LCL_GIT_PACK')=>decode
      EXPORTING
        iv_data    = iv_data
      RECEIVING
        rt_objects = rt_objects.

  ENDMETHOD.

  METHOD encode.

    CALL METHOD ('\PROGRAM=ZABAPGIT\CLASS=LCL_GIT_PACK')=>encode
      EXPORTING
        it_objects = it_objects
      RECEIVING
        rv_data    = rv_data.

  ENDMETHOD.

  METHOD explode.

    TYPES: BEGIN OF ty_visit,
             object TYPE REF TO zif_ags_object,
             deepen TYPE i,
           END OF ty_visit.

    DEFINE _visit_commit.
      CREATE OBJECT lo_parent EXPORTING iv_repo = iv_repo iv_sha1 = &1.
      APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_new>.
      <ls_new>-object = lo_parent.
      <ls_new>-deepen = <ls_visit>-deepen - 1.
    END-OF-DEFINITION.

    DEFINE _visit_tree.
      CREATE OBJECT lo_sub EXPORTING iv_repo = iv_repo iv_sha1 = &1.
      APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_new>.
      <ls_new>-object = lo_sub.
    END-OF-DEFINITION.

    DEFINE _visit_blob.
      APPEND &1 TO lt_blobs_sha1.
    END-OF-DEFINITION.

    DATA: lo_tree       TYPE REF TO zcl_ags_obj_tree,
          lt_files      TYPE zcl_ags_obj_tree=>ty_tree_tt,
          lo_sub        TYPE REF TO zcl_ags_obj_tree,
          lt_blobs_sha1 TYPE zags_sha1_tt,
          li_object     TYPE REF TO zif_ags_object,
          lt_blobs      TYPE zcl_ags_obj_blob=>ty_list,
          lo_parent     TYPE REF TO zcl_ags_obj_commit,
          ls_commit     TYPE zcl_ags_obj_commit=>ty_commit,
          lt_visit      TYPE STANDARD TABLE OF ty_visit WITH DEFAULT KEY,
          lo_commit     TYPE REF TO zcl_ags_obj_commit.

    FIELD-SYMBOLS: <ls_visit> LIKE LINE OF lt_visit,
                   <ls_new>   LIKE LINE OF lt_visit,
                   <ls_file>  LIKE LINE OF lt_files.

    ASSERT NOT iv_repo IS INITIAL.

    APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_visit>.
    <ls_visit>-object = ii_object.
    <ls_visit>-deepen = iv_deepen.

    LOOP AT lt_visit ASSIGNING <ls_visit>.
      APPEND to_object( <ls_visit>-object ) TO rt_objects.

      IF <ls_visit>-object->type( ) = zif_ags_constants=>c_type-commit.
        lo_commit ?= <ls_visit>-object.
        ls_commit = lo_commit->get( ).

        _visit_tree ls_commit-tree.

        IF <ls_visit>-deepen <> 1 AND NOT ls_commit-parent IS INITIAL.
          _visit_commit ls_commit-parent.

          IF NOT ls_commit-parent2 IS INITIAL.
            _visit_commit ls_commit-parent2.
          ENDIF.
        ENDIF.
      ELSEIF <ls_visit>-object->type( ) = zif_ags_constants=>c_type-tree.
        lo_tree ?= <ls_visit>-object.
        lt_files = lo_tree->get_files( ).
        LOOP AT lt_files ASSIGNING <ls_file>.
          IF <ls_file>-chmod = zcl_ags_obj_tree=>c_chmod-dir.
            _visit_tree <ls_file>-sha1.
          ELSE.
            _visit_blob <ls_file>-sha1.
          ENDIF.
        ENDLOOP.
      ENDIF.
    ENDLOOP.

    lt_blobs = zcl_ags_obj_blob=>constructor_mass(
      iv_repo = iv_repo
      it_sha1 = lt_blobs_sha1 ).
    LOOP AT lt_blobs INTO li_object.
      APPEND to_object( li_object ) TO rt_objects.
    ENDLOOP.

  ENDMETHOD.

  METHOD save.

    DATA: li_object TYPE REF TO zif_ags_object,
          lo_blob   TYPE REF TO zcl_ags_obj_blob,
          lo_tree   TYPE REF TO zcl_ags_obj_tree,
          lo_commit TYPE REF TO zcl_ags_obj_commit.

    FIELD-SYMBOLS: <ls_object> LIKE LINE OF it_objects.

    ASSERT NOT iv_repo IS INITIAL.

    LOOP AT it_objects ASSIGNING <ls_object>.
      CASE <ls_object>-type.
        WHEN zif_ags_constants=>c_type-blob.
          CREATE OBJECT lo_blob
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_blob.
        WHEN zif_ags_constants=>c_type-tree.
          CREATE OBJECT lo_tree
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_tree.
        WHEN zif_ags_constants=>c_type-commit.
          CREATE OBJECT lo_commit
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_commit.
        WHEN OTHERS.
          RAISE EXCEPTION TYPE zcx_ags_error
            EXPORTING
              textid = zcx_ags_error=>m009.
      ENDCASE.

      li_object->deserialize( <ls_object>-data ).

      ASSERT li_object->sha1( ) = <ls_object>-sha1.

      li_object->save( ).

    ENDLOOP.

  ENDMETHOD.

  METHOD to_object.

    rs_object-sha1 = ii_object->sha1( ).
    rs_object-type = ii_object->type( ).
    rs_object-data = ii_object->serialize( ).

  ENDMETHOD.
ENDCLASS.

CLASS zcl_ags_pack DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    TYPES:
      BEGIN OF ty_object,
        sha1 TYPE zags_sha1,
        type TYPE zags_type,
        data TYPE xstring,
      END OF ty_object .
    TYPES:
      ty_objects_tt TYPE STANDARD TABLE OF ty_object WITH DEFAULT KEY .
    TYPES:
      ty_adler32 TYPE x LENGTH 4 .

    CLASS-METHODS to_object
      IMPORTING
        !ii_object       TYPE REF TO zif_ags_object
      RETURNING
        VALUE(rs_object) TYPE ty_object
      RAISING
        zcx_ags_error .
    CLASS-METHODS encode
      IMPORTING
        !it_objects    TYPE ty_objects_tt
      RETURNING
        VALUE(rv_data) TYPE xstring .
    CLASS-METHODS decode
      IMPORTING
        !iv_data          TYPE xstring
      RETURNING
        VALUE(rt_objects) TYPE ty_objects_tt .
    CLASS-METHODS save
      IMPORTING
        !iv_repo    TYPE zags_repos-repo
        !it_objects TYPE ty_objects_tt
      RAISING
        zcx_ags_error .
    CLASS-METHODS explode
      IMPORTING
        !iv_repo          TYPE zags_repos-repo
        !ii_object        TYPE REF TO zif_ags_object
        !iv_deepen        TYPE i DEFAULT 0
      RETURNING
        VALUE(rt_objects) TYPE ty_objects_tt
      RAISING
        zcx_ags_error .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS ZCL_AGS_PACK IMPLEMENTATION.

  METHOD decode.

    rt_objects = zcl_abapgit_git_pack=>decode( iv_data ).

  ENDMETHOD.

  METHOD encode.

    rv_data = zcl_abapgit_git_pack=>encode( it_objects ).

  ENDMETHOD.

  METHOD explode.

    TYPES: BEGIN OF ty_visit,
             object TYPE REF TO zif_ags_object,
             deepen TYPE i,
           END OF ty_visit.

    DEFINE _visit_commit.
      CREATE OBJECT lo_parent EXPORTING iv_repo = iv_repo iv_sha1 = &1.
      APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_new>.
      <ls_new>-object = lo_parent.
      <ls_new>-deepen = <ls_visit>-deepen - 1.
    END-OF-DEFINITION.

    DEFINE _visit_tree.
      CREATE OBJECT lo_sub EXPORTING iv_repo = iv_repo iv_sha1 = &1.
      APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_new>.
      <ls_new>-object = lo_sub.
    END-OF-DEFINITION.

    DEFINE _visit_blob.
      APPEND &1 TO lt_blobs_sha1.
    END-OF-DEFINITION.

    DATA: lo_tree       TYPE REF TO zcl_ags_obj_tree,
          lt_files      TYPE zcl_ags_obj_tree=>ty_tree_tt,
          lo_sub        TYPE REF TO zcl_ags_obj_tree,
          lt_blobs_sha1 TYPE zags_sha1_tt,
          li_object     TYPE REF TO zif_ags_object,
          lt_blobs      TYPE zcl_ags_obj_blob=>ty_list,
          lo_parent     TYPE REF TO zcl_ags_obj_commit,
          ls_commit     TYPE zcl_ags_obj_commit=>ty_commit,
          lt_visit      TYPE STANDARD TABLE OF ty_visit WITH DEFAULT KEY,
          lo_commit     TYPE REF TO zcl_ags_obj_commit.

    FIELD-SYMBOLS: <ls_visit> LIKE LINE OF lt_visit,
                   <ls_new>   LIKE LINE OF lt_visit,
                   <ls_file>  LIKE LINE OF lt_files.

    ASSERT NOT iv_repo IS INITIAL.

    APPEND INITIAL LINE TO lt_visit ASSIGNING <ls_visit>.
    <ls_visit>-object = ii_object.
    <ls_visit>-deepen = iv_deepen.

    LOOP AT lt_visit ASSIGNING <ls_visit>.
      APPEND to_object( <ls_visit>-object ) TO rt_objects.

      IF <ls_visit>-object->type( ) = zif_ags_constants=>c_type-commit.
        lo_commit ?= <ls_visit>-object.
        ls_commit = lo_commit->get( ).

        _visit_tree ls_commit-tree.

        IF <ls_visit>-deepen <> 1 AND NOT ls_commit-parent IS INITIAL.
          _visit_commit ls_commit-parent.

          IF NOT ls_commit-parent2 IS INITIAL.
            _visit_commit ls_commit-parent2.
          ENDIF.
        ENDIF.
      ELSEIF <ls_visit>-object->type( ) = zif_ags_constants=>c_type-tree.
        lo_tree ?= <ls_visit>-object.
        lt_files = lo_tree->get_files( ).
        LOOP AT lt_files ASSIGNING <ls_file>.
          IF <ls_file>-chmod = zcl_ags_obj_tree=>c_chmod-dir.
            _visit_tree <ls_file>-sha1.
          ELSE.
            _visit_blob <ls_file>-sha1.
          ENDIF.
        ENDLOOP.
      ENDIF.
    ENDLOOP.

    lt_blobs = zcl_ags_obj_blob=>constructor_mass(
      iv_repo = iv_repo
      it_sha1 = lt_blobs_sha1 ).
    LOOP AT lt_blobs INTO li_object.
      APPEND to_object( li_object ) TO rt_objects.
    ENDLOOP.

  ENDMETHOD.

  METHOD save.

    DATA: li_object TYPE REF TO zif_ags_object,
          lo_blob   TYPE REF TO zcl_ags_obj_blob,
          lo_tree   TYPE REF TO zcl_ags_obj_tree,
          lo_commit TYPE REF TO zcl_ags_obj_commit.

    FIELD-SYMBOLS: <ls_object> LIKE LINE OF it_objects.

    ASSERT NOT iv_repo IS INITIAL.

    LOOP AT it_objects ASSIGNING <ls_object>.
      CASE <ls_object>-type.
        WHEN zif_ags_constants=>c_type-blob.
          CREATE OBJECT lo_blob
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_blob.
        WHEN zif_ags_constants=>c_type-tree.
          CREATE OBJECT lo_tree
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_tree.
        WHEN zif_ags_constants=>c_type-commit.
          CREATE OBJECT lo_commit
            EXPORTING
              iv_repo = iv_repo.
          li_object = lo_commit.
        WHEN OTHERS.
          RAISE EXCEPTION TYPE zcx_ags_error
            EXPORTING
              textid = zcx_ags_error=>m009.
      ENDCASE.

      li_object->deserialize( <ls_object>-data ).

      ASSERT li_object->sha1( ) = <ls_object>-sha1.

      li_object->save( ).

    ENDLOOP.

  ENDMETHOD.

  METHOD to_object.

    rs_object-sha1 = ii_object->sha1( ).
    rs_object-type = ii_object->type( ).
    rs_object-data = ii_object->serialize( ).

  ENDMETHOD.
ENDCLASS.

fix dump

fix dump


*&---------------------------------------------------------------------*
*& Include zabapgit_object_iamu
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_iamu DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_iamu DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.
    ALIASES mo_files FOR lif_object~mo_files.

  PRIVATE SECTION.
    TYPES: BEGIN OF ty_internet_appl_comp_binary,
             attributes TYPE w3mimeattr,
             source     TYPE w3mimetabtype,
             length     TYPE i,
           END OF ty_internet_appl_comp_binary.

    DATA: mo_mime_api TYPE REF TO if_w3_api_mime.

    METHODS:
      load_mime_api
        RAISING
          zcx_abapgit_exception,

      read
        RETURNING
          VALUE(rs_internet_appl_comp_binary) TYPE ty_internet_appl_comp_binary
        RAISING
          zcx_abapgit_exception,

      save
        IMPORTING
          is_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary
        RAISING
          zcx_abapgit_exception,

      release_lock
        RAISING
          zcx_abapgit_exception.

ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_object_iamu IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_iamu IMPLEMENTATION.

  METHOD lif_object~has_changed_since.

    rv_changed = abap_true.

  ENDMETHOD.

  METHOD lif_object~changed_by.

    rv_user = c_user_unknown.

  ENDMETHOD.

  METHOD lif_object~get_metadata.

    rs_metadata = get_metadata( ).

  ENDMETHOD.

  METHOD lif_object~serialize.

    DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.

    ls_internet_appl_comp_binary = read( ).

    io_xml->add( iv_name = 'IAMU'
                 ig_data = ls_internet_appl_comp_binary ).

  ENDMETHOD.

  METHOD lif_object~deserialize.

    DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.

    io_xml->read(
      EXPORTING
        iv_name = 'IAMU'
      CHANGING
        cg_data = ls_internet_appl_comp_binary ).

    ls_internet_appl_comp_binary-attributes-devclass = iv_package.

    save( ls_internet_appl_comp_binary ).

  ENDMETHOD.

  METHOD lif_object~delete.

    load_mime_api( ).

    mo_mime_api->if_w3_api_object~set_changeable(
      EXPORTING
        p_changeable                 = abap_true
      EXCEPTIONS
        action_cancelled             = 1
        object_locked_by_other_user  = 2
        permission_failure           = 3
        object_already_changeable    = 4
        object_already_unlocked      = 5
        object_just_created          = 6
        object_deleted               = 7
        object_modified              = 8
        object_not_existing          = 9
        object_invalid               = 10
        error_occured                = 11
        content_data_error           = 12
        OTHERS                       = 13 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~delete(
      EXCEPTIONS
        object_not_empty      = 1
        object_not_changeable = 2
        object_invalid        = 3
        error_occured         = 4
        OTHERS                = 5 ).
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~delete| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~save(
      EXCEPTIONS
        object_invalid        = 1
        object_not_changeable = 2
        action_cancelled      = 3
        permission_failure    = 4
        not_changed           = 5
        data_invalid          = 6
        error_occured         = 7
        OTHERS                = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~save| ).
    ENDIF.

  ENDMETHOD.

  METHOD lif_object~exists.

    DATA: ls_mime_name TYPE iacikeym.

    ls_mime_name = ms_item-obj_name.

    cl_w3_api_mime=>s_check_exist(
      EXPORTING
        p_mime_name = ls_mime_name
      IMPORTING
        p_exists    = rv_bool ).

  ENDMETHOD.

  METHOD lif_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation   = 'SHOW'
        object_name = ms_item-obj_name
        object_type = ms_item-obj_type.

  ENDMETHOD.

  METHOD lif_object~compare_to_remote_version.

    CREATE OBJECT ro_comparison_result TYPE lcl_comparison_null.

  ENDMETHOD.

  METHOD load_mime_api.

    DATA: ls_mime_name TYPE iacikeym.

    ls_mime_name = ms_item-obj_name.

    cl_w3_api_mime=>if_w3_api_mime~load(
      EXPORTING
        p_mime_name         = ls_mime_name
      IMPORTING
        p_mime              = mo_mime_api
      EXCEPTIONS
        object_not_existing = 1
        permission_failure  = 2
        data_corrupt        = 3
        error_occured       = 4
        OTHERS              = 6 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'error from if_w3_api_mime~load' ).
    ENDIF.

  ENDMETHOD.

  METHOD read.

    load_mime_api( ).

    mo_mime_api->get_attributes(
      IMPORTING
        p_attributes   = rs_internet_appl_comp_binary-attributes
      EXCEPTIONS
        object_invalid = 1
        mime_deleted   = 2
        error_occured  = 3
        OTHERS         = 4 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_attributes| ).
    ENDIF.

    CLEAR: rs_internet_appl_comp_binary-attributes-chname,
           rs_internet_appl_comp_binary-attributes-tdate,
           rs_internet_appl_comp_binary-attributes-ttime,
           rs_internet_appl_comp_binary-attributes-devclass.

    mo_mime_api->get_source(
      IMPORTING
        p_source       = rs_internet_appl_comp_binary-source
        p_datalength   = rs_internet_appl_comp_binary-length
      EXCEPTIONS
        object_invalid = 1
        mime_deleted   = 2
        error_occured  = 3
        OTHERS         = 4 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_source| ).
    ENDIF.

  ENDMETHOD.

  METHOD save.

    cl_w3_api_mime=>if_w3_api_mime~create_new(
      EXPORTING
        p_mime_data             = is_internet_appl_comp_binary-attributes
        p_mime_content          = is_internet_appl_comp_binary-source
        p_datalength            = is_internet_appl_comp_binary-length
      IMPORTING
        p_mime                  = mo_mime_api
      EXCEPTIONS
        object_already_existing = 1
        object_just_created     = 2
        not_authorized          = 3
        undefined_name          = 4
        author_not_existing     = 5
        action_cancelled        = 6
        error_occured           = 7
        OTHERS                  = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~create_ne| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~save(
      EXCEPTIONS
        object_invalid        = 1
        object_not_changeable = 2
        action_cancelled      = 3
        permission_failure    = 4
        not_changed           = 5
        data_invalid          = 6
        error_occured         = 7
        OTHERS                = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~save| ).
    ENDIF.

    release_lock( ).

  ENDMETHOD.

  METHOD release_lock.

    " As a side effect this method removes also existing locks
    mo_mime_api->if_w3_api_object~set_changeable(
      EXPORTING
        p_changeable                 = abap_false
      EXCEPTIONS
        action_cancelled             = 1
        object_locked_by_other_user  = 2
        permission_failure           = 3
        object_already_changeable    = 4
        object_already_unlocked      = 5
        object_just_created          = 6
        object_deleted               = 7
        object_modified              = 8
        object_not_existing          = 9
        object_invalid               = 10
        error_occured                = 11
        content_data_error           = 12
        OTHERS                       = 13 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
    ENDIF.

  ENDMETHOD.

ENDCLASS.

*&---------------------------------------------------------------------*
*& Include zabapgit_object_iamu
*&---------------------------------------------------------------------*

*----------------------------------------------------------------------*
*       CLASS lcl_object_iamu DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_iamu DEFINITION INHERITING FROM lcl_objects_super FINAL.

  PUBLIC SECTION.
    INTERFACES lif_object.
    ALIASES mo_files FOR lif_object~mo_files.

  PRIVATE SECTION.
    TYPES: BEGIN OF ty_internet_appl_comp_binary,
             attributes TYPE w3mimeattr,
             source     TYPE w3mimetabtype,
             length     TYPE i,
           END OF ty_internet_appl_comp_binary.

    DATA: mo_mime_api TYPE REF TO if_w3_api_mime.

    METHODS:
      load_mime_api
        RAISING
          zcx_abapgit_exception,

      read
        RETURNING
          VALUE(rs_internet_appl_comp_binary) TYPE ty_internet_appl_comp_binary
        RAISING
          zcx_abapgit_exception,

      save
        IMPORTING
          is_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary
        RAISING
          zcx_abapgit_exception,

      release_lock
        RAISING
          zcx_abapgit_exception.

ENDCLASS.

*----------------------------------------------------------------------*
*       CLASS lcl_object_iamu IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
CLASS lcl_object_iamu IMPLEMENTATION.

  METHOD lif_object~has_changed_since.

    rv_changed = abap_true.

  ENDMETHOD.

  METHOD lif_object~changed_by.

    rv_user = c_user_unknown.

  ENDMETHOD.

  METHOD lif_object~get_metadata.

    rs_metadata = get_metadata( ).

  ENDMETHOD.

  METHOD lif_object~serialize.

    DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.

    ls_internet_appl_comp_binary = read( ).

    io_xml->add( iv_name = 'IAMU'
                 ig_data = ls_internet_appl_comp_binary ).

  ENDMETHOD.

  METHOD lif_object~deserialize.

    DATA: ls_internet_appl_comp_binary TYPE ty_internet_appl_comp_binary.

    io_xml->read(
      EXPORTING
        iv_name = 'IAMU'
      CHANGING
        cg_data = ls_internet_appl_comp_binary ).

    ls_internet_appl_comp_binary-attributes-devclass = iv_package.

    save( ls_internet_appl_comp_binary ).

  ENDMETHOD.

  METHOD lif_object~delete.

    load_mime_api( ).

    mo_mime_api->if_w3_api_object~set_changeable(
      EXPORTING
        p_changeable                 = abap_true
      EXCEPTIONS
        action_cancelled             = 1
        object_locked_by_other_user  = 2
        permission_failure           = 3
        object_already_changeable    = 4
        object_already_unlocked      = 5
        object_just_created          = 6
        object_deleted               = 7
        object_modified              = 8
        object_not_existing          = 9
        object_invalid               = 10
        error_occured                = 11
        OTHERS                       = 12 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~delete(
      EXCEPTIONS
        object_not_empty      = 1
        object_not_changeable = 2
        object_invalid        = 3
        error_occured         = 4
        OTHERS                = 5 ).
    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~delete| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~save(
      EXCEPTIONS
        object_invalid        = 1
        object_not_changeable = 2
        action_cancelled      = 3
        permission_failure    = 4
        not_changed           = 5
        data_invalid          = 6
        error_occured         = 7
        OTHERS                = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~save| ).
    ENDIF.

  ENDMETHOD.

  METHOD lif_object~exists.

    DATA: ls_mime_name TYPE iacikeym.

    ls_mime_name = ms_item-obj_name.

    cl_w3_api_mime=>s_check_exist(
      EXPORTING
        p_mime_name = ls_mime_name
      IMPORTING
        p_exists    = rv_bool ).

  ENDMETHOD.

  METHOD lif_object~jump.

    CALL FUNCTION 'RS_TOOL_ACCESS'
      EXPORTING
        operation   = 'SHOW'
        object_name = ms_item-obj_name
        object_type = ms_item-obj_type.

  ENDMETHOD.

  METHOD lif_object~compare_to_remote_version.

    CREATE OBJECT ro_comparison_result TYPE lcl_comparison_null.

  ENDMETHOD.

  METHOD load_mime_api.

    DATA: ls_mime_name TYPE iacikeym.

    ls_mime_name = ms_item-obj_name.

    cl_w3_api_mime=>if_w3_api_mime~load(
      EXPORTING
        p_mime_name         = ls_mime_name
      IMPORTING
        p_mime              = mo_mime_api
      EXCEPTIONS
        object_not_existing = 1
        permission_failure  = 2
        data_corrupt        = 3
        error_occured       = 4
        OTHERS              = 6 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( 'error from if_w3_api_mime~load' ).
    ENDIF.

  ENDMETHOD.

  METHOD read.

    load_mime_api( ).

    mo_mime_api->get_attributes(
      IMPORTING
        p_attributes   = rs_internet_appl_comp_binary-attributes
      EXCEPTIONS
        object_invalid = 1
        mime_deleted   = 2
        error_occured  = 3
        OTHERS         = 4 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_attributes| ).
    ENDIF.

    CLEAR: rs_internet_appl_comp_binary-attributes-chname,
           rs_internet_appl_comp_binary-attributes-tdate,
           rs_internet_appl_comp_binary-attributes-ttime,
           rs_internet_appl_comp_binary-attributes-devclass.

    mo_mime_api->get_source(
      IMPORTING
        p_source       = rs_internet_appl_comp_binary-source
        p_datalength   = rs_internet_appl_comp_binary-length
      EXCEPTIONS
        object_invalid = 1
        mime_deleted   = 2
        error_occured  = 3
        OTHERS         = 4 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~get_source| ).
    ENDIF.

  ENDMETHOD.

  METHOD save.

    cl_w3_api_mime=>if_w3_api_mime~create_new(
      EXPORTING
        p_mime_data             = is_internet_appl_comp_binary-attributes
        p_mime_content          = is_internet_appl_comp_binary-source
        p_datalength            = is_internet_appl_comp_binary-length
      IMPORTING
        p_mime                  = mo_mime_api
      EXCEPTIONS
        object_already_existing = 1
        object_just_created     = 2
        not_authorized          = 3
        undefined_name          = 4
        author_not_existing     = 5
        action_cancelled        = 6
        error_occured           = 7
        OTHERS                  = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~create_ne| ).
    ENDIF.

    mo_mime_api->if_w3_api_object~save(
      EXCEPTIONS
        object_invalid        = 1
        object_not_changeable = 2
        action_cancelled      = 3
        permission_failure    = 4
        not_changed           = 5
        data_invalid          = 6
        error_occured         = 7
        OTHERS                = 8 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~save| ).
    ENDIF.

    release_lock( ).

  ENDMETHOD.

  METHOD release_lock.

    " As a side effect this method removes also existing locks
    mo_mime_api->if_w3_api_object~set_changeable(
      EXPORTING
        p_changeable                 = abap_false
      EXCEPTIONS
        action_cancelled             = 1
        object_locked_by_other_user  = 2
        permission_failure           = 3
        object_already_changeable    = 4
        object_already_unlocked      = 5
        object_just_created          = 6
        object_deleted               = 7
        object_modified              = 8
        object_not_existing          = 9
        object_invalid               = 10
        error_occured                = 11
        OTHERS                       = 12 ).

    IF sy-subrc <> 0.
      zcx_abapgit_exception=>raise( |Error from if_w3_api_mime~set_changeable| ).
    ENDIF.

  ENDMETHOD.

ENDCLASS.

Remove not existing exception (7.31 and lower)