Dependency Graph
View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0008140 | mantisbt | bugtracker | public | 2007-07-11 08:56 | 2009-12-15 18:26 |
Reporter | giallu | Assigned To | |||
Priority | normal | Severity | feature | Reproducibility | always |
Status | acknowledged | Resolution | open | ||
Summary | 0008140: Reply to note function | ||||
Description | Bugnotes lacks a "reply" link or button. This should create a copy of the note to the "Add note" input area, prepending all lines with "> ", much like the reply function of email clients | ||||
Tags | patch | ||||
Attached Files | reply.patch (4,436 bytes)
Index: bugnote_view_inc.php =================================================================== RCS file: /cvs/mantis/bugnote_view_inc.php,v retrieving revision 1.1.1.8 retrieving revision 1.13 diff -u -r1.1.1.8 -r1.13 --- bugnote_view_inc.php 22 May 2008 03:38:22 -0000 1.1.1.8 +++ bugnote_view_inc.php 13 Jul 2009 08:35:42 -0000 1.13 @@ -157,6 +157,22 @@ $t_can_delete_note = true; } + + # reply patch + $t_linebegin = "<b>"; + $t_lineend = "</b>"; + $t_text = $t_linebegin . + sprintf( + lang_get( 'in_reply_to' ), + config_get( 'bugnote_link_tag' ) . $v3_id, + prepare_user_name( $v3_reporter_id ) ) . + $t_lineend . "\r\n" . + preg_replace( "/^([^\r\n]*)/m", + $t_linebegin . "> $1". $t_lineend, + $row[ 'note' ] ); + print_button( 'bug_change_status_page.php?new_status=20&new_handler='.$v3_reporter_id.'&bug_id='.$f_bug_id.'&bugnote_text='.urlencode($t_text),lang_get( 'reply' ) ); + + # users above update_bugnote_threshold should be able to edit this bugnote if ( $t_can_edit_note || access_has_bug_level( config_get( 'update_bugnote_threshold' ), $f_bug_id ) ) { print_button( 'bugnote_edit_page.php?bugnote_id='.$v3_id, lang_get( 'bugnote_edit_link' ) ); Index: bug_change_status_page.php =================================================================== RCS file: /cvs/mantis/bug_change_status_page.php,v retrieving revision 1.1.1.9 retrieving revision 1.9 diff -u -r1.1.1.9 -r1.9 --- bug_change_status_page.php 9 Dec 2008 19:20:28 -0000 1.1.1.9 +++ bug_change_status_page.php 11 Jul 2009 08:15:28 -0000 1.9 @@ -35,11 +35,15 @@ <?php $f_bug_id = gpc_get_int( 'bug_id' ); $t_bug = bug_get( $f_bug_id ); + + # reply patch + $f_bugnote_text = gpc_get_string( 'bugnote_text', '' ); + $f_new_handler = gpc_get_int( 'new_handler', NO_USER ); if( $t_bug->project_id != helper_get_current_project() ) { # in case the current project is not the same project of the bug we are viewing... # ... override the current project. This to avoid problems with categories and handlers lists etc. $g_project_override = $t_bug->project_id; } $f_new_status = gpc_get_int( 'new_status' ); @@ -160,8 +165,33 @@ <td> <select name="handler_id"> <option value="0"></option> - <?php print_assign_to_option_list( $t_bug->handler_id, $t_bug->project_id ) ?> + <?php + # reply patch + print_assign_to_option_list( + $f_new_handler != NO_USER ? + $f_new_handler : + ( # ( FEEDBACK == $f_new_status ? + # $t_bug->reporter_id : + $t_bug->handler_id ), $t_bug->project_id ) ?> </select> + <?php + # reply patch + if ( $f_new_handler != NO_USER && + $t_bug->handler_id != $f_new_handler ) { + echo '(default: changed from "' . + prepare_user_name( $t_bug->handler_id ) . + '" to "' . + prepare_user_name( $f_new_handler ) . '")'; + } # else { + # if ( FEEDBACK == $f_new_status && + # $t_bug->reporter_id != $t_bug->handler_id ) { + # echo '(default: changed from "' . + # prepare_user_name( $t_bug->handler_id ) . + # '" to reporter "' . + # prepare_user_name( $t_bug->reporter_id ) . '")'; + # } + } + ?> </td> </tr> <?php } ?> @@ -274,7 +304,10 @@ <?php echo lang_get( 'add_bugnote_title' ) ?> </td> <td class="center"> - <textarea name="bugnote_text" cols="80" rows="10"></textarea> + <textarea name="bugnote_text" cols="80" rows="10"><?php + # reply-patch + echo $f_bugnote_text; +?></textarea> </td> </tr> <?php if ( access_has_bug_level( config_get( 'private_bugnote_threshold' ), $f_bug_id ) ) { ?> Index: custom_strings_inc.php =================================================================== RCS file: custom_strings_inc.php diff -N custom_strings_inc.php --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ custom_strings_inc.php 13 Jul 2009 08:35:42 -0000 1.30 @@ -0,0 +1,9 @@ +<?php + +# -------------------------------------------------------- +# $Id$ +# -------------------------------------------------------- +# + +$s_reply = 'Reply'; +$s_in_reply_to = 'In reply to %s from %s'; reply_corrected.patch (4,166 bytes)
diff --git a/bug_change_status_page.php b/bug_change_status_page.php index 97e0fab..e914438 100644 --- a/bug_change_status_page.php +++ b/bug_change_status_page.php @@ -36,6 +36,10 @@ $f_bug_id = gpc_get_int( 'bug_id' ); $t_bug = bug_get( $f_bug_id ); + # reply patch + $f_bugnote_text = gpc_get_string( 'bugnote_text', '' ); + $f_new_handler = gpc_get_int( 'new_handler', NO_USER ); + if( $t_bug->project_id != helper_get_current_project() ) { # in case the current project is not the same project of the bug we are viewing... # ... override the current project. This to avoid problems with categories and handlers lists etc. @@ -160,8 +164,33 @@ if ( ( $t_resolved > $f_new_status ) && <td> <select name="handler_id"> <option value="0"></option> - <?php print_assign_to_option_list( $t_bug->handler_id, $t_bug->project_id ) ?> + <?php + # reply patch + print_assign_to_option_list( + $f_new_handler != NO_USER ? + $f_new_handler : + ( # ( FEEDBACK == $f_new_status ? + # $t_bug->reporter_id : + $t_bug->handler_id ), $t_bug->project_id ) ?> </select> + <?php + # reply patch + if ( $f_new_handler != NO_USER && + $t_bug->handler_id != $f_new_handler ) { + echo '(default: changed from "' . + prepare_user_name( $t_bug->handler_id ) . + '" to "' . + prepare_user_name( $f_new_handler ) . '")'; + } # else { + # if ( FEEDBACK == $f_new_status && + # $t_bug->reporter_id != $t_bug->handler_id ) { + # echo '(default: changed from "' . + # prepare_user_name( $t_bug->handler_id ) . + # '" to reporter "' . + # prepare_user_name( $t_bug->reporter_id ) . '")'; + # } + # } + ?> </td> </tr> <?php } ?> @@ -274,7 +303,10 @@ if ( ( $f_new_status >= $t_resolved ) && ( CLOSED > $f_new_status ) ) { ?> <?php echo lang_get( 'add_bugnote_title' ) ?> </td> <td class="center"> - <textarea name="bugnote_text" cols="80" rows="10"></textarea> + <textarea name="bugnote_text" cols="80" rows="10"><?php + # reply-patch + echo $f_bugnote_text; +?></textarea> </td> </tr> <?php if ( access_has_bug_level( config_get( 'private_bugnote_threshold' ), $f_bug_id ) ) { ?> diff --git a/bugnote_view_inc.php b/bugnote_view_inc.php index 50ff0af..321e809 100644 --- a/bugnote_view_inc.php +++ b/bugnote_view_inc.php @@ -157,6 +157,22 @@ $t_can_delete_note = true; } + + # reply patch + $t_linebegin = "<b>"; + $t_lineend = "</b>"; + $t_text = $t_linebegin . + sprintf( + lang_get( 'in_reply_to' ), + config_get( 'bugnote_link_tag' ) . $v3_id, + prepare_user_name( $v3_reporter_id ) ) . + $t_lineend . "\r\n" . + preg_replace( "/^([^\r\n]*)/m", + $t_linebegin . "> $1". $t_lineend, + $row[ 'note' ] ); + print_button( 'bug_change_status_page.php?new_status=20&new_handler='.$v3_reporter_id.'&bug_id='.$f_bug_id.'&bugnote_text='.urlencode($t_text),lang_get( 'reply' ) ); + + # users above update_bugnote_threshold should be able to edit this bugnote if ( $t_can_edit_note || access_has_bug_level( config_get( 'update_bugnote_threshold' ), $f_bug_id ) ) { print_button( 'bugnote_edit_page.php?bugnote_id='.$v3_id, lang_get( 'bugnote_edit_link' ) ); diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 68091d6..9f19c2d 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -157,6 +157,10 @@ $s_set_sticky_group_bugs_button = 'Set/Unset Sticky'; $s_fixed_in_version_group_bugs_button = 'Update Fixed in Version'; $s_target_version_group_bugs_button = 'Update Target Version'; +# reply patch +$s_reply = 'Reply'; +$s_in_reply_to = 'In reply to %s from %s'; + # improved JPgraphs strings $s_graph_imp_status_title = 'Synthesis graphs by status'; $s_graph_imp_priority_title = 'Synthesis graphs by priority'; mantis_quote.patch (2,280 bytes)
diff -rupN mantisbt-1.1.7/bugnote_view_inc.php mantisae-1.1.7/bugnote_view_inc.php --- mantisbt-1.1.7/bugnote_view_inc.php 2009-04-20 15:14:00.000000000 +0200 +++ mantisae-1.1.7/bugnote_view_inc.php 2009-09-18 09:19:13.000000000 +0200 @@ -178,6 +178,16 @@ print_button('bugnote_set_view_state.php?private=1&bugnote_id='.$v3_id, lang_get( 'make_private' )); } } + + if ( access_has_bug_level( config_get( 'add_bugnote_threshold' ), $f_bug_id ) ) { + echo "<form name=\"Quote\" action=\"\">"; + echo "<input class=\"button-small\" value=\""; + echo lang_get( 'quote_bugnote_button' ); + echo "\" type=\"button\" onclick=\"QuoteBugNote('"; + echo $v3_id; + echo "')\">"; + echo "</form>"; + } } ?> </div> diff -rupN mantisbt-1.1.7/javascript/common.js mantisae-1.1.7/javascript/common.js --- mantisbt-1.1.7/javascript/common.js 2009-04-20 15:14:00.000000000 +0200 +++ mantisae-1.1.7/javascript/common.js 2009-09-18 09:10:33.000000000 +0200 @@ -22,6 +22,34 @@ * -------------------------------------------------------- */ +function QuoteBugNote( pBugNode ) { + var dst = document.getElementsByName("bugnote_text")[0]; + var node = document.getElementById("c" + pBugNode); + var von = node.getElementsByTagName("td")[0].getElementsByTagName("br")[0].nextSibling.data; + von = von.replace (/^\s+/, '').replace (/\s+$/, ''); + + var text = node.getElementsByTagName("td")[1].textContent || node.getElementsByTagName("td")[1].innerText; + text = text.replace (/^\s+/, '').replace (/\s+$/, ''); + var lines = text.split(/\n/); + var message = von + " wrote in node ~" + pBugNode + ":\n"; + var maxCols = dst.cols -3; + for(var i=0; i<lines.length; i++){ + var tmp = lines[i].replace (/^\s+/, '').replace (/\s+$/, '') + "\n"; + var lastSpace = 0; + while(tmp.length > maxCols ){ + lastSpace = tmp.lastIndexOf(" ", maxCols); + if(lastSpace > 0){ + message += "> " + tmp.substr(0, lastSpace) + "\n"; + tmp = tmp.substr(lastSpace + 1); + } else { + message += "> " + tmp.substr(0, maxCols) + "\n"; + tmp = tmp.substr(maxCols + 1); + } + } + message += "> " + tmp.replace (/^\s+/, '').replace (/\s+$/, '') + "\n"; + } + dst.value += message + "\n"; +} /* * String manipulation reply3.patch (8,495 bytes)
diff --git a/bugnote_view_inc.php b/bugnote_view_inc.php index 3b6a2cf..58bd928 100644 --- a/bugnote_view_inc.php +++ b/bugnote_view_inc.php @@ -146,6 +146,24 @@ $num_notes = count( $t_bugnotes ); $t_can_delete_note = true; } + # reply button + if ( access_has_bug_level( config_get( 'add_bugnote_threshold' ), $f_bug_id ) ) { + $t_text = sprintf( + lang_get( 'in_reply_to' ), + config_get( 'bugnote_link_tag' ) . $t_bugnote->id, + user_get_name( $t_bugnote->reporter_id ) ) . + "\r\n" . + preg_replace( "/^([^\r\n]*)/m", "> $1", $t_bugnote->note ); + + echo "<form name=\"Quote\" action=\"\">"; + echo "<input class=\"button-small\" value=\""; + echo lang_get( 'reply' ); + echo "\" type=\"button\" onclick=\"AddBugNoteText('"; + echo rawurlencode(utf8_decode( $t_text )); + echo "')\">"; + echo "</form>"; + } + # users above update_bugnote_threshold should be able to edit this bugnote if ( $t_can_edit_note || access_has_bug_level( config_get( 'update_bugnote_threshold' ), $f_bug_id ) ) { print_button( 'bugnote_edit_page.php?bugnote_id='.$t_bugnote->id, lang_get( 'bugnote_edit_link' ) ); diff --git a/core/string_api.php b/core/string_api.php index 407fcc6..10b1871 100644 --- a/core/string_api.php +++ b/core/string_api.php @@ -120,6 +120,18 @@ function string_nl2br( $p_string, $p_wrap = 100 ) { } /** + * Show quotes + * @param string $p_string + * @return string + */ +function string_quoting( $p_string ) { + return preg_replace( "/^(> [^\r\n]*)/m", + "<b>$1</b>", + $p_string ); +} + + +/** * Prepare a multiple line string for display to HTML * @param string $p_string * @return string diff --git a/javascript/dev/common.js b/javascript/dev/common.js index af6d397..b149f25 100644 --- a/javascript/dev/common.js +++ b/javascript/dev/common.js @@ -22,6 +22,13 @@ * -------------------------------------------------------- */ +function AddBugNoteText( text ) { + var dst = document.getElementsByName("bugnote_text")[0]; + dst.value += unescape(text) + "\n"; + dst.focus(); +} + + /* * String manipulation */ diff --git a/javascript/min/common.js b/javascript/min/common.js index 70c6203..ac7537a 100644 --- a/javascript/min/common.js +++ b/javascript/min/common.js @@ -1 +1,7 @@ -function Trim(d){if(typeof d!="string"){return d}var c=d;var b="";b=c.substring(0,1);while(b==" "){c=c.substring(1,c.length);b=c.substring(0,1)}b=c.substring(c.length-1,c.length);while(b==" "){c=c.substring(0,c.length-1);b=c.substring(c.length-1,c.length)}return c}function GetCookie(f){var c="MANTIS_"+f;var b=document.cookie;b=b.split(";");var d=0;while(d<b.length){var e=b[d];e=e.split("=");if(Trim(e[0])==c){return(e[1])}d++}return -1}function SetCookie(e,d){var b="MANTIS_"+e;var c=new Date();c.setTime(c.getTime()+(365*24*60*60*1000));document.cookie=b+"="+d+"; expires="+c.toUTCString()+";"}var g_collapse_clear=1;function ToggleDiv(b){t_open_div=document.getElementById(b+"_open");t_closed_div=document.getElementById(b+"_closed");t_cookie=GetCookie("collapse_settings");if(1==g_collapse_clear){t_cookie="";g_collapse_clear=0}if(t_open_div.className=="hidden"){t_open_div.className="";t_closed_div.className="hidden";t_cookie=t_cookie+"|"+b+",1"}else{t_closed_div.className="";t_open_div.className="hidden";t_cookie=t_cookie+"|"+b+",0"}SetCookie("collapse_settings",t_cookie)}function checkall(p_formname,p_state){var t_elements=(eval("document."+p_formname+".elements"));for(var i=0;i<t_elements.length;i++){if(t_elements[i].type=="checkbox"){t_elements[i].checked=p_state}}}var a=navigator.userAgent.indexOf("MSIE");var style_display;if(a!=-1){style_display="block"}else{style_display="table-row"}style_display="block";function setDisplay(c,b){if(!document.getElementById(c)){alert("SetDisplay(): id "+c+" is empty")}if(b!=0){document.getElementById(c).style.display=style_display}else{document.getElementById(c).style.display="none"}}function toggleDisplay(b){setDisplay(b,(document.getElementById(b).style.display=="none")?1:0)}function tag_string_append(b){t_tag_separator=document.getElementById("tag_separator").value;t_tag_string=document.getElementById("tag_string");t_tag_select=document.getElementById("tag_select");if(Trim(b)==""){return}if(t_tag_string.value!=""){t_tag_string.value=t_tag_string.value+t_tag_separator+b}else{t_tag_string.value=t_tag_string.value+b}t_tag_select.selectedIndex=0}; \ No newline at end of file +function AddBugNoteText( text ) { + var dst = document.getElementsByName("bugnote_text")[0]; + dst.value += unescape(text) + "\n"; + dst.focus(); +} + +function Trim(d){if(typeof d!="string"){return d}var c=d;var b="";b=c.substring(0,1);while(b==" "){c=c.substring(1,c.length);b=c.substring(0,1)}b=c.substring(c.length-1,c.length);while(b==" "){c=c.substring(0,c.length-1);b=c.substring(c.length-1,c.length)}return c}function GetCookie(f){var c="MANTIS_"+f;var b=document.cookie;b=b.split(";");var d=0;while(d<b.length){var e=b[d];e=e.split("=");if(Trim(e[0])==c){return(e[1])}d++}return -1}function SetCookie(e,d){var b="MANTIS_"+e;var c=new Date();c.setTime(c.getTime()+(365*24*60*60*1000));document.cookie=b+"="+d+"; expires="+c.toUTCString()+";"}var g_collapse_clear=1;function ToggleDiv(b){t_open_div=document.getElementById(b+"_open");t_closed_div=document.getElementById(b+"_closed");t_cookie=GetCookie("collapse_settings");if(1==g_collapse_clear){t_cookie="";g_collapse_clear=0}if(t_open_div.className=="hidden"){t_open_div.className="";t_closed_div.className="hidden";t_cookie=t_cookie+"|"+b+",1"}else{t_closed_div.className="";t_open_div.className="hidden";t_cookie=t_cookie+"|"+b+",0"}SetCookie("collapse_settings",t_cookie)}function checkall(p_formname,p_state){var t_elements=(eval("document."+p_formname+".elements"));for(var i=0;i<t_elements.length;i++){if(t_elements[i].type=="checkbox"){t_elements[i].checked=p_state}}}var a=navigator.userAgent.indexOf("MSIE");var style_display;if(a!=-1){style_display="block"}else{style_display="table-row"}style_display="block";function setDisplay(c,b){if(!document.getElementById(c)){alert("SetDisplay(): id "+c+" is empty")}if(b!=0){document.getElementById(c).style.display=style_display}else{document.getElementById(c).style.display="none"}}function toggleDisplay(b){setDisplay(b,(document.getElementById(b).style.display=="none")?1:0)}function tag_string_append(b){t_tag_separator=document.getElementById("tag_separator").value;t_tag_string=document.getElementById("tag_string");t_tag_select=document.getElementById("tag_select");if(Trim(b)==""){return}if(t_tag_string.value!=""){t_tag_string.value=t_tag_string.value+t_tag_separator+b}else{t_tag_string.value=t_tag_string.value+b}t_tag_select.selectedIndex=0}; diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 1a1606b..b947c42 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -54,6 +54,10 @@ $s_actiongroup_menu_attach_tags = 'Attach Tags'; $s_actiongroup_bugs = 'Selected Issues'; $s_actiongroup_error_issue_is_readonly = 'Issue is readonly.'; +# reply patch +$s_reply = 'Reply'; +$s_in_reply_to = 'In reply to %s from %s'; + # new strings: $s_all_projects = 'All Projects'; $s_move_bugs = 'Move Issues'; diff --git a/plugins/MantisCoreFormatting/MantisCoreFormatting.php b/plugins/MantisCoreFormatting/MantisCoreFormatting.php index fb3d27a..8307359 100644 --- a/plugins/MantisCoreFormatting/MantisCoreFormatting.php +++ b/plugins/MantisCoreFormatting/MantisCoreFormatting.php @@ -101,6 +101,7 @@ class MantisCoreFormattingPlugin extends MantisFormattingPlugin { $t_string = string_strip_hrefs( $t_string ); $t_string = string_html_specialchars( $t_string ); $t_string = string_restore_valid_html_tags( $t_string, /* multiline = */ true ); + $t_string = string_quoting( $t_string ); if( $p_multiline ) { $t_string = string_preserve_spaces_at_bol( $t_string ); | ||||
It might also be use to include the name and id of the note we are reply to at the top:
We can figure out the exact format. Although including "Reply to 0000775:0001234 by vboctor" in the note buddy is not a good idea, we can have a replyto field in the bug note that stores the original bug note id. Then when we are display the note, if this id is not 0, we display a line localized in the language of the user viewing the note, and then the buddy of the note. |
|
I think, a major advantage would be the possiblity to have the quoted text. I often experience the situation that in a bugnote there are several issues to respond to. In many cases I switch back to email communication, because quoting and answering subquestions is so easy there. But I would find it much better, if all discussion could be saved inside mantis. GUI suggestion: Pressing the button opens the bug_note_add page with the qouted text and a
|
|
I have a patch for this agains 1.1.8, which does the thing with change_status to request feedback. If anyone is interested, I can post it here. |
|
@polzin, please attach your patch. |
|
The patch shows the basic idea and works perfectly for me, but there is more to do, if you want to include it in mantis. I would be willing to spend more work in presenting the patch, if you consider to include it. |
|
reply.patch has problems, |
|
I have attached our version to quote notes. The patch was done with version 1.1.7. The localized strings must be added manually, the key is |
|
I uploaded a new patch (reply3.patch) that is a combination of the previous:
The patch is against current git master head. I also have a patch against 1.1 |
|
polzin, thanks for your patch. I've played around with it quickly and it does work. However I think what Victor was getting at before is that the hierarchical structure of bug notes should be stored as orthogonal fields in the database. This would have multiple advantages such as allowing you to nest bugnotes under each other in a threaded view mode. It also means that instead of storing usernames and so forth as text (remember that usernames can be changed) it is stored as an internal ID/reference to the user. |
|
A real solution with threaded view and usernames as ids is good. But I don't know, if it is about to come and when. So a suggestion could be to include the basic version of the patch right now, because I think it is a huge productivity boost, and add the other functionality later. It could be hacked using the same mechanism: Just add a hidden field to the "Add Note" form, the javascript fills this hidden field with the original bugnote id, and bugnote_view_inc renders "In reply to .... from ...." on top of the bugnote. |
|
So, any comment on including the easy enhancement right now and add the other functionality later? In daily practice I find it very helpful. |
|