partially fixed Github issue #12 : Read only attributes can't be edited anymore. Dynamic refresh for read only attributes when status changes

This commit is contained in:
Ilogeek 2017-09-19 11:39:51 +02:00
parent 7f5d6e6858
commit f314d1a472
4 changed files with 221 additions and 225 deletions

View File

@ -18,6 +18,8 @@ This plugin uses [FontAwesome icons](http://fontawesome.io/)
### Changelog ### Changelog
* **v 0.4.3** : partially fixed Github issue #12 : Read only attributes can't be edited anymore. Dynamic refresh for read only attributes when status changes
* **v 0.4.2** : fixed Github issue #10 : History list updated after modification
* **v 0.4.1** : fixed Github issue #7 : update status list to follow Redmine workflow * **v 0.4.1** : fixed Github issue #7 : update status list to follow Redmine workflow
* **v 0.4.0** : fixed Github issues #2, #4, #9. Edited dropdown display * **v 0.4.0** : fixed Github issues #2, #4, #9. Edited dropdown display
* **v 0.3.0** : start date, due date, ratio and estimated time fields are now dynamically editable. Translation files added (en, fr). Log added in console when AJAX fails * **v 0.3.0** : start date, due date, ratio and estimated time fields are now dynamically editable. Translation files added (en, fr). Log added in console when AJAX fails

View File

@ -19,6 +19,8 @@ $(document).on('click', function(e){
} }
}); });
function initEditFields()
{
/* Put new dropdown lists in the detailed info block */ /* Put new dropdown lists in the detailed info block */
if($('#statusListDropdown').length > 0) { if($('#statusListDropdown').length > 0) {
var htmlCopy = $('#statusListDropdown').get(0).outerHTML; var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
@ -75,6 +77,10 @@ if($('#DueDateInput').length > 0) {
$('.details .attributes .due-date.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' + $('.details .attributes .due-date.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
htmlCopy); htmlCopy);
} }
}
initEditFields();
$('body.controller-issues.action-show').on('click', '.btn.close', function(e){ $('body.controller-issues.action-show').on('click', '.btn.close', function(e){
e.preventDefault(); e.preventDefault();
@ -130,13 +136,11 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
/* get result page content (updated issue detail page with new status) */ /* get result page content (updated issue detail page with new status) */
var parsed = $.parseHTML(msg); var parsed = $.parseHTML(msg);
var statusListDropdown = $(parsed).find("#statusListDropdown select"); /* we update the details block */
var prioritiesListDropdown = $(parsed).find('#prioritiesListDropdown select'); $('div.issue.details').html($(parsed).find('div.issue.details').html());
/* we update dropdown status with new one from updated page */ /* we init edit fields */
$('#statusListDropdown select').html(statusListDropdown.html()); initEditFields();
$('#issue_status_id').html(statusListDropdown.html()); initEditFieldListeners();
$('#prioritiesListDropdown select').html(prioritiesListDropdown.html());
$('#issue_priority_id').html(prioritiesListDropdown.html());
/* we update issue properties edit block */ /* we update issue properties edit block */
$('#all_attributes').html($(parsed).find('#all_attributes').html()); $('#all_attributes').html($(parsed).find('#all_attributes').html());
@ -152,34 +156,10 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
}, 2000); }, 2000);
}, 500); }, 500);
if(type == "progress") { // specific case for progress bar, we need to update the progress bar view
var progressBar = "<tbody><tr>";
var percentTodo = 100 - parseInt(field_value);
if(field_value != 0) { progressBar += "<td style='width: " + field_value + "%;' class='closed' title='" + field_value + "%'></td>"; }
if(percentTodo != 0) { progressBar += "<td style='width: " + percentTodo + "%;' class='todo'></td>"; }
progressBar += "</tr></tbody>";
$('.details .attributes .' + cssClass + '.attribute table.progress').attr('class', 'progress progress-' + field_value).html(progressBar);
$('.details .attributes .' + cssClass + ' .percent').html(field_value + "%");
} else if( type == "date") { // specific case for start date and due date, we have to update min and max date allowed
if(field_name == "start_date")
{
$('body').find('#DueDateInput input').attr('min', field_value);
} else if (field_name == "due_date")
{
$('body').find('#StartDateInput input').attr('max', field_value);
}
}
// update other fields to avoid conflict // update other fields to avoid conflict
$('#issue_lock_version').val(parseInt($('#issue_lock_version').val()) + 1 ); $('#issue_lock_version').val(parseInt($('#issue_lock_version').val()) + 1 );
$('#last_journal_id').val(parseInt($('#last_journal_id').val()) + 1 ); $('#last_journal_id').val(parseInt($('#last_journal_id').val()) + 1 );
if(type == "select")
{
$('#issue_' + field_name + ' option').removeAttr('selected').filter('[value=' + field_value + ']').prop('selected', true);
} else if (type == "input" || type == "date")
{
$('#issue_' + field_name).val(field_value);
}
}, },
error: function(xhr, msg, error) { error: function(xhr, msg, error) {
/* error and no update, info logged into console */ /* error and no update, info logged into console */
@ -200,7 +180,8 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
}; };
/* Listeners foreach attribute */ /* Listeners foreach attribute */
function initEditFieldListeners()
{
var domSelectStatus = $('body').find('#statusListDropdown select'); var domSelectStatus = $('body').find('#statusListDropdown select');
domSelectStatus.on('change', function(e){ domSelectStatus.on('change', function(e){
issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status'); issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status');
@ -297,3 +278,6 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){
$('#DueDateInput a.btn.validate').click(); $('#DueDateInput a.btn.validate').click();
} }
});/* end StartDate */ });/* end StartDate */
}
initEditFieldListeners();

