Merge branch 'master' of https://github.com/Ilogeek/redmine_issue_dynamic_edit
This commit is contained in:
commit
e9426eac99
26
README.md
26
README.md
@ -1,32 +1,44 @@
|
||||
# redmine_issue_dynamic_edit
|
||||
# ✨ redmine_issue_dynamic_edit
|
||||
|
||||
Add new elements on detailed issue page to **dynamically update issue's attributes and custom fields**, directly in the details block of the issue **without any page refresh** (*JIRA style*).
|
||||
|
||||
|
||||
### 🔴 What info you should provide when opening an issue
|
||||
>Please list your installed plugins and the Redmine version you use. Note that I can't fix every issue when you have conflict with an other plugin that also edit the page.
|
||||
>
|
||||
>This plugin use JS a lot. Check your JS console from your web browser ( [HowTo](https://webmasters.stackexchange.com/a/77337) ) and try again to reproduce your issue. You'll see some information about what goes wrong.
|
||||
>
|
||||
>Copy and paste the result that appears in your console in the Github issue and expand all possible object (error data for example). With this data, we can look if there's a problem with the ajax call the plugin performs to update the issue or if there's any JS error.
|
||||
|
||||
### Example
|
||||
### 🔎 Example
|
||||
|
||||

|
||||
|
||||
### Installation
|
||||
### 📦 Installation
|
||||
|
||||
* Clone repo into plugins directory : `git clone https://github.com/Ilogeek/redmine_issue_dynamic_edit.git` (be sure that the parent folder is called `redmine_issue_dynamic_edit`)
|
||||
* Restart your Redmine instance
|
||||
|
||||
### Customization
|
||||
### ⚙ Configuration (new since v 0.6.6)
|
||||
|
||||
You can set some settings by editing the file `assets/javascripts/issue_dynamic_edit_configuration_file.js`. Inside this file you'll find different variable :
|
||||
* **\_CONF\_FORCE\_HTTPS** : Will force AJAX call performed by the plugin to be done with https protocol. Use this value if you encounter some difficulties with "Mixed content" issues
|
||||
* **\_CONF\_LISTENER\_TYPE** : Choose which action will trigger the apparition of the edition block
|
||||
* **\_CONF\_LISTENER\_TARGET** : Choose which area will trigger the apparition of the edition block
|
||||
* **\_CONF\_EXCLUDED\_FIELD\_ID** : Choose which fields to exclude. They won't have the edit block and pencil
|
||||
|
||||
### 🎨 Customization
|
||||
|
||||
Feel free to edit `assets/stylesheets/issue_dynamic_edit.css` to update the look of your fields depending on your current Redmine Theme.
|
||||
|
||||
This plugin uses [FontAwesome icons](http://fontawesome.io/)
|
||||
|
||||
### Changelog
|
||||
### 🆕 Changelog
|
||||
|
||||
* **v.0.6.5** : Checklists plugin support (and all other plugins that compute fields when there's an issue update) (Github requests #26 and #28) + custom url support (Github request #29)
|
||||
* **v.0.6.4** : version field with checkbox display is now supported, Target version and Assignee fields are also supported (Github request #24)
|
||||
* **v 0.6.7** : fixed Github issue #46 : text field focus issue
|
||||
* **v 0.6.6** : New configuration file + Multiple fixes (#30 #31 #35 #36 #37 #38 #41)
|
||||
* **v 0.6.5** : Checklists plugin support (and all other plugins that compute fields when there's an issue update) (Github requests #26 and #28) + custom url support (Github request #29)
|
||||
* **v 0.6.4** : version field with checkbox display is now supported, Target version and Assignee fields are also supported (Github request #24)
|
||||
* **v 0.6.3** : fixed Github issue #22 : DatepickerFallback raised an error
|
||||
* **v 0.6.2** : fixed Github issue #22 : long description is now supported (no more 414 errors)
|
||||
* **v 0.6.1** : fixed Github issue #20
|
||||
|
||||
@ -1,5 +1,17 @@
|
||||
/* Allow inclusion from other page */
|
||||
/*
|
||||
* OPTIONS DEFINED FROM CONFIGURATION FILE
|
||||
*/
|
||||
var _CONF_FORCE_HTTPS = _CONF_FORCE_HTTPS || false;
|
||||
var _CONF_LISTENER_TYPE = _CONF_LISTENER_TYPE || "click";
|
||||
var _CONF_LISTENER_TARGET = _CONF_LISTENER_TARGET || "value";
|
||||
var _CONF_EXCLUDED_FIELD_ID = _CONF_EXCLUDED_FIELD_ID || [];
|
||||
|
||||
/*
|
||||
* Allow inclusion from other page
|
||||
* See https://github.com/Ilogeek/redmine_issue_dynamic_edit/commit/26684a2dd9b12dcc7377afd79e9fe5c142d26ebd for more info
|
||||
*/
|
||||
var LOCATION_HREF = typeof custom_location_href !== 'undefined' ? custom_location_href : window.location.href;
|
||||
if(_CONF_FORCE_HTTPS) { LOCATION_HREF = LOCATION_HREF.replace(/^http:\/\//i, 'https://'); }
|
||||
|
||||
/* FontAwesome inclusion */
|
||||
var cssId = 'fontAwesome';
|
||||
@ -15,17 +27,35 @@ var cssId = 'fontAwesome';
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
$(document).on('click', function(e){
|
||||
$(document).on(_CONF_LISTENER_TYPE, function(e){
|
||||
$('.issue .attributes .attribute .value').removeClass('edited');
|
||||
if($(e.target).closest('.value').length) {
|
||||
if($(e.target).closest('a').length){ return; }
|
||||
if($(e.target).closest('.' + _CONF_LISTENER_TARGET).length) {
|
||||
// avoid text selection if dblclick
|
||||
var sel = window.getSelection ? window.getSelection() : document.selection;
|
||||
var activeElement = document.activeElement;
|
||||
var inputs = ['input', 'select', 'button', 'textarea'];
|
||||
|
||||
if (sel && inputs.indexOf(activeElement.tagName.toLowerCase()) === -1) {
|
||||
if (sel.removeAllRanges) {
|
||||
sel.removeAllRanges();
|
||||
} else if (sel.empty) {
|
||||
sel.empty();
|
||||
}
|
||||
}
|
||||
// we show the edit box
|
||||
$(e.target).closest('.value').addClass('edited');
|
||||
}
|
||||
});
|
||||
|
||||
function isExcluded(elmt_id) {
|
||||
return _CONF_EXCLUDED_FIELD_ID.indexOf(elmt_id) > -1;
|
||||
}
|
||||
|
||||
function initEditFields()
|
||||
{
|
||||
/* Put new dropdown lists in the detailed info block */
|
||||
if($('#statusListDropdown').length > 0) {
|
||||
if($('#statusListDropdown').length > 0 && !isExcluded('statusListDropdown')) {
|
||||
var htmlCopy = $('#statusListDropdown').get(0).outerHTML;
|
||||
$('#statusListDropdown').remove();
|
||||
$('.details .attributes .status.attribute .value').html( '<span class="showValue">' +
|
||||
@ -33,15 +63,7 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#usersListDropdown').length > 0) {
|
||||
var htmlCopy = $('#usersListDropdown').get(0).outerHTML;
|
||||
$('#usersListDropdown').remove();
|
||||
$('.details .attributes .assigned-to.attribute .value').html( '<span class="showValue">' +
|
||||
$('.details .attributes .assigned-to.attribute .value').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#prioritiesListDropdown').length > 0) {
|
||||
if($('#prioritiesListDropdown').length > 0 && !isExcluded('prioritiesListDropdown')) {
|
||||
var htmlCopy = $('#prioritiesListDropdown').get(0).outerHTML;
|
||||
$('#prioritiesListDropdown').remove();
|
||||
$('.details .attributes .priority.attribute .value').html( '<span class="showValue">' +
|
||||
@ -49,7 +71,7 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#doneRatioListDropdown').length > 0) {
|
||||
if($('#doneRatioListDropdown').length > 0 && !isExcluded('doneRatioListDropdown')) {
|
||||
var htmlCopy = $('#doneRatioListDropdown').get(0).outerHTML;
|
||||
$('#doneRatioListDropdown').remove();
|
||||
$('.details .attributes .progress.attribute .value').html('<span class="showValue">' +
|
||||
@ -57,7 +79,7 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#EstimatedTimeInput').length > 0) {
|
||||
if($('#EstimatedTimeInput').length > 0 && !isExcluded('EstimatedTimeInput')) {
|
||||
var htmlCopy = $('#EstimatedTimeInput').get(0).outerHTML;
|
||||
$('#EstimatedTimeInput').remove();
|
||||
$('.details .attributes .estimated-hours.attribute .value').html('<span class="showValue">' +
|
||||
@ -65,7 +87,7 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#StartDateInput').length > 0) {
|
||||
if($('#StartDateInput').length > 0 && !isExcluded('StartDateInput')) {
|
||||
var htmlCopy = $('#StartDateInput').get(0).outerHTML;
|
||||
$('#StartDateInput').remove();
|
||||
$('.details .attributes .start-date.attribute .value').html('<span class="showValue">' +
|
||||
@ -73,7 +95,7 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#DueDateInput').length > 0) {
|
||||
if($('#DueDateInput').length > 0 && !isExcluded('DueDateInput')) {
|
||||
var htmlCopy = $('#DueDateInput').get(0).outerHTML;
|
||||
$('#DueDateInput').remove();
|
||||
$('.details .attributes .due-date.attribute .value').html('<span class="showValue">' +
|
||||
@ -81,21 +103,21 @@ function initEditFields()
|
||||
htmlCopy);
|
||||
}
|
||||
|
||||
if($('#TitleInput').length > 0) {
|
||||
if($('#TitleInput').length > 0 && !isExcluded('TitleInput')) {
|
||||
var htmlCopy = $('#TitleInput').get(0).outerHTML;
|
||||
$('#TitleInput').remove();
|
||||
$('.subject h3').html('<span class="showValue">' + $('.subject h3').html() + '</span> <i class="fa fa-pencil fa-fw" aria-hidden="true"></i>' +
|
||||
htmlCopy).addClass('value');
|
||||
}
|
||||
|
||||
if($('#DescriptionInput').length > 0) {
|
||||
if($('#DescriptionInput').length > 0 && !isExcluded('DescriptionInput')) {
|
||||
var htmlCopy = $('#DescriptionInput').get(0).outerHTML;
|
||||
$('#DescriptionInput').remove();
|
||||
$('div.description .wiki').html(' <i class="fa fa-pencil fa-fw" aria-hidden="true" style="float:right;"></i><span class="showValue">' + $('div.description .wiki').html() + '</span>' +
|
||||
htmlCopy).addClass('value');
|
||||
}
|
||||
|
||||
if($('select#issue_assigned_to_id').length > 0)
|
||||
if($('select#issue_assigned_to_id').length > 0 && !isExcluded('issue_assigned_to_id'))
|
||||
{
|
||||
var htmlCopy = $('select#issue_assigned_to_id').get(0).outerHTML;
|
||||
// 2 technics with simple or double quote (safety first)
|
||||
@ -111,7 +133,7 @@ function initEditFields()
|
||||
editHTML);
|
||||
}
|
||||
|
||||
if($('select#issue_fixed_version_id').length > 0)
|
||||
if($('select#issue_fixed_version_id').length > 0 && !isExcluded('issue_fixed_version_id'))
|
||||
{
|
||||
var htmlCopy = $('select#issue_fixed_version_id').get(0).outerHTML;
|
||||
// 2 technics with simple or double quote (safety first)
|
||||
@ -131,7 +153,7 @@ function initEditFields()
|
||||
{
|
||||
var info = CF_VALUE_JSON[i].custom_field;
|
||||
var value = CF_VALUE_JSON[i].value;
|
||||
if(info.visible && info.editable)
|
||||
if(info.visible && info.editable && !isExcluded("issue_custom_field_values_" + info.id))
|
||||
{
|
||||
if($('.details .attributes .cf_' + info.id + '.attribute .value').length
|
||||
&& $('#issue_custom_field_values_' + info.id).length )
|
||||
@ -378,7 +400,7 @@ function initEditFieldListeners()
|
||||
/* update the classes priority from */
|
||||
$("#content > div.issue").removeClass(function (index, className) {
|
||||
return (className.match (/(^|\s)priority-\S+/g) || []).join(' ');
|
||||
}).addClass('priority-' + domSelectStatus.val());
|
||||
}).addClass('priority-' + domSelectPriorities.val());
|
||||
}); /* end on change domSelectPriorities */
|
||||
|
||||
var domSelectUsers = $('body').find('#usersListDropdown select');
|
||||
|
||||
35
assets/javascripts/issue_dynamic_edit_configuration_file.js
Normal file
35
assets/javascripts/issue_dynamic_edit_configuration_file.js
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* CONFIGURATION FILE
|
||||
* More info on https://github.com/Ilogeek/redmine_issue_dynamic_edit
|
||||
*/
|
||||
|
||||
/*
|
||||
* _CONF_FORCE_HTTPS (boolean)
|
||||
* Will force AJAX call performed by the plugin to be done with https protocol
|
||||
* Use this value if you encounter some difficulties with "Mixed content" issues
|
||||
* Allowed values : false (default), true
|
||||
*/
|
||||
var _CONF_FORCE_HTTPS = false;
|
||||
|
||||
/*
|
||||
* _CONF_LISTENER_TYPE (string)
|
||||
* Choose which action will trigger the apparition of the edition block
|
||||
* Allowed values : click (default), dblclick
|
||||
*/
|
||||
var _CONF_LISTENER_TYPE = "click";
|
||||
|
||||
/*
|
||||
* _CONF_LISTENER_TARGET (string)
|
||||
* Choose which area will trigger the apparition of the edition block
|
||||
* "value" will target the whole line, "fa-pencil" will only target the pencil icon
|
||||
* Allowed values : value (default), fa-pencil
|
||||
*/
|
||||
var _CONF_LISTENER_TARGET = "value";
|
||||
|
||||
/*
|
||||
* _CONF_EXCLUDED_FIELD_ID (string array)
|
||||
* Choose which fields to exclude. They won't have the edit block and pencil
|
||||
* Custom fields have an unique ID and this ID must be prefixed by "issue_custom_field_values_". Eg : "issue_custom_field_values_4" is an allowed value
|
||||
* Allowed values : array of any ID selector (css). Eg : ["statusListDropdown", "StartDateInput", "TitleInput", "issue_custom_field_values_4"]
|
||||
*/
|
||||
var _CONF_EXCLUDED_FIELD_ID = [];
|
||||
2
init.rb
2
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 without refreshing the page (JIRA style)'
|
||||
version '0.6.5'
|
||||
version '0.6.7'
|
||||
url 'https://github.com/ilogeek/redmine_issue_dynamic_edit'
|
||||
author_url 'https://hzilliox.fr'
|
||||
end
|
||||
|
||||
@ -5,7 +5,8 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
||||
end
|
||||
|
||||
def current_is_detail_page(context)
|
||||
ret = context[:controller] && context[:controller].is_a?(IssuesController) && context[:request].original_url.rindex(/\/issues\/\S+/)
|
||||
# check if we see an issue but not creating a new one or on the specific edit page
|
||||
ret = context[:controller] && context[:controller].is_a?(IssuesController) && context[:request].original_url.rindex(/\/issues\/\S+/) && !context[:request].original_url.rindex(/\/issues\/new/) && !context[:request].original_url.rindex(/\/issues\/\d+\/edit/)
|
||||
end
|
||||
|
||||
def view_layouts_base_html_head(context)
|
||||
@ -16,7 +17,7 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
||||
|
||||
def view_layouts_base_body_bottom(context)
|
||||
if current_is_detail_page(context)
|
||||
javascript_include_tag('issue_dynamic_edit.js', :plugin => :redmine_issue_dynamic_edit)
|
||||
javascript_include_tag('issue_dynamic_edit_configuration_file.js', 'issue_dynamic_edit.js', :plugin => :redmine_issue_dynamic_edit)
|
||||
end
|
||||
end
|
||||
|
||||
@ -130,7 +131,9 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
||||
o << "</span>"
|
||||
o << "<script>"
|
||||
o << "//<![CDATA[\n"
|
||||
o << " $(function() { $('#StartDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
||||
o << " if(typeof datepickerOptions !== 'undefined'){\n"
|
||||
o << " $(function() { $('#StartDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
||||
o << " }\n"
|
||||
o << "//]]>\n"
|
||||
o << "</script>"
|
||||
end
|
||||
@ -144,7 +147,9 @@ class DetailsIssueHooks < Redmine::Hook::ViewListener
|
||||
o << "</span>"
|
||||
o << "<script>"
|
||||
o << "//<![CDATA[\n"
|
||||
o << " $(function() { $('#DueDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
||||
o << " if(typeof datepickerOptions !== 'undefined'){\n"
|
||||
o << " $(function() { $('#DueDateInput input').addClass('date').datepickerFallback(datepickerOptions); });\n"
|
||||
o << " }\n"
|
||||
o << "//]]>\n"
|
||||
o << "</script>"
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user