' ).
ro_html->add( '' ).
+
+ " Repo type and name
ro_html->add_icon( iv_name = lv_icon iv_hint = lv_hint ).
ro_html->add( |{ io_repo->get_name( ) }| ).
IF io_repo->is_offline( ) = abap_false.
lo_repo_online ?= io_repo.
ro_html->add( |{ lo_repo_online->get_url( ) }| ).
ENDIF.
+
+ " News
+ IF io_news IS BOUND AND io_news->has_news( ) = abap_true.
+ IF io_news->has_updates( ) = abap_true.
+ lv_icon = 'arrow-up/warning'.
+ ELSE.
+ lv_icon = 'arrow-up/grey80'.
+ ENDIF.
+ ro_html->add_a( iv_act = 'displayNews()'
+ iv_typ = gc_action_type-onclick
+ iv_txt = lcl_html=>icon( iv_name = lv_icon
+ iv_class = 'pad-sides'
+ iv_hint = 'Display changelog' ) ).
+ ENDIF.
ro_html->add( ' | ' ).
ro_html->add( '' ).
- IF io_news IS BOUND AND io_news->has_news( ) = abap_true.
- ro_html->add_a( iv_act = 'displayNews()'
- iv_typ = gc_action_type-onclick
- iv_txt = lcl_html=>icon( iv_name = 'pulse/blue'
- iv_hint = 'Display changelog' ) ).
- ENDIF.
-
+ " Fav
IF abap_true = lcl_app=>user( )->is_favorite_repo( io_repo->get_key( ) ).
lv_icon = 'star/blue' ##NO_TEXT.
ELSE.
@@ -96,14 +106,17 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
iv_class = 'pad-sides'
iv_hint = 'Click to toggle favorite' ) ).
+ " BG
IF lo_pback->exists( io_repo->get_key( ) ) = abap_true.
ro_html->add( 'BG' ).
ENDIF.
+ " Write protect
IF io_repo->is_write_protected( ) = abap_true.
ro_html->add_icon( iv_name = 'lock/darkgrey' iv_hint = 'Locked from pulls' ).
ENDIF.
+ " Branch
IF io_repo->is_offline( ) = abap_false.
lo_repo_online ?= io_repo.
IF iv_show_branch = abap_true.
@@ -119,6 +132,7 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
ENDIF.
ENDIF.
+ " Package
IF iv_show_package = abap_true.
ro_html->add_icon( iv_name = 'package/darkgrey' iv_hint = 'SAP package' ).
ro_html->add( '' ).
@@ -231,7 +245,10 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
METHOD render_news.
- DATA lt_log TYPE lcl_news=>tt_log.
+ DATA: lv_text TYPE string,
+ lv_display TYPE string,
+ lt_log TYPE lcl_news=>tt_log.
+
FIELD-SYMBOLS: LIKE LINE OF lt_log.
CREATE OBJECT ro_html.
@@ -242,24 +259,40 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
lt_log = io_news->get_log( ).
- ro_html->add( '' ).
+ IF io_news->has_unseen( ) = abap_false.
+ lv_display = 'display:none'.
+ ENDIF.
+
+ ro_html->add( | | ).
ro_html->add( ' Announcement of the latest changes'
- && ' '
- && ' ' ).
+ && ' '
+ && lcl_html=>a(
+ iv_txt = '❌'
+ iv_typ = gc_action_type-onclick
+ iv_act = 'displayNews()'
+ iv_class = 'close-btn' )
+ && ' ' ).
- IF io_news->has_important_news( ) = abap_true.
+ IF io_news->has_important( ) = abap_true.
ro_html->add( ' '
&& lcl_html=>icon( iv_name = 'alert' iv_class = 'pad-right' )
- && 'Please note changes marked with "!"'
+ && 'Please note changes marked with "!"'
&& ' ' ).
ENDIF.
" Generate news
ro_html->add( | | ).
LOOP AT lt_log ASSIGNING .
- IF -header = 'X'.
- ro_html->add( |{ -text }| ).
+ IF -is_header = abap_true.
+ IF -pos_to_cur > 0.
+ lv_text = -text && 'update'.
+ ELSEIF -pos_to_cur = 0.
+ lv_text = -text && 'current'.
+ ELSE. " < 0
+ lv_text = -text.
+ ENDIF.
+ ro_html->add( |{ lv_text }| ).
ELSE.
ro_html->add( |{ -text }| ).
ENDIF.
diff --git a/src/zabapgit_news.prog.abap b/src/zabapgit_news.prog.abap
index c872439fd..f5d5965d0 100644
--- a/src/zabapgit_news.prog.abap
+++ b/src/zabapgit_news.prog.abap
@@ -8,59 +8,73 @@ CLASS ltcl_news DEFINITION DEFERRED.
*&---------------------------------------------------------------------*
* Class responsible for preparation of data for news announcements
*----------------------------------------------------------------------*
-CLASS lcl_news DEFINITION FRIENDS ltcl_news.
+CLASS lcl_news DEFINITION CREATE PRIVATE FRIENDS ltcl_news.
PUBLIC SECTION.
+
+ CONSTANTS: c_tail_length TYPE i VALUE 5. " Number of versions to display if no updates
+
TYPES:
BEGIN OF ty_log,
- version TYPE string,
- header TYPE abap_bool,
- important TYPE abap_bool,
- text TYPE string,
+ version TYPE string,
+ pos_to_cur TYPE i,
+ is_header TYPE abap_bool,
+ is_important TYPE abap_bool,
+ text TYPE string,
END OF ty_log,
tt_log TYPE STANDARD TABLE OF ty_log WITH DEFAULT KEY.
- CONSTANTS:
- c_log_path TYPE string VALUE '/',
- c_log_filename TYPE string VALUE 'changelog.txt'.
+ CLASS-METHODS:
+ create " TODO REFACTOR
+ IMPORTING io_repo TYPE REF TO lcl_repo
+ RETURNING VALUE(ro_instance) TYPE REF TO lcl_news
+ RAISING lcx_exception.
- CLASS-METHODS create
- IMPORTING io_repo TYPE REF TO lcl_repo
- RETURNING VALUE(ro_instance) TYPE REF TO lcl_news
- RAISING lcx_exception.
+ METHODS:
+ get_log
+ RETURNING VALUE(rt_log) TYPE tt_log,
+ latest_version
+ RETURNING VALUE(rv_version) TYPE string,
+ has_news
+ RETURNING VALUE(rv_boolean) TYPE abap_bool,
+ has_important
+ RETURNING VALUE(rv_boolean) TYPE abap_bool,
+ has_updates
+ RETURNING VALUE(rv_boolean) TYPE abap_bool,
+ has_unseen
+ RETURNING VALUE(rv_boolean) TYPE abap_bool.
+
+ PRIVATE SECTION.
+ DATA: mt_log TYPE tt_log,
+ mv_current_version TYPE string,
+ mv_lastseen_version TYPE string,
+ mv_latest_version TYPE string.
METHODS:
constructor
- IMPORTING iv_rawdata TYPE xstring
- iv_version TYPE string,
- has_news
- RETURNING value(rv_boolean) TYPE abap_bool,
- get_log
- RETURNING value(rt_log) TYPE tt_log,
- has_important_news
- RETURNING value(rv_boolean) TYPE abap_bool.
-
- PRIVATE SECTION.
- DATA mt_log TYPE tt_log.
+ IMPORTING iv_rawdata TYPE xstring
+ iv_lastseen_version TYPE string
+ iv_current_version TYPE string.
CLASS-METHODS:
- split_string
- IMPORTING iv_string TYPE string
- RETURNING value(rt_lines) TYPE string_table,
-
- convert_version_to_numeric
+ version_to_numeric
IMPORTING iv_version TYPE string
RETURNING value(rv_version) TYPE i,
-
- parse_data
- IMPORTING it_lines TYPE string_table
- iv_version TYPE string
- RETURNING value(rt_log) TYPE tt_log,
-
+ normalize_version
+ IMPORTING iv_version TYPE string
+ RETURNING value(rv_version) TYPE string,
compare_versions
IMPORTING iv_a TYPE string
iv_b TYPE string
- RETURNING value(rv_result) TYPE i.
+ RETURNING value(rv_result) TYPE i,
+ parse_line
+ IMPORTING iv_line TYPE string
+ iv_current_version TYPE string
+ RETURNING value(rs_log) TYPE ty_log,
+ parse
+ IMPORTING it_lines TYPE string_table
+ iv_current_version TYPE string
+ RETURNING value(rt_log) TYPE tt_log.
ENDCLASS. "lcl_news
@@ -71,155 +85,181 @@ ENDCLASS. "lcl_news
*----------------------------------------------------------------------*
CLASS lcl_news IMPLEMENTATION.
- METHOD constructor.
+ METHOD create. " TODO REFACTOR !
- DATA: lt_lines TYPE string_table,
- lv_string TYPE string.
-
- lv_string = lcl_convert=>xstring_to_string_utf8( iv_rawdata ).
- lt_lines = lcl_news=>split_string( lv_string ).
- mt_log = lcl_news=>parse_data( it_lines = lt_lines iv_version = iv_version ).
-
- ENDMETHOD. "constructor
-
- METHOD create.
+ CONSTANTS: " TODO refactor
+ lc_log_path TYPE string VALUE '/',
+ lc_log_filename TYPE string VALUE 'changelog.txt'.
DATA: lt_remote TYPE ty_files_tt,
+ lv_last_seen TYPE string,
+ lv_url TYPE string,
lo_repo_online TYPE REF TO lcl_repo_online.
FIELD-SYMBOLS LIKE LINE OF lt_remote.
- IF io_repo->is_offline( ) = abap_false.
- lo_repo_online ?= io_repo.
+ IF io_repo->is_offline( ) = abap_true.
+ RETURN.
+ ENDIF.
- " News announcement temporary restricted to abapGit only
- IF lo_repo_online->get_url( ) CS '/abapGit.git'.
- lt_remote = io_repo->get_files_remote( ).
+ lo_repo_online ?= io_repo.
+ lv_url = lo_repo_online->get_url( ).
- READ TABLE lt_remote ASSIGNING
- WITH KEY path = c_log_path filename = c_log_filename.
+ " News announcement temporary restricted to abapGit only
+ IF lv_url NS '/abapGit.git'. " TODO refactor
+ RETURN.
+ ENDIF.
- IF sy-subrc = 0.
- CREATE OBJECT ro_instance EXPORTING
- iv_rawdata = -data
- iv_version = gc_abap_version.
-* iv_version = 'v1.30.0'. " for debug
- ENDIF.
- ENDIF.
+ lv_last_seen = lcl_app=>user( )->get_repo_last_change_seen( lv_url ).
+
+ " Find changelog
+ lt_remote = io_repo->get_files_remote( ).
+ READ TABLE lt_remote ASSIGNING
+ WITH KEY path = lc_log_path filename = lc_log_filename.
+
+ IF sy-subrc = 0.
+ CREATE OBJECT ro_instance EXPORTING
+ iv_rawdata = -data
+ iv_current_version = gc_abap_version " TODO refactor
+ iv_lastseen_version = normalize_version( lv_last_seen ).
+ ENDIF.
+
+ IF ro_instance IS BOUND.
+ lcl_app=>user( )->set_repo_last_change_seen(
+ iv_url = lv_url
+ iv_version = ro_instance->latest_version( ) ).
ENDIF.
ENDMETHOD. "create
- METHOD split_string.
+ METHOD constructor.
- DATA ls_line LIKE LINE OF rt_lines.
+ DATA: lt_lines TYPE string_table,
+ lv_string TYPE string,
+ ls_log_line LIKE LINE OF mt_log.
- FIND FIRST OCCURRENCE OF cl_abap_char_utilities=>cr_lf IN iv_string.
-
- " Convert string into table depending on separator type CR_LF vs. LF
- IF sy-subrc = 0.
- SPLIT iv_string AT cl_abap_char_utilities=>cr_lf INTO TABLE rt_lines.
- ELSE.
- SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE rt_lines.
+ " Validate params
+ mv_current_version = normalize_version( iv_current_version ).
+ mv_lastseen_version = normalize_version( iv_lastseen_version ).
+ IF mv_current_version IS INITIAL.
+ RETURN. " Internal format of program version is not correct -> abort parsing
ENDIF.
- ENDMETHOD. "split_string
+ lv_string = lcl_convert=>xstring_to_string_utf8( iv_rawdata ).
+ lt_lines = lcl_convert=>split_string( lv_string ).
+ mt_log = parse( it_lines = lt_lines iv_current_version = mv_current_version ).
- METHOD parse_data.
+ READ TABLE mt_log INTO ls_log_line INDEX 1.
+ mv_latest_version = ls_log_line-version. " Empty if not found
- CONSTANTS:
- lc_changelog_version TYPE string
- VALUE '^\d{4}-\d{2}-\d{2}\s+v(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$',
- lc_internal_version TYPE string
- VALUE '^v?(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$'.
+ ENDMETHOD. "constructor
- DATA:
+ METHOD parse.
+
+ DATA: lv_tail TYPE i,
lv_first_version_found TYPE abap_bool,
lv_version TYPE string,
- lv_current_version TYPE string,
ls_log LIKE LINE OF rt_log.
FIELD-SYMBOLS: LIKE LINE OF it_lines.
- " Internal program version should be in format "vXXX.XXX.XXX"
- FIND FIRST OCCURRENCE OF REGEX lc_internal_version IN iv_version SUBMATCHES lv_current_version.
-
- IF sy-subrc IS NOT INITIAL.
- RETURN. " Internal format of program version is not correct. TODO implement error message
- ENDIF.
-
LOOP AT it_lines ASSIGNING .
- CLEAR: lv_version, ls_log-text, ls_log-important, ls_log-header.
+ ls_log = parse_line( iv_line = iv_current_version = iv_current_version ).
- IF IS INITIAL OR CO ' -='. " Skip empty and technical lines
- CONTINUE.
- ENDIF.
+ " Skip until first version head and Skip empty lines
+ CHECK ls_log IS NOT INITIAL AND
+ ( lv_first_version_found = abap_true OR ls_log-version IS NOT INITIAL ).
- " Check if line is a header line
- FIND FIRST OCCURRENCE OF REGEX lc_changelog_version IN SUBMATCHES lv_version.
-
- " Skip entries before first version found
IF lv_first_version_found = abap_false.
- IF sy-subrc IS NOT INITIAL.
- CONTINUE.
- ELSE.
- lv_first_version_found = abap_true.
+ lv_first_version_found = abap_true.
+ IF compare_versions( iv_a = ls_log-version iv_b = iv_current_version ) <= 0.
+ lv_tail = c_tail_length. " Display some last versions if no updates
ENDIF.
ENDIF.
- "Skip everything below current version
- IF lv_version IS NOT INITIAL
- AND lcl_news=>compare_versions( iv_a = lv_version iv_b = lv_current_version ) < 1.
- EXIT.
- ENDIF.
-
- " Populate log
- IF lv_version IS NOT INITIAL. " Version header
- ls_log-version = lv_version. " ... stays for all subsequent non-version lines
- ls_log-header = abap_true.
- ELSE. " Version line item
- FIND FIRST OCCURRENCE OF REGEX '^\s*!' IN .
- IF sy-subrc IS INITIAL.
- ls_log-important = abap_true. " Change is important
+ IF ls_log-is_header = abap_true.
+ "Skip everything below current version or show tail news
+ IF compare_versions( iv_a = ls_log-version iv_b = iv_current_version ) <= 0.
+ IF lv_tail > 0.
+ lv_tail = lv_tail - 1.
+ ELSE.
+ EXIT.
+ ENDIF.
ENDIF.
+ lv_version = ls_log-version. " Save to fill news lines
+ ELSE.
+ ls_log-version = lv_version.
ENDIF.
- ls_log-text = .
-
APPEND ls_log TO rt_log.
ENDLOOP.
- ENDMETHOD. "parse_data
+ ENDMETHOD. "parse
- METHOD has_news.
+ METHOD parse_line.
- rv_boolean = boolc( lines( mt_log ) > 0 ).
+ CONSTANTS: lc_header_pattern TYPE string
+ VALUE '^\d{4}-\d{2}-\d{2}\s+v(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$'.
- ENDMETHOD. "has_news
+ DATA: lv_version TYPE string.
+
+ IF iv_line IS INITIAL OR iv_line CO ' -='.
+ RETURN. " Skip empty and markup lines
+ ENDIF.
+
+ " Check if line is a header line
+ FIND FIRST OCCURRENCE OF REGEX lc_header_pattern IN iv_line SUBMATCHES lv_version.
+ IF sy-subrc IS INITIAL.
+ lv_version = normalize_version( lv_version ).
+ rs_log-version = lv_version.
+ rs_log-is_header = abap_true.
+ rs_log-pos_to_cur = compare_versions( iv_a = lv_version iv_b = iv_current_version ).
+ ELSE.
+ FIND FIRST OCCURRENCE OF REGEX '^\s*!' IN iv_line.
+ rs_log-is_important = boolc( sy-subrc IS INITIAL ). " Change is important
+ ENDIF.
+
+ rs_log-text = iv_line.
+
+ ENDMETHOD. "parse_line
METHOD get_log.
-
rt_log = me->mt_log.
-
ENDMETHOD. "get_log
- METHOD has_important_news.
+ METHOD latest_version.
+ rv_version = me->mv_latest_version.
+ ENDMETHOD. "latest_version
- READ TABLE mt_log WITH KEY important = abap_true TRANSPORTING NO FIELDS.
+ METHOD has_news.
+ rv_boolean = boolc( lines( mt_log ) > 0 ).
+ ENDMETHOD. "has_news
+ METHOD has_important.
+ READ TABLE mt_log WITH KEY is_important = abap_true TRANSPORTING NO FIELDS.
rv_boolean = boolc( sy-subrc IS INITIAL ).
-
ENDMETHOD. "has_important_news
+ METHOD has_updates.
+ rv_boolean = boolc( compare_versions(
+ iv_a = mv_latest_version
+ iv_b = mv_current_version ) > 0 ).
+ ENDMETHOD. "has_updates
+
+ METHOD has_unseen.
+ rv_boolean = boolc( compare_versions(
+ iv_a = mv_latest_version
+ iv_b = mv_lastseen_version ) > 0 ).
+ ENDMETHOD. "has_unseen
+
METHOD compare_versions.
- DATA:
- lv_version_a TYPE i,
+ DATA: lv_version_a TYPE i,
lv_version_b TYPE i.
" Convert versions to numeric
- lv_version_a = lcl_news=>convert_version_to_numeric( iv_a ).
- lv_version_b = lcl_news=>convert_version_to_numeric( iv_b ).
+ lv_version_a = version_to_numeric( iv_a ).
+ lv_version_b = version_to_numeric( iv_b ).
" Compare versions
IF lv_version_a > lv_version_b.
@@ -232,7 +272,17 @@ CLASS lcl_news IMPLEMENTATION.
ENDMETHOD. "compare_versions
- METHOD convert_version_to_numeric.
+ METHOD normalize_version.
+
+ " Internal program version should be in format "XXX.XXX.XXX" or "vXXX.XXX.XXX"
+ CONSTANTS: lc_version_pattern TYPE string VALUE '^v?(\d{1,3}\.\d{1,3}\.\d{1,3})\s*$'.
+
+ FIND FIRST OCCURRENCE OF REGEX lc_version_pattern
+ IN iv_version SUBMATCHES rv_version.
+
+ ENDMETHOD. "normalize_version
+
+ METHOD version_to_numeric.
DATA: lv_major TYPE numc4,
lv_minor TYPE numc4,
@@ -240,7 +290,7 @@ CLASS lcl_news IMPLEMENTATION.
SPLIT iv_version AT '.' INTO lv_major lv_minor lv_release.
- " Calculated value of version number
+ " Calculated value of version number, empty version will become 0 which is OK
rv_version = lv_major * 1000000 + lv_minor * 1000 + lv_release.
ENDMETHOD. "convert_version_to_numeric
@@ -258,10 +308,11 @@ CLASS ltcl_news DEFINITION FINAL
PRIVATE SECTION.
METHODS:
- split_string FOR TESTING,
- convert_version_to_numeric FOR TESTING,
- compare_versions FOR TESTING,
- parse_data FOR TESTING.
+ version_to_numeric FOR TESTING,
+ compare_versions FOR TESTING,
+ normalize_version FOR TESTING,
+ parse_line FOR TESTING,
+ parse FOR TESTING.
ENDCLASS. "ltcl_news DEFINITION
@@ -272,38 +323,12 @@ ENDCLASS. "ltcl_news DEFINITION
*----------------------------------------------------------------------*
CLASS ltcl_news IMPLEMENTATION.
- METHOD split_string.
-
- DATA: lt_act TYPE string_table,
- lt_exp TYPE string_table.
-
- APPEND 'ABC' TO lt_exp.
- APPEND '123' TO lt_exp.
-
- " Case 1. String separated by CRLF
- lt_act = lcl_news=>split_string( 'ABC' && cl_abap_char_utilities=>cr_lf && '123' ).
-
- cl_abap_unit_assert=>assert_equals( exp = lt_exp
- act = lt_act
- msg = ' Error during string split: CRLF' ).
-
- CLEAR: lt_act.
-
- " Case 2. String separated by LF
- lt_act = lcl_news=>split_string( 'ABC' && cl_abap_char_utilities=>newline && '123' ).
-
- cl_abap_unit_assert=>assert_equals( exp = lt_exp
- act = lt_act
- msg = ' Error during string split: LF' ).
-
- ENDMETHOD. "split_string.
-
- METHOD convert_version_to_numeric.
+ METHOD version_to_numeric.
DATA: lv_version_exp TYPE i VALUE 1023010,
lv_version_act TYPE i.
- lv_version_act = lcl_news=>convert_version_to_numeric( '1.23.10' ).
+ lv_version_act = lcl_news=>version_to_numeric( '1.23.10' ).
cl_abap_unit_assert=>assert_equals( exp = lv_version_exp
act = lv_version_act
@@ -342,16 +367,99 @@ CLASS ltcl_news IMPLEMENTATION.
ENDMETHOD. "compare_versions
+ METHOD normalize_version.
+
+ cl_abap_unit_assert=>assert_equals(
+ act = lcl_news=>normalize_version( '1.28.10' )
+ exp = '1.28.10' ).
+
+ cl_abap_unit_assert=>assert_equals(
+ act = lcl_news=>normalize_version( 'v1.28.10' )
+ exp = '1.28.10' ).
+
+ cl_abap_unit_assert=>assert_equals(
+ act = lcl_news=>normalize_version( 'b1.28.10' )
+ exp = '' ).
+
+ cl_abap_unit_assert=>assert_equals(
+ act = lcl_news=>normalize_version( 'x.y.z' )
+ exp = '' ).
+
+ ENDMETHOD. "normalize_version
+
+ METHOD parse_line.
+
+ DATA: ls_log TYPE lcl_news=>ty_log.
+
+ ls_log = lcl_news=>parse_line(
+ iv_line = '======'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_initial( act = ls_log ).
+
+ ls_log = lcl_news=>parse_line(
+ iv_line = ''
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_initial( act = ls_log ).
+
+ ls_log = lcl_news=>parse_line(
+ iv_line = '------'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_initial( act = ls_log ).
+
+ CLEAR ls_log.
+ ls_log = lcl_news=>parse_line(
+ iv_line = '2017-02-13 v1.28.0'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-version exp = '1.28.0' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_header exp = abap_true ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-pos_to_cur exp = 1 ).
+
+ CLEAR ls_log.
+ ls_log = lcl_news=>parse_line(
+ iv_line = '2017-02-13 v1.26.0'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-version exp = '1.26.0' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_header exp = abap_true ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-pos_to_cur exp = -1 ).
+
+ CLEAR ls_log.
+ ls_log = lcl_news=>parse_line(
+ iv_line = 'news'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-version exp = '' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_header exp = abap_false ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-pos_to_cur exp = 0 ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_important exp = abap_false ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-text exp = 'news' ).
+
+ CLEAR ls_log.
+ ls_log = lcl_news=>parse_line(
+ iv_line = ' ! important news'
+ iv_current_version = '1.26.01' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-version exp = '' ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_header exp = abap_false ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-pos_to_cur exp = 0 ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-is_important exp = abap_true ).
+ cl_abap_unit_assert=>assert_equals( act = ls_log-text exp = ' ! important news' ).
+
+ ENDMETHOD. "parse_line
+
+
DEFINE _add_news_log_entry.
CLEAR: ls_log.
- ls_log-version = &1.
- ls_log-header = &2.
- ls_log-important = &3.
- ls_log-text = &4.
+ ls_log-version = &1.
+ ls_log-is_header = &2.
+ ls_log-is_important = &3.
+ ls_log-pos_to_cur = &4.
+ ls_log-text = &5.
APPEND ls_log TO lt_log_exp.
END-OF-DEFINITION.
- METHOD parse_data.
+ DEFINE _add_news_txt_entry.
+ APPEND &1 TO lt_lines.
+ END-OF-DEFINITION.
+
+ METHOD parse.
DATA:
lt_log_exp TYPE lcl_news=>tt_log,
@@ -360,43 +468,64 @@ CLASS ltcl_news IMPLEMENTATION.
lt_lines TYPE string_table.
" Generate test data
- APPEND '======' TO lt_lines.
- APPEND '------' TO lt_lines.
- APPEND ` ` TO lt_lines.
- APPEND 'abapGit changelog' TO lt_lines.
- APPEND '2017-02-13 v1.28.0' TO lt_lines.
- APPEND '------------------' TO lt_lines.
- APPEND '+ Staging page redesigned' TO lt_lines.
- APPEND '! Support for core data services' TO lt_lines.
- APPEND ` ` TO lt_lines.
- APPEND '2017-01-25 v1.27.0' TO lt_lines.
- APPEND '------------------' TO lt_lines.
- APPEND '+ Two factor authentication with github.com' TO lt_lines.
- APPEND '2017-01-25 v1.26.0' TO lt_lines.
+ _add_news_txt_entry '======'.
+ _add_news_txt_entry '------'.
+ _add_news_txt_entry ` `.
+ _add_news_txt_entry 'abapGit changelog'.
+ _add_news_txt_entry '2017-02-13 v1.28.0'.
+ _add_news_txt_entry '------------------'.
+ _add_news_txt_entry '+ Staging page redesigned'.
+ _add_news_txt_entry '! Support for core data services'.
+ _add_news_txt_entry ` `.
+ _add_news_txt_entry '2017-01-25 v1.27.0'.
+ _add_news_txt_entry '------------------'.
+ _add_news_txt_entry '+ Two factor authentication with github.com'.
+ _add_news_txt_entry '2017-01-25 v1.26.0'.
+ " Case 1
" Generate expected results
- _add_news_log_entry '1.28.0' 'X' '' '2017-02-13 v1.28.0'.
- _add_news_log_entry '1.28.0' '' '' '+ Staging page redesigned'.
- _add_news_log_entry '1.28.0' '' 'X' '! Support for core data services'.
- _add_news_log_entry '1.27.0' 'X' '' '2017-01-25 v1.27.0'.
- _add_news_log_entry '1.27.0' '' '' '+ Two factor authentication with github.com'.
-
- " Case 1. Test parsing of data
- lt_log_act = lcl_news=>parse_data( it_lines = lt_lines iv_version = '1.26.01' ).
+ " VERSION HEAD IMP POS TEXT
+ _add_news_log_entry '1.28.0' 'X' '' 1 '2017-02-13 v1.28.0'.
+ _add_news_log_entry '1.28.0' '' '' 0 '+ Staging page redesigned'.
+ _add_news_log_entry '1.28.0' '' 'X' 0 '! Support for core data services'.
+ _add_news_log_entry '1.27.0' 'X' '' 1 '2017-01-25 v1.27.0'.
+ _add_news_log_entry '1.27.0' '' '' 0 '+ Two factor authentication with github.com'.
+ lt_log_act = lcl_news=>parse( it_lines = lt_lines iv_current_version = '1.26.01' ).
cl_abap_unit_assert=>assert_equals( exp = lt_log_exp
act = lt_log_act
msg = ' Error during parsing: Case 1.' ).
- CLEAR: lt_log_act, lt_log_exp.
- " Case 2. Negative test - format of version is not correct
- lt_log_act = lcl_news=>parse_data( it_lines = lt_lines iv_version = 'version.1.27.00' ).
+ " Case 2 (exect version match)
+ CLEAR lt_log_exp.
+ " VERSION HEAD IMP UPD TEXT
+ _add_news_log_entry '1.28.0' 'X' '' 1 '2017-02-13 v1.28.0'.
+ _add_news_log_entry '1.28.0' '' '' 0 '+ Staging page redesigned'.
+ _add_news_log_entry '1.28.0' '' 'X' 0 '! Support for core data services'.
+ " Case 1. Test parsing of data
+ lt_log_act = lcl_news=>parse( it_lines = lt_lines iv_current_version = '1.27.00' ).
cl_abap_unit_assert=>assert_equals( exp = lt_log_exp
act = lt_log_act
msg = ' Error during parsing: Case 2.' ).
- ENDMETHOD. "parse_data
+ " Case 3 (display tail)
+ CLEAR lt_log_exp.
+ " VERSION HEAD IMP UPD TEXT
+ _add_news_log_entry '1.28.0' 'X' '' 0 '2017-02-13 v1.28.0'.
+ _add_news_log_entry '1.28.0' '' '' 0 '+ Staging page redesigned'.
+ _add_news_log_entry '1.28.0' '' 'X' 0 '! Support for core data services'.
+ _add_news_log_entry '1.27.0' 'X' '' -1 '2017-01-25 v1.27.0'.
+ _add_news_log_entry '1.27.0' '' '' 0 '+ Two factor authentication with github.com'.
+ _add_news_log_entry '1.26.0' 'X' '' -1 '2017-01-25 v1.26.0'.
+
+ " Case 1. Test parsing of data
+ lt_log_act = lcl_news=>parse( it_lines = lt_lines iv_current_version = '1.28.00' ).
+ cl_abap_unit_assert=>assert_equals( exp = lt_log_exp
+ act = lt_log_act
+ msg = ' Error during parsing: Case 3.' ).
+
+ ENDMETHOD. "parse
ENDCLASS. "ltcl_news IMPLEMENTATION
diff --git a/src/zabapgit_page_main.prog.abap b/src/zabapgit_page_main.prog.abap
index 0fa1c5392..34652313f 100644
--- a/src/zabapgit_page_main.prog.abap
+++ b/src/zabapgit_page_main.prog.abap
@@ -310,9 +310,10 @@ CLASS lcl_gui_page_main IMPLEMENTATION.
lo_news = lcl_news=>create( io_repo ).
ro_html->add( || ).
- ro_html->add( lcl_gui_chunk_lib=>render_repo_top( io_repo = io_repo
- io_news = lo_news
- iv_interactive_branch = abap_true ) ).
+ ro_html->add( lcl_gui_chunk_lib=>render_repo_top(
+ io_repo = io_repo
+ io_news = lo_news
+ iv_interactive_branch = abap_true ) ).
ro_html->add( lcl_gui_chunk_lib=>render_news( io_news = lo_news ) ).
diff --git a/src/zabapgit_persistence.prog.abap b/src/zabapgit_persistence.prog.abap
index b4cd59fd3..7a14d0467 100644
--- a/src/zabapgit_persistence.prog.abap
+++ b/src/zabapgit_persistence.prog.abap
@@ -424,6 +424,16 @@ CLASS lcl_persistence_user DEFINITION FINAL CREATE PRIVATE FRIENDS lcl_app.
RETURNING VALUE(rv_email) TYPE string
RAISING lcx_exception.
+ METHODS set_repo_last_change_seen
+ IMPORTING iv_url TYPE lcl_persistence_repo=>ty_repo-url
+ iv_version TYPE string
+ RAISING lcx_exception.
+
+ METHODS get_repo_last_change_seen
+ IMPORTING iv_url TYPE lcl_persistence_repo=>ty_repo-url
+ RETURNING VALUE(rv_version) TYPE string
+ RAISING lcx_exception.
+
METHODS toggle_hide_files
RETURNING VALUE(rv_hide) TYPE abap_bool
RAISING lcx_exception.
@@ -468,9 +478,10 @@ CLASS lcl_persistence_user DEFINITION FINAL CREATE PRIVATE FRIENDS lcl_app.
TYPES:
BEGIN OF ty_repo_config,
- url TYPE lcl_persistence_repo=>ty_repo-url,
- login TYPE string,
- git_user TYPE ty_git_user,
+ url TYPE lcl_persistence_repo=>ty_repo-url,
+ login TYPE string,
+ git_user TYPE ty_git_user,
+ last_change_seen TYPE string,
END OF ty_repo_config.
TYPES: ty_repo_config_tt TYPE STANDARD TABLE OF ty_repo_config WITH DEFAULT KEY.
@@ -479,11 +490,11 @@ CLASS lcl_persistence_user DEFINITION FINAL CREATE PRIVATE FRIENDS lcl_app.
BEGIN OF ty_user,
default_git_user TYPE ty_git_user,
repo_show TYPE lcl_persistence_repo=>ty_repo-key,
- repo_config TYPE ty_repo_config_tt,
hide_files TYPE abap_bool,
changes_only TYPE abap_bool,
diff_unified TYPE abap_bool,
favorites TYPE tt_favorites,
+ repo_config TYPE ty_repo_config_tt,
END OF ty_user.
METHODS constructor
@@ -657,6 +668,8 @@ CLASS lcl_persistence_user IMPLEMENTATION.
update( ls_user ).
+ COMMIT WORK AND WAIT.
+
ENDMETHOD. "update_repo_config
METHOD set_repo_git_user_name.
@@ -707,6 +720,22 @@ CLASS lcl_persistence_user IMPLEMENTATION.
ENDMETHOD. "get_repo_email
+ METHOD set_repo_last_change_seen.
+
+ DATA: ls_repo_config TYPE ty_repo_config.
+
+ ls_repo_config = read_repo_config( iv_url ).
+ ls_repo_config-last_change_seen = iv_version.
+ update_repo_config( iv_url = iv_url is_repo_config = ls_repo_config ).
+
+ ENDMETHOD. "set_last_change_seen
+
+ METHOD get_repo_last_change_seen.
+
+ rv_version = read_repo_config( iv_url )-last_change_seen.
+
+ ENDMETHOD. "get_last_change_seen
+
METHOD toggle_hide_files.
DATA ls_user TYPE ty_user.
diff --git a/src/zabapgit_unit_test.prog.abap b/src/zabapgit_unit_test.prog.abap
index d35f7620e..5e657bf94 100644
--- a/src/zabapgit_unit_test.prog.abap
+++ b/src/zabapgit_unit_test.prog.abap
@@ -50,6 +50,7 @@ CLASS ltcl_convert DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FIN
PRIVATE SECTION.
METHODS convert_int FOR TESTING RAISING lcx_exception.
+ METHODS split_string FOR TESTING.
ENDCLASS. "ltcl_convert DEFINITION
@@ -79,6 +80,32 @@ CLASS ltcl_convert IMPLEMENTATION.
ENDMETHOD. "convert_int
+ METHOD split_string.
+
+ DATA: lt_act TYPE string_table,
+ lt_exp TYPE string_table.
+
+ APPEND 'ABC' TO lt_exp.
+ APPEND '123' TO lt_exp.
+
+ " Case 1. String separated by CRLF
+ lt_act = lcl_convert=>split_string( 'ABC' && cl_abap_char_utilities=>cr_lf && '123' ).
+
+ cl_abap_unit_assert=>assert_equals( exp = lt_exp
+ act = lt_act
+ msg = ' Error during string split: CRLF' ).
+
+ CLEAR: lt_act.
+
+ " Case 2. String separated by LF
+ lt_act = lcl_convert=>split_string( 'ABC' && cl_abap_char_utilities=>newline && '123' ).
+
+ cl_abap_unit_assert=>assert_equals( exp = lt_exp
+ act = lt_act
+ msg = ' Error during string split: LF' ).
+
+ ENDMETHOD. "split_string.
+
ENDCLASS. "ltcl_convert IMPLEMENTATION
CLASS lth_critical_tests DEFINITION FINAL.
diff --git a/src/zabapgit_util.prog.abap b/src/zabapgit_util.prog.abap
index 8845576d9..5b6689bfb 100644
--- a/src/zabapgit_util.prog.abap
+++ b/src/zabapgit_util.prog.abap
@@ -109,6 +109,10 @@ CLASS lcl_convert DEFINITION FINAL.
iv_length TYPE i
RETURNING VALUE(rv_xstring) TYPE xstring.
+ CLASS-METHODS split_string
+ IMPORTING iv_string TYPE string
+ RETURNING value(rt_lines) TYPE string_table.
+
ENDCLASS. "lcl_convert DEFINITION
*----------------------------------------------------------------------*
@@ -218,6 +222,21 @@ CLASS lcl_convert IMPLEMENTATION.
ENDMETHOD. "x_to_bitbyte
+ METHOD split_string.
+
+ DATA ls_line LIKE LINE OF rt_lines.
+
+ FIND FIRST OCCURRENCE OF cl_abap_char_utilities=>cr_lf IN iv_string.
+
+ " Convert string into table depending on separator type CR_LF vs. LF
+ IF sy-subrc = 0.
+ SPLIT iv_string AT cl_abap_char_utilities=>cr_lf INTO TABLE rt_lines.
+ ELSE.
+ SPLIT iv_string AT cl_abap_char_utilities=>newline INTO TABLE rt_lines.
+ ENDIF.
+
+ ENDMETHOD. "split_string
+
ENDCLASS. "lcl_convert IMPLEMENTATION
*----------------------------------------------------------------------*
|