View File

@ -6,7 +6,7 @@ Redmine::Plugin.register :redmine_issue_dynamic_edit do
name 'Redmine Dynamic edit Issue plugin' name 'Redmine Dynamic edit Issue plugin'
author 'Hugo Zilliox' author 'Hugo Zilliox'
description 'Allows users to dynamically update issue attributes in detailed view' description 'Allows users to dynamically update issue attributes in detailed view'
version '0.4.2' version '0.4.3'
url 'https://github.com/ilogeek/redmine_issue_dynamic_edit' url 'https://github.com/ilogeek/redmine_issue_dynamic_edit'
author_url 'https://hzilliox.fr' author_url 'https://hzilliox.fr'
end end

View File

@ -22,6 +22,9 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
if (issue_id) if (issue_id)
issue = Issue.find(issue_id) issue = Issue.find(issue_id)
readOnlyAttributes = issue.read_only_attribute_names(User.current)
# o << issue.required_attribute_names(User.current).to_json
if (issue) if (issue)
if (User.current.allowed_to?(:edit_issues, project)) if (User.current.allowed_to?(:edit_issues, project))
@ -30,7 +33,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
# Status dropdown # Status dropdown
statuses = issue.new_statuses_allowed_to(User.current) statuses = issue.new_statuses_allowed_to(User.current)
if (!statuses.empty?) if (!statuses.empty? && !(readOnlyAttributes.include? 'status_id'))
o << "<span class='dynamicEdit' id='statusListDropdown'>" o << "<span class='dynamicEdit' id='statusListDropdown'>"
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>" o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
statuses.each do |s| statuses.each do |s|
@ -45,8 +48,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
# Users dropdown # Users dropdown
assignables = project.assignable_users assignables = project.assignable_users
o << assignables.to_json if (!assignables.empty? && !(readOnlyAttributes.include? 'assigned_to_id'))
if (!assignables.empty?)
o << "<span class='dynamicEdit' id='usersListDropdown'>" o << "<span class='dynamicEdit' id='usersListDropdown'>"
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>" o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
assignables.each do |u| assignables.each do |u|
@ -61,7 +63,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
# Priorities dropdown # Priorities dropdown
priorities = IssuePriority.all priorities = IssuePriority.all
if(!priorities.empty?) if(!priorities.empty? && !(readOnlyAttributes.include? 'priority_id'))
o << "<span class='dynamicEdit' id='prioritiesListDropdown'>" o << "<span class='dynamicEdit' id='prioritiesListDropdown'>"
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>" o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
priorities.each do |p| priorities.each do |p|
@ -75,6 +77,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
end end
# %done dropdown # %done dropdown
if(!(readOnlyAttributes.include? 'done_ratio'))
percent = 0 percent = 0
o << "<span class='dynamicEdit' id='doneRatioListDropdown'>" o << "<span class='dynamicEdit' id='doneRatioListDropdown'>"
o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>" o << "<select data-issue='#{issue_id}'><option disabled='disabled' selected> </option>"
@ -90,15 +93,19 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
end end
end end
o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>" o << "</select> <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a></span>"
end
# Estimated_time dropdown # Estimated_time dropdown
if(!(readOnlyAttributes.include? 'estimated_hours'))
o << "<span class='dynamicEdit' id='EstimatedTimeInput'>" o << "<span class='dynamicEdit' id='EstimatedTimeInput'>"
o << " <input type='text' value='#{issue.estimated_hours}' size='6'/>" o << " <input type='text' value='#{issue.estimated_hours}' size='6'/>"
o << "<a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>" o << "<a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
o << " <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>" o << " <a href='#' class='btn btn-primary close' aria-label='" + l(:ide_txt_cancel_btn) + "'><i class='fa fa-times fa-fw' aria-hidden='true'></i></a>"
o << "</span>" o << "</span>"
end
# Start date # Start date
if(!(readOnlyAttributes.include? 'start_date'))
o << "<span class='dynamicEdit' id='StartDateInput'>" o << "<span class='dynamicEdit' id='StartDateInput'>"
o << " <input size=\"10\" value=\"#{issue.start_date}\" type=\"date\" max=\"#{issue.due_date}\">" o << " <input size=\"10\" value=\"#{issue.start_date}\" type=\"date\" max=\"#{issue.due_date}\">"
o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>" o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
@ -109,8 +116,10 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
o << " $(function() { $('#StartDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n" o << " $(function() { $('#StartDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
o << "//]]>\n" o << "//]]>\n"
o << "</script>" o << "</script>"
end
# Due date # Due date
if(!(readOnlyAttributes.include? 'due_date'))
o << "<span class='dynamicEdit' id='DueDateInput'>" o << "<span class='dynamicEdit' id='DueDateInput'>"
o << " <input size=\"10\" value=\"#{issue.due_date}\" type=\"date\" min=\"#{issue.start_date}\">" o << " <input size=\"10\" value=\"#{issue.due_date}\" type=\"date\" min=\"#{issue.start_date}\">"
o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>" o << " <a href='#' class='btn btn-primary validate' aria-label='" + l(:ide_txt_validation_btn) + "'><i class='fa fa-check fa-fw' aria-hidden='true'></i></a>"
@ -121,6 +130,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
o << " $(function() { $('#DueDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n" o << " $(function() { $('#DueDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
o << "//]]>\n" o << "//]]>\n"
o << "</script>" o << "</script>"
end
end end
end end