From f5dfd328f584204e72b0aa5210d8b69d3d834ba8 Mon Sep 17 00:00:00 2001 From: Alexander Tsybulsky Date: Wed, 1 Jul 2020 09:16:52 +0300 Subject: [PATCH] Dialogs: add online repo HTML form (#3533) * first draft * progress ... * html->create() * more progress * and more * getting closer * wiring * refactor popup_search_help * refactor create_package to services * fix services->new_online after rebase * linter fixes * more linter fixes * straing map util * error handling * submits for side actions (ugly ... but ...) * cleanups * linter fixes * refactor form values logic * linter fix * review fixes * remove unused var after rebase * review fixes, better side action layout * linter fix * minor css tweaks --- src/ui/core/zcl_abapgit_html.clas.abap | 10 + src/ui/zabapgit_css_common.w3mi.data.css | 122 +++++- .../zcl_abapgit_gui_page_addonline.clas.abap | 313 ++++++++++++++ .../zcl_abapgit_gui_page_addonline.clas.xml | 16 + src/ui/zcl_abapgit_gui_router.clas.abap | 6 +- .../zcl_abapgit_html_action_utils.clas.abap | 15 + src/ui/zcl_abapgit_html_form.clas.abap | 386 ++++++++++++++++++ src/ui/zcl_abapgit_html_form.clas.xml | 16 + src/ui/zcl_abapgit_popups.clas.abap | 33 ++ src/ui/zcl_abapgit_services_basis.clas.abap | 43 ++ src/ui/zcl_abapgit_services_basis.clas.xml | 16 + src/ui/zcl_abapgit_services_repo.clas.abap | 39 +- ..._abapgit_ui_injector.clas.testclasses.abap | 4 + src/ui/zif_abapgit_popups.intf.abap | 9 +- src/ui/zif_abapgit_services_repo.intf.abap | 15 + src/ui/zif_abapgit_services_repo.intf.xml | 15 + src/utils/zcl_abapgit_string_map.clas.abap | 160 ++++++++ src/utils/zcl_abapgit_string_map.clas.xml | 16 + src/zcl_abapgit_repo_srv.clas.abap | 14 +- 19 files changed, 1218 insertions(+), 30 deletions(-) create mode 100644 src/ui/zcl_abapgit_gui_page_addonline.clas.abap create mode 100644 src/ui/zcl_abapgit_gui_page_addonline.clas.xml create mode 100644 src/ui/zcl_abapgit_html_form.clas.abap create mode 100644 src/ui/zcl_abapgit_html_form.clas.xml create mode 100644 src/ui/zcl_abapgit_services_basis.clas.abap create mode 100644 src/ui/zcl_abapgit_services_basis.clas.xml create mode 100644 src/ui/zif_abapgit_services_repo.intf.abap create mode 100644 src/ui/zif_abapgit_services_repo.intf.xml create mode 100644 src/utils/zcl_abapgit_string_map.clas.abap create mode 100644 src/utils/zcl_abapgit_string_map.clas.xml diff --git a/src/ui/core/zcl_abapgit_html.clas.abap b/src/ui/core/zcl_abapgit_html.clas.abap index 4b2f38d2c..c7683ffd7 100644 --- a/src/ui/core/zcl_abapgit_html.clas.abap +++ b/src/ui/core/zcl_abapgit_html.clas.abap @@ -30,6 +30,11 @@ CLASS zcl_abapgit_html DEFINITION !iv_hint TYPE string OPTIONAL !iv_class TYPE string OPTIONAL !iv_onclick TYPE string OPTIONAL . + + CLASS-METHODS create + RETURNING + VALUE(ro_html) TYPE REF TO zcl_abapgit_html. + PROTECTED SECTION. PRIVATE SECTION. @@ -214,6 +219,11 @@ CLASS ZCL_ABAPGIT_HTML IMPLEMENTATION. ENDMETHOD. + METHOD create. + CREATE OBJECT ro_html. + ENDMETHOD. + + METHOD icon. DATA: lv_hint TYPE string, diff --git a/src/ui/zabapgit_css_common.w3mi.data.css b/src/ui/zabapgit_css_common.w3mi.data.css index dc8710c7a..87fb03df4 100644 --- a/src/ui/zabapgit_css_common.w3mi.data.css +++ b/src/ui/zabapgit_css_common.w3mi.data.css @@ -29,7 +29,7 @@ sup { } input, textarea, select { - padding: 3px 6px; + padding: 3px 0.5em; border: 1px solid; } input:focus, textarea:focus { @@ -64,6 +64,14 @@ span.separator { .inline { display: inline; } .hidden { visibility: hidden; } .nodisplay { display: none } +.m-em5-sides { margin-left: 0.5em; margin-right: 0.5em } +.w600px { width: 600px } +.wmax600px { max-width: 600px } +.auto-center { /* use with max-width */ + width: 100%; + margin-left: auto; + margin-right: auto; +} /* PANELS */ div.panel { @@ -1006,3 +1014,115 @@ table.settings_tab input { text-align: center; } settings_tab tr:first-child td { border-top: 0px; } + +/* DIALOGS */ + +.dialog input::placeholder { color: #ccc } +.dialog input:-ms-input-placeholder { color: #ccc } +.dialog { + border: 1px solid #cbcbcb; + background-color: #f4f4f4; + padding: 1em 1em; + border-radius: 4px; + text-align: left; +} +.dialog li { + padding: 5px 10px; + display: block; + list-style: none; + position: relative; +} +.dialog li.dialog-commands { + text-align: right; + margin-top: 12px; +} +.dialog li.dialog-commands a { + border: 1px solid #ccc; + cursor: pointer; + text-decoration: none; + padding: 6px 12px; + border-radius: 3px; + font-size: smaller; +} +.dialog li.dialog-commands input[type="submit"] { + border: 1px solid #ccc; + padding: 6px 12px; + border-radius: 3px; + cursor: pointer; +} +.dialog li.dialog-commands input[type="submit"].main { + background-color: #64a8ff; + color: #fff; + border: 1px solid transparent; +} +.dialog label { + color: #444; + display: block; + font-size: 90%; + margin-top: 6px; + margin-bottom: 6px; + padding-left: 0.5em; +} +.dialog label em { + color: #64a8ff; +} +.dialog li.error small { + color: #ff5959; + display: block; + font-size: 75%; + margin: 4px 0px; + padding-left: 0.5em; +} +.dialog li.error input[type="text"] { + border-color: #ff5959; +} +.dialog .radio-container { + border: 1px solid #ddd; + background-color: white; + display: inline-block; + padding: 4px; + border-radius: 3px; +} +.dialog input[type="checkbox"] + label { + display: inline-block; +} +.dialog input[type="text"] { + width: 100%; + box-sizing : border-box; + height: 2.5em; +} +.dialog .radio-container input[type="radio"] { + visibility: hidden; + display: none; + height: 0px; + width: 0px; +} +.dialog .radio-container input[type="radio"] + label { + color: #808080; + border: 1px solid transparent; + cursor: pointer; + width: auto; + margin: 0px; + padding: 3px 8px; + border-radius: 2px; + display: inline-block; +} +.dialog .radio-container input[type="radio"]:checked + label { + background-color: #64a8ff; + color: #fff; + border: 1px solid transparent; +} +.dialog li.with-command div.input-container { + display: table-cell; + width: 100%; +} +.dialog li.with-command div.command-container { + display: table-cell; +} +.dialog li.with-command input[type="submit"] { + height: 2.5em; +} +.dialog li.with-command input[type="submit"]:hover { + background-color: #64a8ff; + color: #fff; +} diff --git a/src/ui/zcl_abapgit_gui_page_addonline.clas.abap b/src/ui/zcl_abapgit_gui_page_addonline.clas.abap new file mode 100644 index 000000000..c7be50fde --- /dev/null +++ b/src/ui/zcl_abapgit_gui_page_addonline.clas.abap @@ -0,0 +1,313 @@ +CLASS zcl_abapgit_gui_page_addonline DEFINITION + PUBLIC + INHERITING FROM zcl_abapgit_gui_page + FINAL + CREATE PRIVATE . + + PUBLIC SECTION. + + CLASS-METHODS create + " TODO importing prefilled form data + RETURNING + VALUE(ro_page) TYPE REF TO zcl_abapgit_gui_page_addonline + RAISING + zcx_abapgit_exception. + + METHODS zif_abapgit_gui_event_handler~on_event REDEFINITION . + + METHODS constructor + RAISING + zcx_abapgit_exception. + + PROTECTED SECTION. + + METHODS render_content REDEFINITION. + + PRIVATE SECTION. + + CONSTANTS: + BEGIN OF c_id, + url TYPE string VALUE 'url', + package TYPE string VALUE 'package', + branch_name TYPE string VALUE 'branch_name', + display_name TYPE string VALUE 'display_name', + folder_logic TYPE string VALUE 'folder_logic', + ignore_subpackages TYPE string VALUE 'ignore_subpackages', + master_lang_only TYPE string VALUE 'master_lang_only', + END OF c_id. + + CONSTANTS: + BEGIN OF c_event, + go_back TYPE string VALUE 'go-back', + choose_package TYPE string VALUE 'choose-package', + create_package TYPE string VALUE 'create-package', + choose_branch TYPE string VALUE 'choose-branch', + add_online_repo TYPE string VALUE 'add-repo-online', + END OF c_event. + + DATA mo_validation_log TYPE REF TO zcl_abapgit_string_map. + DATA mo_form_data TYPE REF TO zcl_abapgit_string_map. + + METHODS parse_form + IMPORTING + it_post_data TYPE cnht_post_data_tab + RETURNING + VALUE(ro_form_data) TYPE REF TO zcl_abapgit_string_map + RAISING + zcx_abapgit_exception. + + METHODS validate_form + IMPORTING + io_form_data TYPE REF TO zcl_abapgit_string_map + RETURNING + VALUE(ro_validation_log) TYPE REF TO zcl_abapgit_string_map + RAISING + zcx_abapgit_exception. + +ENDCLASS. + + + +CLASS ZCL_ABAPGIT_GUI_PAGE_ADDONLINE IMPLEMENTATION. + + + METHOD constructor. + super->constructor( ). + ms_control-page_title = 'Clone online repository'. " TODO refactor + CREATE OBJECT mo_validation_log. + CREATE OBJECT mo_form_data. + ENDMETHOD. + + + METHOD create. + CREATE OBJECT ro_page. + ENDMETHOD. + + + METHOD parse_form. + + DATA lt_form TYPE tihttpnvp. + DATA ls_field LIKE LINE OF lt_form. + + lt_form = zcl_abapgit_html_action_utils=>parse_post_data( it_post_data ). + CREATE OBJECT ro_form_data. + + LOOP AT lt_form INTO ls_field. + CASE ls_field-name. + WHEN c_id-url OR c_id-package OR c_id-branch_name OR c_id-display_name OR c_id-folder_logic. + ro_form_data->set( + iv_key = ls_field-name + iv_val = ls_field-value ). + WHEN c_id-ignore_subpackages OR c_id-master_lang_only. " Flags + ro_form_data->set( + iv_key = ls_field-name + iv_val = boolc( ls_field-value = 'on' ) ). + WHEN OTHERS. + zcx_abapgit_exception=>raise( |Unexpected form field [{ ls_field-name }]| ). + ENDCASE. + ENDLOOP. + + ENDMETHOD. + + + METHOD render_content. + + DATA lo_form TYPE REF TO zcl_abapgit_html_form. + + ri_html = zcl_abapgit_html=>create( ). + + lo_form = zcl_abapgit_html_form=>create( iv_form_id = 'add-repo-online-form' ). + lo_form->text( + iv_name = c_id-url + iv_required = abap_true + iv_label = 'Git repository URL' + iv_hint = 'HTTPS address of the repository to clone' + iv_placeholder = 'https://github.com/...git' ). + lo_form->text( + iv_name = c_id-package + iv_side_action = c_event-choose_package + iv_required = abap_true + iv_label = 'Package' + iv_hint = 'SAP package for the code (should be a dedicated one)' + iv_placeholder = 'Z... / $...' ). + lo_form->text( + iv_name = c_id-branch_name + iv_side_action = c_event-choose_branch + iv_label = 'Branch' + iv_hint = 'Switch to a specific branch on clone (default: master)' + iv_placeholder = 'master' ). + lo_form->radio( + iv_name = c_id-folder_logic + iv_default_value = zif_abapgit_dot_abapgit=>c_folder_logic-prefix + iv_label = 'Folder logic' + iv_hint = 'Define how package folders are named in the repo (see https://docs.abapgit.org)' ). + lo_form->option( + iv_label = 'Prefix' + iv_value = zif_abapgit_dot_abapgit=>c_folder_logic-prefix ). + lo_form->option( + iv_label = 'Full' + iv_value = zif_abapgit_dot_abapgit=>c_folder_logic-full ). + lo_form->text( + iv_name = c_id-display_name + iv_label = 'Display name' + iv_hint = 'Name to show instead of original repo name (optional)' ). + lo_form->checkbox( + iv_name = c_id-ignore_subpackages + iv_label = 'Ignore subpackages' + iv_hint = 'Syncronize root package only (see https://docs.abapgit.org)' ). + lo_form->checkbox( + iv_name = c_id-master_lang_only + iv_label = 'Serialize master language only' + iv_hint = 'Ignore translations, serialize just master language' ). + lo_form->command( + iv_label = 'Clone online repo' + iv_is_main = abap_true + iv_action = c_event-add_online_repo ). + lo_form->command( + iv_label = 'Create package' + iv_action = c_event-create_package ). + lo_form->command( + iv_label = 'Back' + iv_action = c_event-go_back ). + + ri_html->add( lo_form->render( + iv_form_class = 'dialog w600px m-em5-sides' " to center add wmax600px and auto-center instead + io_values = mo_form_data + io_validation_log = mo_validation_log ) ). + + ENDMETHOD. + + + METHOD validate_form. + + DATA lx_err TYPE REF TO zcx_abapgit_exception. + + CREATE OBJECT ro_validation_log. + + IF io_form_data->get( c_id-url ) IS INITIAL. + ro_validation_log->set( + iv_key = c_id-url + iv_val = 'Url cannot be empty' ). + ELSE. + TRY. + zcl_abapgit_url=>validate( io_form_data->get( c_id-url ) ). + CATCH zcx_abapgit_exception INTO lx_err. + ro_validation_log->set( + iv_key = c_id-url + iv_val = lx_err->get_text( ) ). + ENDTRY. + ENDIF. + + IF io_form_data->get( c_id-package ) IS INITIAL. + ro_validation_log->set( + iv_key = c_id-package + iv_val = 'Package cannot be empty' ). + ELSE. + TRY. + zcl_abapgit_repo_srv=>get_instance( )->validate_package( + iv_package = |{ io_form_data->get( c_id-package ) }| + iv_ign_subpkg = |{ io_form_data->get( c_id-ignore_subpackages ) }| ). + CATCH zcx_abapgit_exception INTO lx_err. + ro_validation_log->set( + iv_key = c_id-package + iv_val = lx_err->get_text( ) ). + ENDTRY. + ENDIF. + + IF io_form_data->get( c_id-folder_logic ) <> zif_abapgit_dot_abapgit=>c_folder_logic-prefix + AND io_form_data->get( c_id-folder_logic ) <> zif_abapgit_dot_abapgit=>c_folder_logic-full. + ro_validation_log->set( + iv_key = c_id-folder_logic + iv_val = |Invalid folder logic { io_form_data->get( c_id-folder_logic ) + }. Must be { zif_abapgit_dot_abapgit=>c_folder_logic-prefix + } or { zif_abapgit_dot_abapgit=>c_folder_logic-full } | ). + ENDIF. + + ENDMETHOD. + + + METHOD zif_abapgit_gui_event_handler~on_event. + + DATA ls_repo_params TYPE zif_abapgit_services_repo=>ty_repo_params. + + mo_form_data = parse_form( it_postdata ). " import data from html before re-render + + CASE iv_action. + WHEN c_event-go_back. + ev_state = zcl_abapgit_gui=>c_event_state-go_back. + + WHEN c_event-create_package. + + mo_form_data->set( + iv_key = c_id-package + iv_val = zcl_abapgit_services_basis=>create_package( + iv_prefill_package = |{ mo_form_data->get( 'package' ) }| ) ). + IF mo_form_data->get( c_id-package ) IS NOT INITIAL. + mo_validation_log = validate_form( mo_form_data ). + ev_state = zcl_abapgit_gui=>c_event_state-re_render. + ELSE. + ev_state = zcl_abapgit_gui=>c_event_state-no_more_act. + ENDIF. + + WHEN c_event-choose_package. + + mo_form_data->set( + iv_key = c_id-package + iv_val = zcl_abapgit_ui_factory=>get_popups( )->popup_search_help( 'TDEVC-DEVCLASS' ) ). + IF mo_form_data->get( c_id-package ) IS NOT INITIAL. + mo_validation_log = validate_form( mo_form_data ). + ev_state = zcl_abapgit_gui=>c_event_state-re_render. + ELSE. + ev_state = zcl_abapgit_gui=>c_event_state-no_more_act. + ENDIF. + + WHEN c_event-choose_branch. + + mo_validation_log = validate_form( mo_form_data ). + IF mo_validation_log->has( c_id-url ) = abap_true. + ev_state = zcl_abapgit_gui=>c_event_state-re_render. " Display errors + RETURN. + ENDIF. + mo_form_data->set( + iv_key = c_id-branch_name + iv_val = zcl_abapgit_ui_factory=>get_popups( )->branch_list_popup( mo_form_data->get( c_id-url ) )-name ). + + IF mo_form_data->get( c_id-branch_name ) IS INITIAL. + ev_state = zcl_abapgit_gui=>c_event_state-no_more_act. + ELSE. + mo_form_data->set( + iv_key = c_id-branch_name + iv_val = replace( " strip technical + val = mo_form_data->get( c_id-branch_name ) + sub = 'refs/heads/' + with = '' ) ). + ev_state = zcl_abapgit_gui=>c_event_state-re_render. + ENDIF. + + WHEN c_event-add_online_repo. + + mo_validation_log = validate_form( mo_form_data ). + + IF mo_validation_log->is_empty( ) = abap_true. + mo_form_data->to_abap( CHANGING cs_container = ls_repo_params ). + zcl_abapgit_services_repo=>new_online( ls_repo_params ). + ev_state = zcl_abapgit_gui=>c_event_state-go_back. + ELSE. + ev_state = zcl_abapgit_gui=>c_event_state-re_render. " Display errors + ENDIF. + + ENDCASE. + + IF ev_state IS INITIAL. " TODO !!! Refactor this disaster !!! + super->zif_abapgit_gui_event_handler~on_event( + EXPORTING + iv_action = iv_action + iv_getdata = iv_getdata + it_postdata = it_postdata + IMPORTING + ei_page = ei_page + ev_state = ev_state ). + ENDIF. + + ENDMETHOD. +ENDCLASS. diff --git a/src/ui/zcl_abapgit_gui_page_addonline.clas.xml b/src/ui/zcl_abapgit_gui_page_addonline.clas.xml new file mode 100644 index 000000000..68dc88e43 --- /dev/null +++ b/src/ui/zcl_abapgit_gui_page_addonline.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_ABAPGIT_GUI_PAGE_ADDONLINE + E + abapgit GUI add repo online page + 1 + X + X + X + + + + diff --git a/src/ui/zcl_abapgit_gui_router.clas.abap b/src/ui/zcl_abapgit_gui_router.clas.abap index f73a744e3..06aeb7aae 100644 --- a/src/ui/zcl_abapgit_gui_router.clas.abap +++ b/src/ui/zcl_abapgit_gui_router.clas.abap @@ -126,7 +126,7 @@ ENDCLASS. -CLASS zcl_abapgit_gui_router IMPLEMENTATION. +CLASS ZCL_ABAPGIT_GUI_ROUTER IMPLEMENTATION. METHOD abapgit_services_actions. @@ -497,8 +497,8 @@ CLASS zcl_abapgit_gui_router IMPLEMENTATION. zcl_abapgit_services_repo=>remove( lv_key ). ev_state = zcl_abapgit_gui=>c_event_state-re_render. WHEN zif_abapgit_definitions=>c_action-repo_newonline. - zcl_abapgit_services_repo=>new_online( lv_url ). - ev_state = zcl_abapgit_gui=>c_event_state-re_render. + ei_page = zcl_abapgit_gui_page_addonline=>create( ). + ev_state = zcl_abapgit_gui=>c_event_state-new_page. WHEN zif_abapgit_definitions=>c_action-repo_refresh_checksums. " Rebuild local checksums zcl_abapgit_services_repo=>refresh_local_checksums( lv_key ). ev_state = zcl_abapgit_gui=>c_event_state-re_render. diff --git a/src/ui/zcl_abapgit_html_action_utils.clas.abap b/src/ui/zcl_abapgit_html_action_utils.clas.abap index 1e14c782c..f816aff07 100644 --- a/src/ui/zcl_abapgit_html_action_utils.clas.abap +++ b/src/ui/zcl_abapgit_html_action_utils.clas.abap @@ -4,6 +4,11 @@ CLASS zcl_abapgit_html_action_utils DEFINITION PUBLIC SECTION. + CLASS-METHODS parse_post_data + IMPORTING + !it_post_data TYPE cnht_post_data_tab + RETURNING + VALUE(rt_fields) TYPE tihttpnvp . CLASS-METHODS parse_fields IMPORTING !iv_string TYPE clike @@ -360,6 +365,16 @@ CLASS ZCL_ABAPGIT_HTML_ACTION_UTILS IMPLEMENTATION. ENDMETHOD. + METHOD parse_post_data. + + DATA lv_serialized_post_data TYPE string. + + CONCATENATE LINES OF it_post_data INTO lv_serialized_post_data. + rt_fields = parse_fields( lv_serialized_post_data ). + + ENDMETHOD. + + METHOD stage_decode. DATA: lt_fields TYPE tihttpnvp. diff --git a/src/ui/zcl_abapgit_html_form.clas.abap b/src/ui/zcl_abapgit_html_form.clas.abap new file mode 100644 index 000000000..eb5eccf07 --- /dev/null +++ b/src/ui/zcl_abapgit_html_form.clas.abap @@ -0,0 +1,386 @@ +CLASS zcl_abapgit_html_form DEFINITION + PUBLIC + FINAL + CREATE PRIVATE . + + PUBLIC SECTION. + + CLASS-METHODS create + IMPORTING + iv_form_id TYPE string OPTIONAL + RETURNING + VALUE(ro_form) TYPE REF TO zcl_abapgit_html_form. + + METHODS render + IMPORTING + iv_form_class TYPE string + io_values TYPE REF TO zcl_abapgit_string_map + io_validation_log TYPE REF TO zcl_abapgit_string_map OPTIONAL + RETURNING + VALUE(ri_html) TYPE REF TO zif_abapgit_html. + + METHODS command + IMPORTING + iv_label TYPE string + iv_action TYPE string + iv_is_main TYPE abap_bool DEFAULT abap_false + iv_as_a TYPE abap_bool DEFAULT abap_false. + + METHODS text + IMPORTING + iv_label TYPE string + iv_name TYPE string + iv_hint TYPE string OPTIONAL + iv_required TYPE abap_bool DEFAULT abap_false + iv_placeholder TYPE string OPTIONAL + iv_side_action TYPE string OPTIONAL. + + METHODS checkbox + IMPORTING + iv_label TYPE string + iv_name TYPE string + iv_hint TYPE string OPTIONAL. + + METHODS radio + IMPORTING + iv_label TYPE string + iv_name TYPE string + iv_default_value TYPE string OPTIONAL + iv_hint TYPE string OPTIONAL. + + METHODS option + IMPORTING + iv_label TYPE string + iv_value TYPE string. + + PROTECTED SECTION. + PRIVATE SECTION. + + TYPES: + BEGIN OF ty_subitem, + label TYPE string, + value TYPE string, + END OF ty_subitem. + TYPES: + tty_subitems TYPE STANDARD TABLE OF ty_subitem WITH DEFAULT KEY. + + TYPES: + BEGIN OF ty_field, + type TYPE i, + name TYPE string, + label TYPE string, + hint TYPE string, + dblclick TYPE string, + placeholder TYPE string, + required TYPE string, + item_class TYPE string, + error TYPE string, + default_value TYPE string, + side_action TYPE string, + subitems TYPE tty_subitems, +* onclick ??? + END OF ty_field. + + TYPES: + BEGIN OF ty_command, + label TYPE string, + action TYPE string, + is_main TYPE abap_bool, + as_a TYPE abap_bool, +* onclick ??? + END OF ty_command. + + CONSTANTS: + BEGIN OF c_field_type, + text TYPE i VALUE 1, + radio TYPE i VALUE 2, + checkbox TYPE i VALUE 3, + END OF c_field_type. + + DATA mt_fields TYPE STANDARD TABLE OF ty_field. + DATA mt_commands TYPE STANDARD TABLE OF ty_command. + DATA mv_form_id TYPE string. + + CLASS-METHODS render_field + IMPORTING + ii_html TYPE REF TO zif_abapgit_html + io_values TYPE REF TO zcl_abapgit_string_map + io_validation_log TYPE REF TO zcl_abapgit_string_map + is_field TYPE ty_field. + + CLASS-METHODS render_command + IMPORTING + ii_html TYPE REF TO zif_abapgit_html + is_cmd TYPE ty_command. + +ENDCLASS. + + + +CLASS ZCL_ABAPGIT_HTML_FORM IMPLEMENTATION. + + + METHOD checkbox. + + DATA ls_field LIKE LINE OF mt_fields. + + ls_field-type = c_field_type-checkbox. + ls_field-name = iv_name. + ls_field-label = iv_label. + + IF iv_hint IS NOT INITIAL. + ls_field-hint = | title="{ iv_hint }"|. + ENDIF. + + APPEND ls_field TO mt_fields. + + ENDMETHOD. + + + METHOD command. + + DATA ls_cmd LIKE LINE OF mt_commands. + + ASSERT iv_as_a IS INITIAL OR iv_is_main IS INITIAL. + + ls_cmd-label = iv_label. + ls_cmd-action = iv_action. + ls_cmd-is_main = iv_is_main. + ls_cmd-as_a = iv_as_a. + + APPEND ls_cmd TO mt_commands. + + ENDMETHOD. + + + METHOD create. + CREATE OBJECT ro_form. + ro_form->mv_form_id = iv_form_id. + ENDMETHOD. + + + METHOD option. + + FIELD-SYMBOLS LIKE LINE OF mt_fields. + DATA ls_option LIKE LINE OF -subitems. + DATA lv_size TYPE i. + + lv_size = lines( mt_fields ). + ASSERT lv_size > 0. " Exception ? Maybe add zcx_no_check ? + + READ TABLE mt_fields INDEX lv_size ASSIGNING . + ASSERT sy-subrc = 0. + ASSERT -type = c_field_type-radio. " Or dropdown - TODO in future + + ls_option-label = iv_label. + ls_option-value = iv_value. + + APPEND ls_option TO -subitems. + + ENDMETHOD. + + + METHOD radio. + + DATA ls_field LIKE LINE OF mt_fields. + + ls_field-type = c_field_type-radio. + ls_field-name = iv_name. + ls_field-label = iv_label. + ls_field-default_value = iv_default_value. + + IF iv_hint IS NOT INITIAL. + ls_field-hint = | title="{ iv_hint }"|. + ENDIF. + + APPEND ls_field TO mt_fields. + + ENDMETHOD. + + + METHOD render. + + FIELD-SYMBOLS LIKE LINE OF mt_fields. + FIELD-SYMBOLS LIKE LINE OF mt_commands. + DATA ls_form_id TYPE string. + + IF mv_form_id IS NOT INITIAL. + ls_form_id = | id="{ mv_form_id }"|. + ENDIF. + + ri_html = zcl_abapgit_html=>create( ). + + ri_html->add( |
    | ). + ri_html->add( |
    | ). + + LOOP AT mt_fields ASSIGNING . + render_field( + ii_html = ri_html + io_values = io_values + io_validation_log = io_validation_log + is_field = ). + ENDLOOP. + + ri_html->add( |
  • | ). + + LOOP AT mt_commands ASSIGNING . + render_command( + ii_html = ri_html + is_cmd = ). + ENDLOOP. + + ri_html->add( |
  • | ). + + ri_html->add( || ). + ri_html->add( |
| ). + + ENDMETHOD. + + + METHOD render_command. + + DATA lv_main_submit TYPE string. + + IF is_cmd-as_a = abap_true. + ii_html->add_a( + iv_txt = is_cmd-label + iv_act = is_cmd-action + iv_class = 'dialog-commands' ). + ELSE. + IF is_cmd-is_main = abap_true. + lv_main_submit = ' class="main"'. + ELSE. + CLEAR lv_main_submit. + ENDIF. + ii_html->add( || ). + ENDIF. + + ENDMETHOD. + + + METHOD render_field. + + DATA lv_opt_id TYPE string. + DATA lv_error TYPE string. + DATA lv_value TYPE string. + DATA lv_checked TYPE string. + DATA lv_item_class TYPE string. + FIELD-SYMBOLS LIKE LINE OF is_field-subitems. + + " Get value and validation error from maps + lv_value = io_values->get( is_field-name ). + IF io_validation_log IS BOUND. + lv_error = io_validation_log->get( is_field-name ). + ENDIF. + + " Prepare item class + lv_item_class = is_field-item_class. + IF lv_error IS NOT INITIAL. + lv_item_class = condense( lv_item_class && ' error' ). + ENDIF. + IF lv_item_class IS NOT INITIAL. + lv_item_class = | class="{ lv_item_class }"|. + ENDIF. + + " Render field + ii_html->add( || ). + + CASE is_field-type. + WHEN c_field_type-text. + + ii_html->add( || ). + IF lv_error IS NOT INITIAL. + ii_html->add( |{ lv_error }| ). + ENDIF. + + IF is_field-side_action IS NOT INITIAL. + ii_html->add( '
' ). " Ugly :( + ENDIF. + + ii_html->add( || ). + + IF is_field-side_action IS NOT INITIAL. + ii_html->add( '
' ). + ii_html->add( '
' ). + ii_html->add( || ). + ii_html->add( '
' ). + ENDIF. + + WHEN c_field_type-checkbox. + + IF lv_error IS NOT INITIAL. + ii_html->add( |{ lv_error }| ). + ENDIF. + IF lv_value IS NOT INITIAL. + lv_checked = ' checked'. + ENDIF. + ii_html->add( || ). + ii_html->add( || ). + + WHEN c_field_type-radio. + + ii_html->add( |{ is_field-label }{ is_field-required }| ). + IF lv_error IS NOT INITIAL. + ii_html->add( |{ lv_error }| ). + ENDIF. + ii_html->add( |
| ). + + LOOP AT is_field-subitems ASSIGNING . + CLEAR lv_checked. + IF lv_value = -value OR ( lv_value IS INITIAL AND -value = is_field-default_value ). + lv_checked = ' checked'. + ENDIF. + lv_opt_id = |{ is_field-name }{ sy-tabix }|. + ii_html->add( || ). + ii_html->add( || ). + ENDLOOP. + + ii_html->add( '
' ). + + WHEN OTHERS. + ASSERT 1 = 0. + ENDCASE. + + ii_html->add( '' ). + + ENDMETHOD. + + + METHOD text. + + DATA ls_field LIKE LINE OF mt_fields. + + ls_field-type = c_field_type-text. + ls_field-name = iv_name. + ls_field-label = iv_label. + + IF iv_hint IS NOT INITIAL. + ls_field-hint = | title="{ iv_hint }"|. + ENDIF. + + IF iv_side_action IS NOT INITIAL AND mv_form_id IS NOT INITIAL. + ls_field-item_class = 'with-command'. + ls_field-side_action = iv_side_action. + ls_field-dblclick = | ondblclick="document.getElementById('{ mv_form_id + }').action = 'sapevent:{ iv_side_action + }'; document.getElementById('{ mv_form_id + }').submit()"|. + ENDIF. + + IF iv_required = abap_true. + ls_field-required = ' *'. + ENDIF. + + IF iv_placeholder IS NOT INITIAL. + ls_field-placeholder = | placeholder="{ iv_placeholder }"|. + ENDIF. + + APPEND ls_field TO mt_fields. + + ENDMETHOD. +ENDCLASS. diff --git a/src/ui/zcl_abapgit_html_form.clas.xml b/src/ui/zcl_abapgit_html_form.clas.xml new file mode 100644 index 000000000..22dde6a52 --- /dev/null +++ b/src/ui/zcl_abapgit_html_form.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_ABAPGIT_HTML_FORM + E + abapgit html form component + 1 + X + X + X + + + + diff --git a/src/ui/zcl_abapgit_popups.clas.abap b/src/ui/zcl_abapgit_popups.clas.abap index 14f8c5af7..b97968db4 100644 --- a/src/ui/zcl_abapgit_popups.clas.abap +++ b/src/ui/zcl_abapgit_popups.clas.abap @@ -742,6 +742,39 @@ CLASS ZCL_ABAPGIT_POPUPS IMPLEMENTATION. ENDMETHOD. + METHOD zif_abapgit_popups~popup_search_help. + + DATA lt_ret TYPE TABLE OF ddshretval. + DATA ls_ret LIKE LINE OF lt_ret. + DATA lv_tabname TYPE dfies-tabname. + DATA lv_fieldname TYPE dfies-fieldname. + + SPLIT iv_tab_field AT '-' INTO lv_tabname lv_fieldname. + lv_tabname = to_upper( lv_tabname ). + lv_fieldname = to_upper( lv_fieldname ). + + CALL FUNCTION 'F4IF_FIELD_VALUE_REQUEST' + EXPORTING + tabname = lv_tabname + fieldname = lv_fieldname + TABLES + return_tab = lt_ret + EXCEPTIONS + OTHERS = 5. + + IF sy-subrc <> 0. + zcx_abapgit_exception=>raise( |F4IF_FIELD_VALUE_REQUEST error [{ iv_tab_field }]| ). + ENDIF. + + IF lines( lt_ret ) > 0. + READ TABLE lt_ret INDEX 1 INTO ls_ret. + ASSERT sy-subrc = 0. + rv_value = ls_ret-fieldval. + ENDIF. + + ENDMETHOD. + + METHOD zif_abapgit_popups~popup_to_confirm. CALL FUNCTION 'POPUP_TO_CONFIRM' diff --git a/src/ui/zcl_abapgit_services_basis.clas.abap b/src/ui/zcl_abapgit_services_basis.clas.abap new file mode 100644 index 000000000..6f5be4b69 --- /dev/null +++ b/src/ui/zcl_abapgit_services_basis.clas.abap @@ -0,0 +1,43 @@ +CLASS zcl_abapgit_services_basis DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + CLASS-METHODS create_package + IMPORTING + iv_prefill_package TYPE devclass OPTIONAL + RETURNING + VALUE(rv_package) TYPE devclass + RAISING + zcx_abapgit_exception. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS ZCL_ABAPGIT_SERVICES_BASIS IMPLEMENTATION. + + + METHOD create_package. + + DATA ls_package_data TYPE scompkdtln. + DATA lv_create TYPE abap_bool. + + ls_package_data-devclass = to_upper( iv_prefill_package ). + + zcl_abapgit_ui_factory=>get_popups( )->popup_to_create_package( + IMPORTING + es_package_data = ls_package_data + ev_create = lv_create ). + + IF lv_create = abap_true. + zcl_abapgit_factory=>get_sap_package( ls_package_data-devclass )->create( ls_package_data ). + rv_package = ls_package_data-devclass. + COMMIT WORK. + ENDIF. + + ENDMETHOD. +ENDCLASS. diff --git a/src/ui/zcl_abapgit_services_basis.clas.xml b/src/ui/zcl_abapgit_services_basis.clas.xml new file mode 100644 index 000000000..642e7c313 --- /dev/null +++ b/src/ui/zcl_abapgit_services_basis.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_ABAPGIT_SERVICES_BASIS + E + abapGit basis services + 1 + X + X + X + + + + diff --git a/src/ui/zcl_abapgit_services_repo.clas.abap b/src/ui/zcl_abapgit_services_repo.clas.abap index 30dae00ef..1a7e5a951 100644 --- a/src/ui/zcl_abapgit_services_repo.clas.abap +++ b/src/ui/zcl_abapgit_services_repo.clas.abap @@ -7,7 +7,7 @@ CLASS zcl_abapgit_services_repo DEFINITION CLASS-METHODS new_online IMPORTING - !iv_url TYPE string + !is_repo_params TYPE zif_abapgit_services_repo=>ty_repo_params RETURNING VALUE(ro_repo) TYPE REF TO zcl_abapgit_repo_online RAISING @@ -177,45 +177,38 @@ CLASS zcl_abapgit_services_repo IMPLEMENTATION. METHOD new_online. - DATA: ls_popup TYPE zif_abapgit_popups=>ty_popup, + DATA: lo_repo TYPE REF TO zcl_abapgit_repo, li_repo_srv TYPE REF TO zif_abapgit_repo_srv, lv_reason TYPE string. - ls_popup = zcl_abapgit_ui_factory=>get_popups( )->repo_popup( iv_url ). - IF ls_popup-cancel = abap_true. - RAISE EXCEPTION TYPE zcx_abapgit_cancel. - ENDIF. - " make sure package is not already in use for a different repository " 702: chaining calls with exp&imp parameters causes syntax error li_repo_srv = zcl_abapgit_repo_srv=>get_instance( ). li_repo_srv->get_repo_from_package( EXPORTING - iv_package = ls_popup-package + iv_package = is_repo_params-package IMPORTING eo_repo = lo_repo ev_reason = lv_reason ). IF lo_repo IS BOUND. - MESSAGE lv_reason TYPE 'S'. - ELSE. - ro_repo = zcl_abapgit_repo_srv=>get_instance( )->new_online( - iv_url = ls_popup-url - iv_branch_name = ls_popup-branch_name - iv_package = ls_popup-package - iv_display_name = ls_popup-display_name - iv_folder_logic = ls_popup-folder_logic - iv_ign_subpkg = ls_popup-ign_subpkg - iv_master_lang_only = ls_popup-master_lang_only ). - - toggle_favorite( ro_repo->get_key( ) ). - - lo_repo ?= ro_repo. + zcx_abapgit_exception=>raise( lv_reason ). ENDIF. + ro_repo = zcl_abapgit_repo_srv=>get_instance( )->new_online( + iv_url = is_repo_params-url + iv_branch_name = is_repo_params-branch_name + iv_package = is_repo_params-package + iv_display_name = is_repo_params-display_name + iv_folder_logic = is_repo_params-folder_logic + iv_ign_subpkg = is_repo_params-ignore_subpackages + iv_master_lang_only = is_repo_params-master_lang_only ). + + toggle_favorite( ro_repo->get_key( ) ). + " Set default repo for user - zcl_abapgit_persistence_user=>get_instance( )->set_repo_show( lo_repo->get_key( ) ). + zcl_abapgit_persistence_user=>get_instance( )->set_repo_show( ro_repo->get_key( ) ). COMMIT WORK. diff --git a/src/ui/zcl_abapgit_ui_injector.clas.testclasses.abap b/src/ui/zcl_abapgit_ui_injector.clas.testclasses.abap index 5a9964289..fe7630c39 100644 --- a/src/ui/zcl_abapgit_ui_injector.clas.testclasses.abap +++ b/src/ui/zcl_abapgit_ui_injector.clas.testclasses.abap @@ -96,6 +96,10 @@ CLASS ltcl_abapgit_popups_mock IMPLEMENTATION. ENDMETHOD. + METHOD zif_abapgit_popups~popup_search_help. + + ENDMETHOD. + ENDCLASS. CLASS ltcl_no_dependency_injection IMPLEMENTATION. diff --git a/src/ui/zif_abapgit_popups.intf.abap b/src/ui/zif_abapgit_popups.intf.abap index f84ebe325..c888e3fa3 100644 --- a/src/ui/zif_abapgit_popups.intf.abap +++ b/src/ui/zif_abapgit_popups.intf.abap @@ -3,7 +3,7 @@ INTERFACE zif_abapgit_popups TYPES: - BEGIN OF ty_popup, + BEGIN OF ty_popup, " TODO remove, use zif_abapgit_services_repo=>ty_repo_params instead url TYPE string, package TYPE devclass, branch_name TYPE string, @@ -16,6 +16,13 @@ INTERFACE zif_abapgit_popups CONSTANTS c_new_branch_label TYPE string VALUE '+ create new ...' ##NO_TEXT. + METHODS popup_search_help + IMPORTING + !iv_tab_field TYPE string + RETURNING + VALUE(rv_value) TYPE ddshretval-fieldval + RAISING + zcx_abapgit_exception . METHODS popup_package_export EXPORTING !ev_package TYPE devclass diff --git a/src/ui/zif_abapgit_services_repo.intf.abap b/src/ui/zif_abapgit_services_repo.intf.abap new file mode 100644 index 000000000..3d82cf308 --- /dev/null +++ b/src/ui/zif_abapgit_services_repo.intf.abap @@ -0,0 +1,15 @@ +INTERFACE zif_abapgit_services_repo + PUBLIC . + + TYPES: + BEGIN OF ty_repo_params, + url TYPE string, + package TYPE devclass, + branch_name TYPE string, + display_name TYPE string, + folder_logic TYPE string, + ignore_subpackages TYPE abap_bool, + master_lang_only TYPE abap_bool, + END OF ty_repo_params . + +ENDINTERFACE. diff --git a/src/ui/zif_abapgit_services_repo.intf.xml b/src/ui/zif_abapgit_services_repo.intf.xml new file mode 100644 index 000000000..c01050e60 --- /dev/null +++ b/src/ui/zif_abapgit_services_repo.intf.xml @@ -0,0 +1,15 @@ + + + + + + ZIF_ABAPGIT_SERVICES_REPO + E + abapGit repo service definitions + 2 + 1 + X + + + + diff --git a/src/utils/zcl_abapgit_string_map.clas.abap b/src/utils/zcl_abapgit_string_map.clas.abap new file mode 100644 index 000000000..d45f3ceef --- /dev/null +++ b/src/utils/zcl_abapgit_string_map.clas.abap @@ -0,0 +1,160 @@ +CLASS zcl_abapgit_string_map DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + + TYPES: + BEGIN OF ty_entry, + k TYPE string, + v TYPE string, + END OF ty_entry, + tty_entries TYPE STANDARD TABLE OF ty_entry WITH KEY k, + tts_entries TYPE SORTED TABLE OF ty_entry WITH UNIQUE KEY k. + + CLASS-METHODS create + RETURNING + VALUE(ro_instance) TYPE REF TO zcl_abapgit_string_map. + + METHODS get + IMPORTING + iv_key TYPE string + RETURNING + VALUE(rv_val) TYPE string. + + METHODS has + IMPORTING + iv_key TYPE string + RETURNING + VALUE(rv_has) TYPE abap_bool. + + METHODS set + IMPORTING + iv_key TYPE string + iv_val TYPE string OPTIONAL. + + METHODS size + RETURNING + VALUE(rv_size) TYPE i. + + METHODS is_empty + RETURNING + VALUE(rv_yes) TYPE abap_bool. + + METHODS delete + IMPORTING + iv_key TYPE string. + + METHODS clear. + + METHODS to_abap + CHANGING + !cs_container TYPE any + RAISING + zcx_abapgit_exception. + + PROTECTED SECTION. + PRIVATE SECTION. + DATA mt_entries TYPE tts_entries. + +ENDCLASS. + + + +CLASS ZCL_ABAPGIT_STRING_MAP IMPLEMENTATION. + + + METHOD clear. + CLEAR mt_entries. + ENDMETHOD. + + + METHOD create. + CREATE OBJECT ro_instance. + ENDMETHOD. + + + METHOD delete. + + DELETE mt_entries WHERE k = iv_key. + + ENDMETHOD. + + + METHOD get. + + FIELD-SYMBOLS LIKE LINE OF mt_entries. + READ TABLE mt_entries ASSIGNING WITH KEY k = iv_key. + IF sy-subrc IS INITIAL. + rv_val = -v. + ENDIF. + + ENDMETHOD. + + + METHOD has. + + READ TABLE mt_entries TRANSPORTING NO FIELDS WITH KEY k = iv_key. + rv_has = boolc( sy-subrc IS INITIAL ). + + ENDMETHOD. + + + METHOD is_empty. + rv_yes = boolc( lines( mt_entries ) = 0 ). + ENDMETHOD. + + + METHOD set. + + DATA ls_entry LIKE LINE OF mt_entries. + FIELD-SYMBOLS LIKE LINE OF mt_entries. + + READ TABLE mt_entries ASSIGNING WITH KEY k = iv_key. + IF sy-subrc IS INITIAL. + -v = iv_val. + ELSE. + ls_entry-k = iv_key. + ls_entry-v = iv_val. + INSERT ls_entry INTO TABLE mt_entries. + ENDIF. + + ENDMETHOD. + + + METHOD size. + + rv_size = lines( mt_entries ). + + ENDMETHOD. + + + METHOD to_abap. + + DATA lo_type TYPE REF TO cl_abap_typedescr. + DATA lo_struc TYPE REF TO cl_abap_structdescr. + DATA lv_field TYPE string. + FIELD-SYMBOLS LIKE LINE OF mt_entries. + FIELD-SYMBOLS TYPE any. + + lo_type = cl_abap_typedescr=>describe_by_data( cs_container ). + IF lo_type->type_kind <> cl_abap_typedescr=>typekind_struct1 + AND lo_type->type_kind <> cl_abap_typedescr=>typekind_struct2. + zcx_abapgit_exception=>raise( 'Only structures supported' ). + ENDIF. + + lo_struc ?= lo_type. + LOOP AT mt_entries ASSIGNING . + lv_field = to_upper( -k ). + ASSIGN COMPONENT lv_field OF STRUCTURE cs_container TO . + IF sy-subrc = 0. + " TODO check target type ? + = -v. + ELSE. + zcx_abapgit_exception=>raise( |Component { lv_field } not found in target| ). + ENDIF. + ENDLOOP. + + ENDMETHOD. +ENDCLASS. diff --git a/src/utils/zcl_abapgit_string_map.clas.xml b/src/utils/zcl_abapgit_string_map.clas.xml new file mode 100644 index 000000000..a2c77f6aa --- /dev/null +++ b/src/utils/zcl_abapgit_string_map.clas.xml @@ -0,0 +1,16 @@ + + + + + + ZCL_ABAPGIT_STRING_MAP + E + abapGit string Map implementation + 1 + X + X + X + + + + diff --git a/src/zcl_abapgit_repo_srv.clas.abap b/src/zcl_abapgit_repo_srv.clas.abap index c1e668a79..0c1d5e85f 100644 --- a/src/zcl_abapgit_repo_srv.clas.abap +++ b/src/zcl_abapgit_repo_srv.clas.abap @@ -387,14 +387,24 @@ CLASS ZCL_ABAPGIT_REPO_SRV IMPLEMENTATION. METHOD zif_abapgit_repo_srv~new_online. DATA: ls_repo TYPE zif_abapgit_persistence=>ty_repo, + lv_branch_name LIKE iv_branch_name, lv_key TYPE zif_abapgit_persistence=>ty_repo-key, ls_dot_abapgit TYPE zif_abapgit_dot_abapgit=>ty_dot_abapgit. ASSERT NOT iv_url IS INITIAL - AND NOT iv_branch_name IS INITIAL AND NOT iv_package IS INITIAL. + lv_branch_name = iv_branch_name. + IF lv_branch_name IS INITIAL. + lv_branch_name = 'refs/heads/master'. + ENDIF. + IF -1 = find( + val = lv_branch_name + sub = 'refs/heads/' ). + lv_branch_name = 'refs/heads/' && lv_branch_name. " Assume short branch name was received + ENDIF. + IF zcl_abapgit_auth=>is_allowed( zif_abapgit_auth=>gc_authorization-create_repo ) = abap_false. zcx_abapgit_exception=>raise( 'Not authorized' ). ENDIF. @@ -409,7 +419,7 @@ CLASS ZCL_ABAPGIT_REPO_SRV IMPLEMENTATION. lv_key = zcl_abapgit_persist_factory=>get_repo( )->add( iv_url = iv_url - iv_branch_name = iv_branch_name + iv_branch_name = lv_branch_name " local ! iv_display_name = iv_display_name iv_package = iv_package iv_offline = abap_false