From f314d1a472d13c68df5b5fc6cabc7ed3fd74c910 Mon Sep 17 00:00:00 2001 From: Ilogeek Date: Tue, 19 Sep 2017 11:39:51 +0200 Subject: [PATCH] partially fixed Github issue #12 : Read only attributes can't be edited anymore. Dynamic refresh for read only attributes when status changes --- README.md | 2 + assets/javascripts/issue_dynamic_edit.js | 348 +++++++++++------------ init.rb | 2 +- lib/details_issue_hooks.rb | 94 +++--- 4 files changed, 221 insertions(+), 225 deletions(-) diff --git a/README.md b/README.md index 102d6d4..5fc7f58 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ This plugin uses [FontAwesome icons](http://fontawesome.io/) ### 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.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 diff --git a/assets/javascripts/issue_dynamic_edit.js b/assets/javascripts/issue_dynamic_edit.js index c463d46..179e43f 100644 --- a/assets/javascripts/issue_dynamic_edit.js +++ b/assets/javascripts/issue_dynamic_edit.js @@ -18,63 +18,69 @@ $(document).on('click', function(e){ $(e.target).closest('.value').addClass('edited'); } }); - -/* Put new dropdown lists in the detailed info block */ -if($('#statusListDropdown').length > 0) { - var htmlCopy = $('#statusListDropdown').get(0).outerHTML; - $('#statusListDropdown').remove(); - $('.details .attributes .status.attribute .value').html( '' + - $('.details .attributes .status.attribute .value').html() + ' ' + - htmlCopy); -} - -if($('#usersListDropdown').length > 0) { - var htmlCopy = $('#usersListDropdown').get(0).outerHTML; - $('#usersListDropdown').remove(); - $('.details .attributes .assigned-to.attribute .value').html( '' + - $('.details .attributes .assigned-to.attribute .value').html() + ' ' + - htmlCopy); -} - -if($('#prioritiesListDropdown').length > 0) { - var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML; - $('#prioritiesListDropdown').remove(); - $('.details .attributes .priority.attribute .value').html( '' + - $('.details .attributes .priority.attribute .value').html() + ' ' + - htmlCopy); + +function initEditFields() +{ + /* Put new dropdown lists in the detailed info block */ + if($('#statusListDropdown').length > 0) { + var htmlCopy = $('#statusListDropdown').get(0).outerHTML; + $('#statusListDropdown').remove(); + $('.details .attributes .status.attribute .value').html( '' + + $('.details .attributes .status.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#usersListDropdown').length > 0) { + var htmlCopy = $('#usersListDropdown').get(0).outerHTML; + $('#usersListDropdown').remove(); + $('.details .attributes .assigned-to.attribute .value').html( '' + + $('.details .attributes .assigned-to.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#prioritiesListDropdown').length > 0) { + var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML; + $('#prioritiesListDropdown').remove(); + $('.details .attributes .priority.attribute .value').html( '' + + $('.details .attributes .priority.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#doneRatioListDropdown').length > 0) { + var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML; + $('#doneRatioListDropdown').remove(); + $('.details .attributes .progress.attribute .value').html('' + + $('.details .attributes .progress.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#EstimatedTimeInput').length > 0) { + var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML; + $('#EstimatedTimeInput').remove(); + $('.details .attributes .estimated-hours.attribute .value').html('' + + $('.details .attributes .estimated-hours.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#StartDateInput').length > 0) { + var htmlCopy = $('#StartDateInput').get(0).outerHTML; + $('#StartDateInput').remove(); + $('.details .attributes .start-date.attribute .value').html('' + + $('.details .attributes .start-date.attribute .value').html() + ' ' + + htmlCopy); + } + + if($('#DueDateInput').length > 0) { + var htmlCopy = $('#DueDateInput').get(0).outerHTML; + $('#DueDateInput').remove(); + $('.details .attributes .due-date.attribute .value').html('' + + $('.details .attributes .due-date.attribute .value').html() + ' ' + + htmlCopy); + } } -if($('#doneRatioListDropdown').length > 0) { - var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML; - $('#doneRatioListDropdown').remove(); - $('.details .attributes .progress.attribute .value').html('' + - $('.details .attributes .progress.attribute .value').html() + ' ' + - htmlCopy); -} +initEditFields(); -if($('#EstimatedTimeInput').length > 0) { - var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML; - $('#EstimatedTimeInput').remove(); - $('.details .attributes .estimated-hours.attribute .value').html('' + - $('.details .attributes .estimated-hours.attribute .value').html() + ' ' + - htmlCopy); -} - -if($('#StartDateInput').length > 0) { - var htmlCopy = $('#StartDateInput').get(0).outerHTML; - $('#StartDateInput').remove(); - $('.details .attributes .start-date.attribute .value').html('' + - $('.details .attributes .start-date.attribute .value').html() + ' ' + - htmlCopy); -} - -if($('#DueDateInput').length > 0) { - var htmlCopy = $('#DueDateInput').get(0).outerHTML; - $('#DueDateInput').remove(); - $('.details .attributes .due-date.attribute .value').html('' + - $('.details .attributes .due-date.attribute .value').html() + ' ' + - htmlCopy); -} $('body.controller-issues.action-show').on('click', '.btn.close', function(e){ e.preventDefault(); @@ -130,14 +136,12 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){ /* get result page content (updated issue detail page with new status) */ var parsed = $.parseHTML(msg); - var statusListDropdown = $(parsed).find("#statusListDropdown select"); - var prioritiesListDropdown = $(parsed).find('#prioritiesListDropdown select'); - /* we update dropdown status with new one from updated page */ - $('#statusListDropdown select').html(statusListDropdown.html()); - $('#issue_status_id').html(statusListDropdown.html()); - $('#prioritiesListDropdown select').html(prioritiesListDropdown.html()); - $('#issue_priority_id').html(prioritiesListDropdown.html()); - + /* we update the details block */ + $('div.issue.details').html($(parsed).find('div.issue.details').html()); + /* we init edit fields */ + initEditFields(); + initEditFieldListeners(); + /* we update issue properties edit block */ $('#all_attributes').html($(parsed).find('#all_attributes').html()); @@ -151,35 +155,11 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){ $('.details .attributes .' + cssClass + '.attribute i.fa-check.statusOk').remove(); }, 2000); }, 500); - - if(type == "progress") { // specific case for progress bar, we need to update the progress bar view - var progressBar = ""; - var percentTodo = 100 - parseInt(field_value); - if(field_value != 0) { progressBar += ""; } - if(percentTodo != 0) { progressBar += ""; } - progressBar += ""; - $('.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 $('#issue_lock_version').val(parseInt($('#issue_lock_version').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 and no update, info logged into console */ @@ -200,100 +180,104 @@ function issueDynamicUpdate(field_name, field_value, type, cssClass){ }; /* Listeners foreach attribute */ - - var domSelectStatus = $('body').find('#statusListDropdown select'); - domSelectStatus.on('change', function(e){ - issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status'); +function initEditFieldListeners() +{ + var domSelectStatus = $('body').find('#statusListDropdown select'); + domSelectStatus.on('change', function(e){ + issueDynamicUpdate('status_id', domSelectStatus.val(), 'select', 'status'); - /* update the classes status from */ - $("#content > div.issue").removeClass(function (index, className) { - return (className.match (/(^|\s)status-\S+/g) || []).join(' '); - }).addClass('status-' + domSelectStatus.val()); - }); /* end on change domSelectStatus */ - - var domSelectPriorities = $('body').find('#prioritiesListDropdown select'); - domSelectPriorities.on('change', function(e){ - issueDynamicUpdate('priority_id', domSelectPriorities.val(), 'select', 'priority'); + /* update the classes status from */ + $("#content > div.issue").removeClass(function (index, className) { + return (className.match (/(^|\s)status-\S+/g) || []).join(' '); + }).addClass('status-' + domSelectStatus.val()); + }); /* end on change domSelectStatus */ + + var domSelectPriorities = $('body').find('#prioritiesListDropdown select'); + domSelectPriorities.on('change', function(e){ + issueDynamicUpdate('priority_id', domSelectPriorities.val(), 'select', 'priority'); - /* update the classes priority from */ - $("#content > div.issue").removeClass(function (index, className) { - return (className.match (/(^|\s)priority-\S+/g) || []).join(' '); - }).addClass('priority-' + domSelectStatus.val()); - }); /* end on change domSelectPriorities */ - - var domSelectUsers = $('body').find('#usersListDropdown select'); - domSelectUsers.on('change', function(e){ - issueDynamicUpdate('assigned_to_id', domSelectUsers.val(), 'select', 'assigned-to'); - }); /* end on change domSelectUsers */ - - var domSelectRatio = $('body').find('#doneRatioListDropdown select'); - domSelectRatio.on('change', function(e){ - issueDynamicUpdate('done_ratio', domSelectRatio.val(), 'progress', 'progress'); - }); /* end on change domSelectRatio */ - - var domInputEstimatedTime = $('body').find('#EstimatedTimeInput input'); - $('#EstimatedTimeInput a.btn.validate').on('click', function(e) - { - e.preventDefault(); - $('.estimated-hours .value .error').remove(); - var estimatedTime = parseFloat(domInputEstimatedTime.val()); - if(estimatedTime >= 0) - { - issueDynamicUpdate('estimated_hours', estimatedTime, 'input', 'estimated-hours'); - } else { - /* estimated time must be > 0 */ - $('.estimated-hours .value').append(' ' + _TXT_ERROR_POSITIVE_NUMBER + ''); - } - return false; - }); - - domInputEstimatedTime.on('keyup', function(e){ - $('.details .attributes .estimated-hours.attribute .selectedValue span').html( - $('.details .attributes .estimated-hours.attribute .value input').val() - ); - if (e.keyCode == 13) { - $('#EstimatedTimeInput a.btn.validate').click(); - } - });/* end EstimatedTime */ - - var domInputStartDate = $('body').find('#StartDateInput input'); - $('#StartDateInput a.btn.validate').on('click', function(e) - { - e.preventDefault(); - $('.start-date .value .error').remove(); - if(new Date(domInputStartDate.val()).getTime() <= new Date($('body').find('#DueDateInput input').val()).getTime()) - { - issueDynamicUpdate('start_date', domInputStartDate.val(), 'date', 'start-date'); - } else { - /* start date must be < due date */ - $('.start-date .value').append(' ' + _TXT_ERROR_START_DATE + ''); - } - return false; - }); - - domInputStartDate.on('keyup', function(e){ - if (e.keyCode == 13) { - $('#StartDateInput a.btn.validate').click(); - } - });/* end StartDate */ - - var domInputDueDate = $('body').find('#DueDateInput input'); - $('#DueDateInput a.btn.validate').on('click', function(e) - { - e.preventDefault(); - $('.due-date .value .error').remove(); - if(new Date($('body').find('#StartDateInput input').val()).getTime() <= new Date(domInputDueDate.val()).getTime()) - { - issueDynamicUpdate('due_date', domInputDueDate.val(), 'date', 'due-date'); - } else { - /* start date must be < due date */ - $('.due-date .value').append(' ' + _TXT_ERROR_DUE_DATE + ''); - } - return false; - }); - - domInputDueDate.on('keyup', function(e){ - if (e.keyCode == 13) { - $('#DueDateInput a.btn.validate').click(); - } - });/* end StartDate */ \ No newline at end of file + /* update the classes priority from */ + $("#content > div.issue").removeClass(function (index, className) { + return (className.match (/(^|\s)priority-\S+/g) || []).join(' '); + }).addClass('priority-' + domSelectStatus.val()); + }); /* end on change domSelectPriorities */ + + var domSelectUsers = $('body').find('#usersListDropdown select'); + domSelectUsers.on('change', function(e){ + issueDynamicUpdate('assigned_to_id', domSelectUsers.val(), 'select', 'assigned-to'); + }); /* end on change domSelectUsers */ + + var domSelectRatio = $('body').find('#doneRatioListDropdown select'); + domSelectRatio.on('change', function(e){ + issueDynamicUpdate('done_ratio', domSelectRatio.val(), 'progress', 'progress'); + }); /* end on change domSelectRatio */ + + var domInputEstimatedTime = $('body').find('#EstimatedTimeInput input'); + $('#EstimatedTimeInput a.btn.validate').on('click', function(e) + { + e.preventDefault(); + $('.estimated-hours .value .error').remove(); + var estimatedTime = parseFloat(domInputEstimatedTime.val()); + if(estimatedTime >= 0) + { + issueDynamicUpdate('estimated_hours', estimatedTime, 'input', 'estimated-hours'); + } else { + /* estimated time must be > 0 */ + $('.estimated-hours .value').append(' ' + _TXT_ERROR_POSITIVE_NUMBER + ''); + } + return false; + }); + + domInputEstimatedTime.on('keyup', function(e){ + $('.details .attributes .estimated-hours.attribute .selectedValue span').html( + $('.details .attributes .estimated-hours.attribute .value input').val() + ); + if (e.keyCode == 13) { + $('#EstimatedTimeInput a.btn.validate').click(); + } + });/* end EstimatedTime */ + + var domInputStartDate = $('body').find('#StartDateInput input'); + $('#StartDateInput a.btn.validate').on('click', function(e) + { + e.preventDefault(); + $('.start-date .value .error').remove(); + if(new Date(domInputStartDate.val()).getTime() <= new Date($('body').find('#DueDateInput input').val()).getTime()) + { + issueDynamicUpdate('start_date', domInputStartDate.val(), 'date', 'start-date'); + } else { + /* start date must be < due date */ + $('.start-date .value').append(' ' + _TXT_ERROR_START_DATE + ''); + } + return false; + }); + + domInputStartDate.on('keyup', function(e){ + if (e.keyCode == 13) { + $('#StartDateInput a.btn.validate').click(); + } + });/* end StartDate */ + + var domInputDueDate = $('body').find('#DueDateInput input'); + $('#DueDateInput a.btn.validate').on('click', function(e) + { + e.preventDefault(); + $('.due-date .value .error').remove(); + if(new Date($('body').find('#StartDateInput input').val()).getTime() <= new Date(domInputDueDate.val()).getTime()) + { + issueDynamicUpdate('due_date', domInputDueDate.val(), 'date', 'due-date'); + } else { + /* start date must be < due date */ + $('.due-date .value').append(' ' + _TXT_ERROR_DUE_DATE + ''); + } + return false; + }); + + domInputDueDate.on('keyup', function(e){ + if (e.keyCode == 13) { + $('#DueDateInput a.btn.validate').click(); + } + });/* end StartDate */ +} + +initEditFieldListeners(); \ No newline at end of file diff --git a/init.rb b/init.rb index ea148ff..e4d898c 100644 --- a/init.rb +++ b/init.rb @@ -6,7 +6,7 @@ Redmine::Plugin.register :redmine_issue_dynamic_edit do name 'Redmine Dynamic edit Issue plugin' author 'Hugo Zilliox' 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' author_url 'https://hzilliox.fr' end diff --git a/lib/details_issue_hooks.rb b/lib/details_issue_hooks.rb index 61891ac..5ae3b48 100644 --- a/lib/details_issue_hooks.rb +++ b/lib/details_issue_hooks.rb @@ -22,6 +22,9 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener if (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 (User.current.allowed_to?(:edit_issues, project)) @@ -30,7 +33,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener # Status dropdown statuses = issue.new_statuses_allowed_to(User.current) - if (!statuses.empty?) + if (!statuses.empty? && !(readOnlyAttributes.include? 'status_id')) o << "" o << "" assignables.each do |u| @@ -61,7 +63,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener # Priorities dropdown priorities = IssuePriority.all - if(!priorities.empty?) + if(!priorities.empty? && !(readOnlyAttributes.include? 'priority_id')) o << "" o << "" - loop do - if (percent == issue.done_ratio) - o << "" - else - o << "" + if(!(readOnlyAttributes.include? 'done_ratio')) + percent = 0 + o << "" + o << " " end - o << " " # Estimated_time dropdown - o << "" - o << " " - o << "" - o << " " - o << "" + if(!(readOnlyAttributes.include? 'estimated_hours')) + o << "" + o << " " + o << "" + o << " " + o << "" + end # Start date - o << "" - o << " " - o << " " - o << " " - o << "" - o << "" + if(!(readOnlyAttributes.include? 'start_date')) + o << "" + o << " " + o << " " + o << " " + o << "" + o << "" + end # Due date - o << "" - o << " " - o << " " - o << " " - o << "" - o << "" + if(!(readOnlyAttributes.include? 'due_date')) + o << "" + o << " " + o << " " + o << " " + o << "" + o << "" + end end end