Merge pull request #618 from sbcgua/master

Stage page UX improvements
This commit is contained in:
Lars Hvam 2017-02-13 15:18:38 +01:00 committed by GitHub
commit 659225ac5c
7 changed files with 306 additions and 148 deletions

View File

@ -24,12 +24,12 @@ img { border: 0px; vertical-align: middle; }
table { border-collapse: collapse; }
pre { display: inline; }
form input, textarea, select {
border: 1px solid #DDD;
input, textarea, select {
padding: 3px 6px;
border: 1px solid #ddd;
}
form input:focus, textarea:focus {
input:focus, textarea:focus {
border: 1px solid #8cadd9;
}
@ -373,7 +373,6 @@ table.repo_tab {
}
.stage_tab th {
color: #BBB;
font-size: 10pt;
text-align: left;
font-weight: normal;
background-color: #edf2f9;
@ -382,22 +381,23 @@ table.repo_tab {
.stage_tab td.status {
width: 2em;
text-align: center;
color: #ccc;
background-color: #fafafa;
}
.stage_tab td.highlight {
color: #444 !important;
font-weight: bold;
}
.stage_tab td.cmd a { padding: 0px 4px; }
.stage_tab th.cmd a { padding: 0px 4px; }
.stage_tab td.method { color: #ccc; }
.stage_tab td.user { color: #aaa; }
.stage_tab td.type { color: #aaa; }
.stage_tab tbody tr:first-child td { padding-top: 0.5em; }
.stage_tab tbody tr:last-child td { padding-bottom: 0.5em; }
.stage_tab td.cmd a { padding: 0px 4px; }
/* STAGE */
.stage_tab td.method {
color: #ccc;
}
.stage_tab tr.firstrow td { border-top: 0px; }
.stage_tab tr.title td {
color: #BBB;
font-size: 10pt;
background-color: #edf2f9;
padding: 4px 0.5em;
text-align: center;
.stage_tab mark {
color: white;
background-color: #79a0d2;
}
/* COMMIT */

View File

@ -32,6 +32,10 @@ CLASS lcl_gui_chunk_lib DEFINITION FINAL.
RETURNING VALUE(ro_html) TYPE REF TO lcl_html
RAISING lcx_exception.
CLASS-METHODS render_js_error_banner
RETURNING VALUE(ro_html) TYPE REF TO lcl_html
RAISING lcx_exception.
ENDCLASS. "lcl_gui_chunk_lib
CLASS lcl_gui_chunk_lib IMPLEMENTATION.
@ -196,10 +200,17 @@ CLASS lcl_gui_chunk_lib IMPLEMENTATION.
lv_error = iv_error.
ENDIF.
ro_html->add( '<div class="dummydiv attention">' ).
ro_html->add( '<div class="dummydiv error">' ).
ro_html->add( |{ lcl_html=>icon( 'alert/red' ) } Error: { lv_error }| ).
ro_html->add( '</div>' ).
ENDMETHOD. "render_error
METHOD render_js_error_banner.
CREATE OBJECT ro_html.
ro_html->add( '<div id="js-error-banner" class="dummydiv error">' ).
ro_html->add( |{ lcl_html=>icon( 'alert/red' ) } JS init error, please log an issue| ).
ro_html->add( '</div>' ).
ENDMETHOD. "render_js_error_stub
ENDCLASS. "lcl_gui_chunk_lib

View File

@ -71,6 +71,22 @@ function submitFormById(id) {
document.getElementById(id).submit();
}
// JS error stub
function errorStub(event) {
var element = event.target || event.srcElement;
var targetName = element.id || element.name || "???";
alert("JS Error, please log an issue (@" + targetName + ")");
}
// confirm JS initilization
function confirmInitialized() {
var errorBanner = document.getElementById("js-error-banner");
if (errorBanner) {
errorBanner.style.display = "none";
}
debugOutput("js: OK"); // Final final confirmation :)
}
/**********************************************************
* STAGE PAGE Logic
**********************************************************/
@ -78,131 +94,219 @@ function submitFormById(id) {
// Stage helper constructor
function StageHelper(params) {
this.pageSeed = params.seed;
this.tabId = params.stageTabId;
this.formAction = params.formAction;
this.commitNodeId = params.commitNodeId;
this.commitAllNodeId = params.commitAllNodeId;
this.choiseCount = 0;
this.setHook();
this.lastSearchValue = "";
// DOM nodes
this.dom = {
stageTab: document.getElementById(params.ids.stageTab),
commitBtn: document.getElementById(params.ids.commitBtn),
commitAllBtn: document.getElementById(params.ids.commitAllBtn),
objectSearch: document.getElementById(params.ids.objectSearch),
fileCounter: document.getElementById(params.ids.fileCounter)
};
// Table columns (autodetection)
this.col = this.detectColumns();
// Constants
this.HIGHLIGHT_STYLE = "highlight";
this.STATUS = {
"add": "A",
"remove": "R",
"ignore": "I",
"reset": "?"
};
this.setHooks();
}
// Hook global click listener on table, load/unload actions
StageHelper.prototype.setHook = function() {
var stageTab = document.getElementById(this.tabId);
StageHelper.prototype.setHooks = function() {
this.dom.stageTab.onclick = this.onTableClick.bind(this);
this.dom.commitBtn.onclick = this.submit.bind(this);
this.dom.objectSearch.oninput = this.onSearch.bind(this);
this.dom.objectSearch.onkeypress = this.onSearch.bind(this);
window.onbeforeunload = this.onPageUnload.bind(this);
window.onload = this.onPageLoad.bind(this);
}
if (stageTab.addEventListener) {
stageTab.addEventListener("click", this.onEvent.bind(this));
} else {
stageTab.attachEvent("onclick", this.onEvent.bind(this));
// Detect column index
StageHelper.prototype.detectColumns = function() {
var dataRow = this.dom.stageTab.tBodies[0].rows[0];
var cols = {};
for (var i = dataRow.cells.length - 1; i >= 0; i--) {
if (dataRow.cells[i].className) cols[dataRow.cells[i].className] = i;
}
window.onbeforeunload = this.onPageUnload.bind(this);
window.onload = this.onPageLoad.bind(this);
return cols;
}
// Store table state on leaving the page
StageHelper.prototype.onPageUnload = function() {
if (!window.sessionStorage) return;
var data = this.collectData();
window.sessionStorage.setItem(this.pageSeed, JSON.stringify(data));
}
// Re-store table state on entering the page
StageHelper.prototype.onPageLoad = function() {
var data = JSON.parse(window.sessionStorage.getItem(this.pageSeed));
var stage = document.getElementById(this.tabId);
for (var i = stage.rows.length - 1; i >= 0; i--) {
var tr = stage.rows[i];
if (tr.parentNode.tagName == "THEAD") continue;
var context = tr.parentNode.className;
var cmd = data[tr.cells[1].innerText];
if (!cmd) continue;
var data = window.sessionStorage && JSON.parse(window.sessionStorage.getItem(this.pageSeed));
this.formatTR(tr, cmd, context);
this.choiseCount += (this.countChoiceImpact(cmd) > 0) ? 1 : 0;
if (data) {
this.iterateStageTab(function (row) {
var status = data[row.cells[this.col.name].innerText];
this.updateRow(row, status || this.STATUS.reset);
});
debugOutput("StageHelper.onPageLoad from Storage");
} else { // Render initial commands
this.iterateStageTab(function (row) {
this.updateRow(row, this.STATUS.reset);
});
debugOutput("StageHelper.onPageLoad initial state");
}
this.updateMenu();
}
// Event handler, change status
StageHelper.prototype.onEvent = function (event) {
if (!event.target) {
if (event.srcElement) { event.target = event.srcElement; }
else { return; }
// Table event handler, change status
StageHelper.prototype.onTableClick = function (event) {
var target = event.target || event.srcElement;
if (!target || target.tagName != "A") return;
var td = target.parentNode;
if (!td || ["TD","TH"].indexOf(td.tagName) == -1 || td.className != "cmd") return;
var status = this.STATUS[target.innerText]; // Convert anchor text to status
var targetRow = td.parentNode;
if (td.tagName === "TD") {
this.updateRow(targetRow, status);
} else { // TH
this.iterateStageTab(function (row) {
if (row.style.display !== "none" // Not filtered out
&& row.className === targetRow.className // Same context as header
) {
this.updateRow(row, status);
}
});
}
if (event.target.tagName != "A") return;
var td = event.target.parentNode;
if (!td || td.tagName != "TD" || td.className != "cmd") return;
var cmd = event.target.innerText;
var tr = td.parentNode;
var context = tr.parentNode.className;
switch (cmd) {
case "add": cmd = "A"; break;
case "remove": cmd = "R"; break;
case "ignore": cmd = "I"; break;
case "reset": cmd = "?"; break;
}
this.formatTR(tr, cmd, context);
this.choiseCount += this.countChoiceImpact(cmd);
this.updateMenu();
}
// Update action counter -> affects menu update after
StageHelper.prototype.countChoiceImpact = function (cmd) {
if ("ARI".indexOf(cmd) > -1) { return 1; }
else if ("?".indexOf(cmd) > -1) { return -1; }
else { alert("Unknown command"); }
// Search object
StageHelper.prototype.onSearch = function (e) {
if ( // Enter hit or clear, IE SUCKS !
e.type === "input" && !e.target.value && this.lastSearchValue
|| e.type === "keypress" && e.which === 13 ) {
this.lastSearchValue = e.target.value;
this.iterateStageTab(this.applyFilterToRow, e.target.value);
}
}
// Re-format table line
StageHelper.prototype.formatTR = function (tr, cmd, context) {
var cmdReset = "<a>reset</a>";
var cmdLocal = "<a>add</a>";
var cmdRemote = "<a>ignore</a><a>remove</a>";
StageHelper.prototype.applyFilterToRow = function (row, filter) {
var td = row.cells[this.col.name];
var origTxt = td.innerText; // without tags
var newTxt = "";
tr.cells[0].innerText = cmd;
if (cmd == "?") {
tr.cells[0].style.color = "#CCC"; //grey
tr.cells[2].innerHTML = (context == "local") ? cmdLocal : cmdRemote;
if (filter) {
newTxt = origTxt.replace(filter, "<mark>"+filter+"</mark>");
if (newTxt !== origTxt) { // fits filter
row.style.display = "table-row";
} else {
row.style.display = "none";
}
} else { // No filter -> just reset the value
newTxt = origTxt;
row.style.display = "table-row";
}
if (td.firstChild.tagName === "A") {
td.firstChild.innerHTML = newTxt;
} else {
tr.cells[0].style.color = "";
tr.cells[2].innerHTML = cmdReset;
td.innerHTML = newTxt;
}
}
// Get how status should affect object counter
StageHelper.prototype.getStatusImpact = function (status) {
if (typeof status !== "string"
|| status.length !== 1
|| "ARI?".indexOf(status) == -1) {
alert("Unknown status");
} else {
return (status !== this.STATUS.reset) ? 1 : 0;
}
}
// Update table line
StageHelper.prototype.updateRow = function (row, newStatus) {
var oldStatus = row.cells[this.col.status].innerText;
if (oldStatus !== newStatus) {
row.cells[this.col.status].innerText = newStatus;
if (newStatus === this.STATUS.reset) {
row.cells[this.col.status].classList.remove(this.HIGHLIGHT_STYLE);
} else {
row.cells[this.col.status].classList.add(this.HIGHLIGHT_STYLE);
}
this.updateRowCommand(row, newStatus);
} else if (!row.cells[this.col.cmd].innerText) {
this.updateRowCommand(row, newStatus); // For initial run
}
this.choiseCount += this.getStatusImpact(newStatus) - this.getStatusImpact(oldStatus);
}
// Update command cell (render set of commands)
StageHelper.prototype.updateRowCommand = function (row, status) {
var CMD_RESET = "<a>reset</a>";
var CMD_LOCAL = "<a>add</a>";
var CMD_REMOTE = "<a>ignore</a><a>remove</a>";
if (status === this.STATUS.reset) {
row.cells[this.col.cmd].innerHTML = (row.className == "local") ? CMD_LOCAL : CMD_REMOTE;
} else {
row.cells[this.col.cmd].innerHTML = CMD_RESET;
}
}
// Update menu items visibility
StageHelper.prototype.updateMenu = function () {
if (this.choiseCount > 0) {
document.getElementById(this.commitNodeId).style.display = "inline";
document.getElementById(this.commitAllNodeId).style.display = "none";
} else {
document.getElementById(this.commitNodeId).style.display = "none";
document.getElementById(this.commitAllNodeId).style.display = "inline";
}
this.dom.commitBtn.style.display = (this.choiseCount > 0) ? "inline" : "none";
this.dom.commitAllBtn.style.display = (this.choiseCount > 0) ? "none" : "inline";
this.dom.fileCounter.innerHTML = this.choiseCount.toString();
}
// Submin stage state to the server
// Submit stage state to the server
StageHelper.prototype.submit = function () {
var data = this.collectData();
submitSapeventForm(data, this.formAction);
submitSapeventForm(this.collectData(), this.formAction);
}
// Extract data from the table
StageHelper.prototype.collectData = function () {
var stage = document.getElementById(this.tabId);
var data = {};
for (var i = 0; i < stage.rows.length; i++) {
var row = stage.rows[i];
if (row.parentNode.tagName == "THEAD") continue;
data[row.cells[1].innerText] = row.cells[0].innerText;
}
this.iterateStageTab(function (row) {
data[row.cells[this.col.name].innerText] = row.cells[this.col.status].innerText;
});
return data;
}
// table iteration helper
StageHelper.prototype.iterateStageTab = function (cb /*, ...*/) {
for (var b = 0, bN = this.dom.stageTab.tBodies.length; b < bN; b++) {
var tbody = this.dom.stageTab.tBodies[b];
for (var r = 0, rN = tbody.rows.length; r < rN; r++) {
args = [tbody.rows[r]].concat(Array.prototype.slice.call(arguments, 1));
cb.apply(this, args); // callback
}
}
}

View File

@ -55,7 +55,7 @@ CLASS lcl_object_smim IMPLEMENTATION.
SELECT SINGLE chng_user FROM smimloio INTO rv_user
WHERE loio_id = lv_loio. "#EC CI_GENBUFF
IF sy-subrc <> 0.
IF sy-subrc <> 0 OR rv_user IS INITIAL.
rv_user = c_user_unknown.
ENDIF.

View File

@ -176,7 +176,7 @@ CLASS lcl_gui_page IMPLEMENTATION.
IF lo_script IS BOUND AND lo_script->is_empty( ) = abap_false.
ro_html->add( '<script type="text/javascript">' ).
ro_html->add( lo_script ).
ro_html->add( 'debugOutput("js: OK");' ).
ro_html->add( 'confirmInitialized();' ).
ro_html->add( '</script>' ).
ENDIF.

View File

@ -101,10 +101,13 @@ CLASS lcl_gui_page_commit IMPLEMENTATION.
lt_stage = mo_stage->get_all( ).
ro_html->add( '<table class="stage_tab">' ).
ro_html->add( '<tr class="title firstrow">').
ro_html->add( '<td colspan="2">Staged files</td>').
ro_html->add( '<thead>' ).
ro_html->add( '<tr>').
ro_html->add( '<th colspan="2">Staged files</th>').
ro_html->add( '</tr>' ).
ro_html->add( '</thead>' ).
ro_html->add( '<tbody>' ).
LOOP AT lt_stage ASSIGNING <ls_stage>.
ro_html->add( '<tr>' ).
ro_html->add( '<td class="method">' ).
@ -115,6 +118,7 @@ CLASS lcl_gui_page_commit IMPLEMENTATION.
ro_html->add( '</td>' ).
ro_html->add( '</tr>' ).
ENDLOOP.
ro_html->add( '</tbody>' ).
ro_html->add( '</table>' ).

View File

@ -32,8 +32,9 @@ CLASS lcl_gui_page_stage DEFINITION FINAL INHERITING FROM lcl_gui_page.
render_list
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
render_file
IMPORTING is_file TYPE ty_file
iv_context TYPE string
IMPORTING iv_context TYPE string
is_file TYPE ty_file
is_item TYPE ty_item OPTIONAL
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
render_menu
RETURNING VALUE(ro_html) TYPE REF TO lcl_html,
@ -44,6 +45,8 @@ CLASS lcl_gui_page_stage DEFINITION FINAL INHERITING FROM lcl_gui_page.
METHODS process_stage_list
IMPORTING it_postdata TYPE cnht_post_data_tab
RAISING lcx_exception.
METHODS build_menu
RETURNING VALUE(ro_menu) TYPE REF TO lcl_html_toolbar.
ENDCLASS.
@ -52,10 +55,10 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
ms_control-page_title = 'STAGE'.
mo_repo = io_repo.
ms_files = lcl_stage_logic=>get( mo_repo ).
ms_control-page_title = 'STAGE'.
mo_repo = io_repo.
ms_files = lcl_stage_logic=>get( mo_repo ).
CREATE OBJECT mo_stage
EXPORTING
@ -64,8 +67,21 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
GET TIME STAMP FIELD mv_ts.
ms_control-page_menu = build_menu( ).
ENDMETHOD.
METHOD build_menu.
CREATE OBJECT ro_menu.
IF lines( ms_files-local ) > 0.
ro_menu->add( iv_txt = |All diffs|
iv_act = |{ gc_action-go_diff }?key={ mo_repo->get_key( ) }| ).
ENDIF.
ENDMETHOD. "build_menu
METHOD lif_gui_page~on_event.
FIELD-SYMBOLS: <ls_file> LIKE LINE OF ms_files-local.
@ -146,25 +162,25 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
CREATE OBJECT ro_html.
ro_html->add( '<table id="stage_tab" class="stage_tab">' ).
ro_html->add( '<table id="stageTab" class="stage_tab">' ).
" Local changes
LOOP AT ms_files-local ASSIGNING <ls_local>.
AT FIRST.
ro_html->add('<thead><tr>').
ro_html->add('<th></th><th colspan="2">LOCAL</th>' ).
ro_html->add('<th>' ).
IF lines( ms_files-local ) > 1.
ro_html->add_a( iv_txt = |{ lines( ms_files-local ) } diffs|
iv_act = |{ gc_action-go_diff }?key={ mo_repo->get_key( ) }| ).
ENDIF.
ro_html->add('</th>').
ro_html->add('<th>Last changed by</th>').
ro_html->add('<thead><tr class="local">').
ro_html->add('<th>Type</th>').
ro_html->add('<th>Files to add (click to see diff)</th>' ).
ro_html->add('<th>Changed by</th>').
ro_html->add('<th></th>' ). " Status
ro_html->add('<th class="cmd">&#x2193;<a>add</a>/<a>reset</a>&#x2193;</th>' ).
ro_html->add('</tr></thead>').
ro_html->add('<tbody class="local">').
ro_html->add('<tbody>').
ENDAT.
ro_html->add( render_file( is_file = <ls_local>-file iv_context = 'local' ) ).
ro_html->add( render_file(
iv_context = 'local'
is_file = <ls_local>-file
is_item = <ls_local>-item ) ). " TODO Refactor, unify structure
AT LAST.
ro_html->add('</tbody>').
@ -174,13 +190,19 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
" Remote changes
LOOP AT ms_files-remote ASSIGNING <ls_remote>.
AT FIRST.
ro_html->add('<thead><tr>').
ro_html->add('<th></th><th colspan="3">REMOTE</th>' ).
ro_html->add('</tr></thead>').
ro_html->add('<tbody class="remote">').
ro_html->add( '<thead><tr class="remote">' ).
ro_html->add( '<th></th>' ). " Type
ro_html->add( '<th colspan="2">Files to remove or non-code</th>' ).
ro_html->add( '<th></th>' ). " Status
ro_html->add( '<th class="cmd">' &&
'&#x2193;<a>ignore</a><a>remove</a><a>reset</a>&#x2193;</th>' ).
ro_html->add( '</tr></thead>' ).
ro_html->add( '<tbody>' ).
ENDAT.
ro_html->add( render_file( is_file = <ls_remote> iv_context = 'remote' ) ).
ro_html->add( render_file(
iv_context = 'remote'
is_file = <ls_remote> ) ).
AT LAST.
ro_html->add('</tbody>').
@ -193,31 +215,32 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
METHOD render_file.
DATA: lv_param TYPE string,
lv_user TYPE xubname.
DATA: lv_param TYPE string,
lv_filename TYPE string.
CREATE OBJECT ro_html.
lv_filename = is_file-path && is_file-filename.
ro_html->add( |<tr class="{ iv_context }">| ).
ro_html->add( |<td class="status" style="color: #CCC">?</td>| ).
ro_html->add( |<td>{ is_file-path && is_file-filename }</td>| ).
CASE iv_context.
WHEN 'local'.
lv_param = lcl_html_action_utils=>file_encode( iv_key = mo_repo->get_key( )
ig_file = is_file ).
ro_html->add( '<td class="cmd"><a>add</a></td>' ).
ro_html->add( '<td>' ).
ro_html->add_a( iv_txt = 'diff' iv_act = |{ gc_action-go_diff }?{ lv_param }| ).
ro_html->add( '</td>' ).
lv_user = read_last_changed_by( is_file ).
ro_html->add( |<td>{ lv_user }</td> | ).
lv_param = lcl_html_action_utils=>file_encode( iv_key = mo_repo->get_key( )
ig_file = is_file ).
lv_filename = lcl_html=>a( iv_txt = lv_filename
iv_act = |{ gc_action-go_diff }?{ lv_param }| ).
ro_html->add( |<td class="type">{ is_item-obj_type }</td>| ).
ro_html->add( |<td class="name">{ lv_filename }</td>| ).
ro_html->add( |<td class="user">{ read_last_changed_by( is_file ) }</td>| ).
WHEN 'remote'.
ro_html->add( '<td class="cmd"><a>ignore</a><a>remove</a></td>' ).
ro_html->add( |<td><span class="grey">-</span></td>| ).
ro_html->add( '<td class="type">-</td>' ). " Dummy for object type
ro_html->add( |<td class="name">{ lv_filename }</td>| ).
ro_html->add( '<td></td>' ). " Dummy for changed-by
ENDCASE.
ro_html->add( |<td class="status">?</td>| ).
ro_html->add( '<td class="cmd"></td>' ). " Command added in JS
ro_html->add( '</tr>' ).
ENDMETHOD. "render_file
@ -228,6 +251,7 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
ro_html->add( '<div class="repo">' ).
ro_html->add( lcl_gui_chunk_lib=>render_repo_top( mo_repo ) ).
ro_html->add( lcl_gui_chunk_lib=>render_js_error_banner( ) ).
ro_html->add( render_menu( ) ).
ro_html->add( render_list( ) ).
ro_html->add( '</div>' ).
@ -236,22 +260,29 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
METHOD render_menu.
DATA lv_local_count TYPE i.
CREATE OBJECT ro_html.
lv_local_count = lines( ms_files-local ).
ro_html->add( '<div class="paddings">' ).
ro_html->add_a( iv_act = 'gHelper.submit();'
ro_html->add_a( iv_act = 'errorStub(event)' " Will be reinit by JS
iv_typ = gc_action_type-onclick
iv_id = 'act_commit'
iv_id = 'commitButton'
iv_style = 'display: none'
iv_txt = 'Commit'
iv_txt = 'Commit (<span id="fileCounter"></span>)'
iv_opt = gc_html_opt-strong ) ##NO_TEXT.
IF lines( ms_files-local ) > 0.
IF lv_local_count > 0.
ro_html->add_a( iv_act = |{ c_action-stage_all }|
iv_id = 'act_commit_all'
iv_txt = 'Add all and commit') ##NO_TEXT.
iv_id = 'commitAllButton'
iv_txt = |Add all and commit ({ lv_local_count })| ) ##NO_TEXT.
ENDIF.
ro_html->add( '</div>' ).
ro_html->add( '<div>' ).
ro_html->add( '<input id="objectSearch" type="search" placeholder="Filter objects">' ).
ro_html->add( '</div>' ).
ENDMETHOD. "render_menu
METHOD scripts.
@ -260,10 +291,16 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
ro_html->add( 'var gStageParams = {' ).
ro_html->add( | seed: "stage{ mv_ts }",| ).
ro_html->add( ' stageTabId: "stage_tab",' ).
ro_html->add( ' formAction: "stage_commit",' ).
ro_html->add( ' commitNodeId: "act_commit",' ).
ro_html->add( ' commitAllNodeId: "act_commit_all"' ).
ro_html->add( ' ids: {' ).
ro_html->add( ' stageTab: "stageTab",' ).
ro_html->add( ' commitBtn: "commitButton",' ).
ro_html->add( ' commitAllBtn: "commitAllButton",' ).
ro_html->add( ' objectSearch: "objectSearch",' ).
ro_html->add( ' fileCounter: "fileCounter"' ).
ro_html->add( ' }' ).
ro_html->add( '}' ).
ro_html->add( 'var gHelper = new StageHelper(gStageParams);' ).
@ -281,6 +318,8 @@ CLASS lcl_gui_page_stage IMPLEMENTATION.
CATCH lcx_exception.
CLEAR rv_user. "Should not raise errors if user last changed by was not found
ENDTRY.
rv_user = to_lower( rv_user ).
ENDMETHOD.
ENDCLASS.